This commit is contained in:
Oleg Shishlyannikov 2025-10-26 12:03:37 +03:00
commit 83b8a98f1c
26 changed files with 885 additions and 0 deletions

3
.clang-format Normal file
View File

@ -0,0 +1,3 @@
Language: Cpp
ColumnLimit: 180
IndentPPDirectives: AfterHash

6
.clang_complete Normal file
View File

@ -0,0 +1,6 @@
-I/usr/include/c++/14
-I/usr/include/x86_64-linux-gnu/c++/14
-I../build/_deps/tpl-src
-I./thirdparty
-std=gnu++23

2
.clangd Normal file
View File

@ -0,0 +1,2 @@
CompileFlags:
Add: ['-std=gnu++23']

63
CMakeLists.txt Normal file
View File

@ -0,0 +1,63 @@
cmake_minimum_required(VERSION 3.14)
include(FetchContent)
set(FETCHCONTENT_QUIET FALSE)
set(FTXUI_BUILD_EXAMPLES ON)
FetchContent_Declare(
fmt
GIT_REPOSITORY https://github.com/fmtlib/fmt
GIT_PROGRESS TRUE
)
FetchContent_MakeAvailable(fmt)
FetchContent_Declare(
inja
GIT_REPOSITORY https://github.com/pantor/inja
GIT_PROGRESS TRUE
GIT_TAG main
)
FetchContent_MakeAvailable(inja)
FetchContent_Declare(
libzmq
GIT_REPOSITORY https://github.com/zeromq/libzmq
GIT_PROGRESS TRUE
)
FetchContent_MakeAvailable(libzmq)
FetchContent_Declare(
cppzmq
GIT_REPOSITORY https://github.com/zeromq/cppzmq
GIT_PROGRESS TRUE
)
FetchContent_MakeAvailable(cppzmq)
project(module_arch_POC)
set(CMAKE_CXX_STANDARD 23)
set(CMAKE_BUILD_TYPE Debug)
set(CMAKE_MESSAGE_LOG_LEVEL TRACE)
set(CMAKE_VERBOSE_MAKEFILE OFF)
set(CMAKE_CXX_COMPILER /usr/bin/clang++)
file(GLOB_RECURSE SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/src/*.hpp ${CMAKE_CURRENT_SOURCE_DIR}/src/*.cpp)
add_executable(${CMAKE_PROJECT_NAME} ${SOURCES})
target_include_directories(${CMAKE_PROJECT_NAME} PRIVATE
${CMAKE_CURRENT_SOURCE_DIR}
${fmt_SOURCE_DIR}/include
${inja_SOURCE_DIR}/include
${libzmq_SOURCE_DIR}/include
${cppzmq_SOURCE_DIR}/include
)
target_link_directories(${CMAKE_PROJECT_NAME}
PRIVATE
${fmt_BINARY_DIR}
${inja_BINARY_DIR}
${libzmq_BINARY_DIR}
${cppzmq_BINARY_DIR}
)

0
README.md Normal file
View File

46
src/adapter.hpp Normal file
View File

@ -0,0 +1,46 @@
#pragma once
#include <boost/callable_traits.hpp>
#include <boost/callable_traits/args.hpp>
#include <boost/callable_traits/return_type.hpp>
#include <boost/signals2.hpp>
#include <boost/signals2/variadic_signal.hpp>
#include <string>
#include <tuple>
#define FMT_HEADER_ONLY
#include <fmt/format.h>
#include <fmt/ranges.h>
#include "adapter_base.hpp"
using namespace boost::callable_traits;
template <typename Encoder, typename Decoder>
requires(
// Encoder return type is same as Decoder argument type, encoder and decoder have only 1 arg
std::tuple_size_v<args_t<Decoder>> == 1 && std::is_same_v<return_type_t<Encoder>, std::remove_cvref_t<std::tuple_element_t<0, args_t<Decoder>>>> &&
std::tuple_size_v<args_t<Encoder>> == 1 && std::is_same_v<return_type_t<Decoder>, std::remove_cvref_t<std::tuple_element_t<0, args_t<Encoder>>>>)
class Adapter : public AdapterBase<std::tuple_element_t<0, boost::callable_traits::args_t<Encoder>>> {
public:
using encoder_type_t = Encoder;
using decoder_type_t = Decoder;
using adapter_type_t = Adapter;
using callback_arg_type_t = std::tuple_element_t<0, boost::callable_traits::args_t<Encoder>>;
using base_t = AdapterBase<callback_arg_type_t>;
using callback_type_t = base_t::callback_type_t;
Adapter(const std::string &name, std::pair<Encoder, Decoder> &&fns)
: AdapterBase<callback_arg_type_t>(name), mc_enc_(std::forward<Encoder>(fns.first)), mc_dec_(std::forward<Decoder>(fns.second)) {}
inline const auto &encoder() const { return mc_enc_; }
inline const auto &decoder() const { return mc_dec_; }
typename base_t::callback_type_t &callback() const override final { return m_callback_; };
private:
const Encoder mc_enc_;
const Decoder mc_dec_;
mutable callback_type_t m_callback_;
};

18
src/adapter_base.hpp Normal file
View File

@ -0,0 +1,18 @@
#pragma once
#include <boost/signals2.hpp>
template <typename InType> class AdapterBase {
public:
using callback_type_t = boost::signals2::signal<void(const InType &)>;
AdapterBase(const std::string &name) : mc_name_(name) {}
virtual callback_type_t &callback() const = 0;
inline const auto &name() const { return mc_name_; }
protected:
private:
const std::string mc_name_;
};

7
src/adapter_factory.hpp Normal file
View File

@ -0,0 +1,7 @@
#pragma once
#include "adapter.hpp"
template <typename Encoder, typename Decoder> auto makeAdapter(const std::string &name, std::pair<Encoder, Decoder> &&fns) {
return std::make_unique<Adapter<Encoder, Decoder>>(name, std::forward<std::pair<Encoder, Decoder>>(fns));
}

87
src/module.hpp Normal file
View File

@ -0,0 +1,87 @@
#pragma once
#include <cstdint>
#include <tuple>
#include <unordered_map>
#include <utility>
#include <zmq.hpp>
#include "port_base.hpp"
#include "tuple.hpp"
enum class module_type_e : uint32_t {
STANDALONE,
INCOMPOSITION,
};
template <typename EnvDataType> class ModuleBase {
public:
ModuleBase(int32_t argc, char **argv, char **envp, const std::string &name)
: mc_name_(name), mc_cli_args_({
.argc = argc,
.argv = argv,
.envp = envp,
}) {}
inline const auto &name() const { return mc_name_; }
virtual const PortBase<EnvDataType> &port(const std::string &name) const = 0;
virtual void run(void (*entry)(int32_t, char **, char **, const std::unordered_map<std::string, const PortBase<EnvDataType> *> &)) const = 0;
protected:
const auto &cli__() const { return mc_cli_args_; }
private:
const std::string mc_name_;
const struct {
int32_t argc;
char **argv, **envp;
} mc_cli_args_;
};
template <typename... Ports> class Module : public ModuleBase<std::tuple_element_t<0, std::tuple<typename Ports::port_data_type_t...>>> {
public:
using port_data_type_t = std::tuple_element_t<0, std::tuple<typename Ports::port_data_type_t...>>;
Module(int32_t argc, char **argv, char **envp, const std::string &name, zmq::context_t &zmq_ctx, std::tuple<std::unique_ptr<Ports>...> &&ports)
: ModuleBase<port_data_type_t>(argc, argv, envp, name), mc_ports_([&]<size_t... Ids>(std::index_sequence<Ids...>) {
return std::make_tuple([&]<size_t Idx>() {
using port_type_t = std::remove_cvref_t<decltype(std::get<Idx>(ports))>;
auto &port = std::get<Idx>(ports);
return std::make_tuple(port->name(), std::hash<std::string>()(port->name()), std::forward<port_type_t>(port));
}.template operator()<Ids>()...);
}(std::make_index_sequence<sizeof...(Ports)>{})) {}
const PortBase<port_data_type_t> &port(const std::string &name) const override {
const PortBase<port_data_type_t> *ret = nullptr;
size_t hash = std::hash<std::string>()(name);
tp::for_each(mc_ports_, [&](const auto &p) {
if (!ret) {
const auto &[port_name, name_hash, port] = p;
if (name == port_name) {
ret = static_cast<const PortBase<port_data_type_t> *>(port.get());
}
}
});
if (!ret) {
throw std::runtime_error(fmt::format("Port with name '{}' not found in '{}' module\r\n", name, this->name()));
}
return *ret;
}
void run(void (*entry)(int32_t, char **, char **, const std::unordered_map<std::string, const PortBase<port_data_type_t> *> &)) const override {
entry(this->cli__().argc, this->cli__().argv, this->cli__().envp,
[&]<size_t... Ids>(std::index_sequence<Ids...>) -> std::unordered_map<std::string, const PortBase<port_data_type_t> *> {
return {std::make_pair(std::get<0u>(std::get<Ids>(ports_())), std::get<2u>(std::get<Ids>(ports_())).get())...};
}(std::make_index_sequence<std::tuple_size_v<std::remove_cvref_t<decltype(ports_())>>>{}));
}
private:
inline const auto &ports_() const { return mc_ports_; }
const std::tuple<std::tuple<std::string, size_t, std::unique_ptr<Ports>>...> mc_ports_;
};

9
src/module_factory.hpp Normal file
View File

@ -0,0 +1,9 @@
#pragma once
#include "module.hpp"
#include <memory>
template <enum module_type_e mt, typename... Ports>
auto makeModule(int32_t argc, char **argv, char **envp, const std::string &name, zmq::context_t &zmq_ctx, std::tuple<std::unique_ptr<Ports>...> &&ports) {
return std::make_unique<Module<Ports...>>(argc, argv, envp, name, zmq_ctx, std::forward<std::tuple<std::unique_ptr<Ports>...>>(ports));
}

134
src/port.hpp Normal file
View File

@ -0,0 +1,134 @@
#pragma once
#include <boost/callable_traits.hpp>
#include <boost/callable_traits/args.hpp>
#include <boost/callable_traits/return_type.hpp>
#include <cstddef>
#include "port_base.hpp"
#include "ports.hpp"
#include "tuple.hpp"
using namespace boost::callable_traits;
template <typename...> class Port;
template <typename... Adapters, typename... Args>
class Port<std::tuple<Adapters...>, std::tuple<Args...>> : public PortBase<std::tuple_element_t<0, std::tuple<return_type_t<typename Adapters::encoder_type_t>...>>> {
public:
using port_data_type_t = std::tuple_element_t<0, std::tuple<return_type_t<typename Adapters::encoder_type_t>...>>;
Port(enum port_types_e pt, const std::string &name, const std::string &endpoint, zmq::context_t &zmq_ctx, std::tuple<std::unique_ptr<Adapters>...> &&adapters,
std::tuple<Args...> &&args)
: PortBase<port_data_type_t>(pt, name, endpoint, zmq_ctx),
// Init adapters
mc_adapters_([&]<size_t... Ids>(std::index_sequence<Ids...>) {
return std::make_tuple([&]<size_t Idx>() {
using adapter_type_t = std::remove_cvref_t<decltype(*std::get<Idx>(adapters))>;
using adapter_input_type_t = return_type_t<typename adapter_type_t::decoder_type_t>;
return std::make_tuple(std::get<Idx>(adapters)->name(), std::hash<std::string>()(std::get<Idx>(adapters)->name()),
typeid(std::remove_cvref_t<adapter_input_type_t>).hash_code(), std::forward<std::unique_ptr<adapter_type_t>>(std::get<Idx>(adapters)));
}.template operator()<Ids>()...);
}(std::make_index_sequence<sizeof...(Adapters)>{})) {
std::apply([&, this](auto &&...args) { init_impl_(pt, zmq_ctx, std::move(endpoint), std::forward<Args>(args)...); }, std::forward<decltype(args)>(args));
}
void stop() const override {
this->stop__();
mc_impl_->close();
}
protected:
void send__(const void *data, size_t size, size_t hash) const override {
tp::for_each(mc_adapters_, [&](const auto &e) {
const auto &[adapter_name, adapter_namehash, adapter_typehash, adapter] = e;
if (adapter_typehash == hash) {
using adapter_in_type_t = std::remove_cvref_t<return_type_t<decltype(adapter->decoder())>>;
typename PortImplBase<cbk_type_t_>::port_payload_s payload = {
.typehash = hash,
.data = adapter->encoder()(*reinterpret_cast<const adapter_in_type_t *>(data)),
};
msgpack::sbuffer buf;
msgpack::pack(buf, payload);
mc_impl_->send(buf);
}
});
}
void *get_adapter__(const std::string &name, size_t namehash, size_t typehash) const override final {
void *ret = nullptr;
tp::for_each(mc_adapters_, [&](auto &a) {
if (!ret) {
auto &[adapter_name, adapter_namehash, adapter_typehash, adapter] = a;
if (adapter_typehash == typehash && adapter_namehash == namehash) {
ret = reinterpret_cast<void *>(adapter.get());
}
}
});
if (!ret) {
throw std::runtime_error(fmt::format("No adapter '{}' in port '{}'\r\n", name, this->name()));
}
return ret;
};
private:
using base_t_ = PortBase<port_data_type_t>;
using cbk_type_t_ = std::function<void(const port_data_type_t &, size_t)>;
mutable std::tuple<std::tuple<std::string, size_t, size_t, std::unique_ptr<Adapters>>...> mc_adapters_;
mutable std::unique_ptr<PortImplBase<cbk_type_t_>> mc_impl_;
void listen__(std::stop_token st) const override { mc_impl_->listen(st); }
template <typename... ImplArgs> void init_impl_(enum port_types_e pt, ImplArgs &&...args) const {
using enum port_types_e;
static constexpr auto make_null_impl_pair = []<enum port_types_e port_type>() consteval {
if constexpr (port_type == UNKNOWN) {
return std::make_pair(port_type, static_cast<PortImplBase<cbk_type_t_> *>(nullptr));
} else {
return std::make_pair(port_type, static_cast<PortImpl<port_type, cbk_type_t_> *>(nullptr));
}
};
static constexpr auto impl_map =
std::make_tuple(make_null_impl_pair.template operator()<UNKNOWN>(), make_null_impl_pair.template operator()<PUB>(), make_null_impl_pair.template operator()<SUB>(),
make_null_impl_pair.template operator()<REQ>(), make_null_impl_pair.template operator()<REP>(), make_null_impl_pair.template operator()<ROUTER>(),
make_null_impl_pair.template operator()<DEALER>(), make_null_impl_pair.template operator()<PUSH>(), make_null_impl_pair.template operator()<PULL>(),
make_null_impl_pair.template operator()<PAIR>());
tp::for_each(impl_map, [&](const auto &p) {
const auto &[type, null_pimpl] = p;
if (type == pt) {
using impl_type_t = std::remove_pointer_t<decltype(null_pimpl)>;
if constexpr (std::is_constructible_v<impl_type_t, const ImplArgs &..., cbk_type_t_ &&>) {
mc_impl_ = std::make_unique<impl_type_t>(
// Args
std::forward<ImplArgs>(args)...,
// Callback
[this](const port_data_type_t &data, size_t hash) {
tp::for_each(mc_adapters_, [&](const auto &e) {
const auto &[adapter_name, adapter_namehash, adapter_typehash, adapter] = e;
if (adapter_typehash == hash) {
adapter->callback()(adapter->decoder()(data));
}
});
});
}
}
});
}
};
template <typename... Adapters, typename... Args>
Port(enum port_types_e, const std::string &, const std::string &, zmq::context_t &, std::tuple<std::unique_ptr<Adapters>...> &&, std::tuple<Args...> &&)
-> Port<std::tuple<Adapters...>, std::tuple<Args...>>;

68
src/port_base.hpp Normal file
View File

@ -0,0 +1,68 @@
#pragma once
#include <cstdint>
#include <future>
#include <msgpack.hpp>
#include <zmq.hpp>
#define FMT_HEADER_ONLY
#include <fmt/format.h>
#include <fmt/ranges.h>
#include "port_types.hpp"
#include "adapter_base.hpp"
template <class T> constexpr std::string_view type_name() {
using namespace std;
#ifdef __clang__
string_view p = __PRETTY_FUNCTION__;
return string_view(p.data() + 34, p.size() - 34 - 1);
#elif defined(__GNUC__)
string_view p = __PRETTY_FUNCTION__;
# if __cplusplus < 201402
return string_view(p.data() + 36, p.size() - 36 - 1);
# else
return string_view(p.data() + 49, p.find(';', 49) - 49);
# endif
#elif defined(_MSC_VER)
string_view p = __FUNCSIG__;
return string_view(p.data() + 84, p.size() - 84 - 7);
#endif
}
template <typename EncodedType> class PortBase {
public:
PortBase(enum port_types_e port_type, const std::string &name, const std::string &endpoint, zmq::context_t &zmq_ctx)
: mc_type_(port_type), mc_name_(name), mc_endpoint_(endpoint), mc_name_hash_(std::hash<std::string>()(name)), mc_endpoint_hash_(std::hash<std::string>()(endpoint)) {}
inline const auto &type() const { return mc_type_; }
inline const auto &name() const { return mc_name_; }
inline const auto &endpoint() const { return mc_endpoint_; }
inline const auto &name_hash() const { return mc_name_hash_; }
inline const auto &endpoint_hash() const { return mc_endpoint_hash_; }
template <typename InType> const PortBase<EncodedType> &operator<<(const InType &in) const {
send__(&in, sizeof(InType), typeid(InType).hash_code());
return *this;
}
void listen() const { listen__(m_ss_.get_token()); };
virtual void stop() const = 0;
template <typename AdapterInType> auto &callback(const std::string &name) const {
return (*static_cast<AdapterBase<AdapterInType> *>(get_adapter__(name, std::hash<std::string>()(name), typeid(std::remove_cvref_t<AdapterInType>).hash_code()))).callback();
}
protected:
void stop__() const { m_ss_.request_stop(); }
virtual void send__(const void *data, size_t size, size_t type_hash) const = 0;
virtual void listen__(std::stop_token) const = 0;
virtual void *get_adapter__(const std::string &name, size_t namehash, size_t typehash) const = 0;
private:
const enum port_types_e mc_type_;
const std::string mc_name_, mc_endpoint_;
const size_t mc_name_hash_, mc_endpoint_hash_;
mutable std::stop_source m_ss_;
};

22
src/port_dealer_impl.hpp Normal file
View File

@ -0,0 +1,22 @@
#pragma once
#include "port_impl_base.hpp"
#include "port_types.hpp"
#define FMT_HEADER_ONLY
#include <fmt/format.h>
#include <fmt/ranges.h>
template <typename Callback> class PortImpl<port_types_e::DEALER, Callback> : public PortImplBase<Callback> {
public:
PortImpl(zmq::context_t &zmq_ctx, const std::string &endpoint, Callback &&callback) : PortImplBase<Callback>(zmq_ctx, endpoint, std::forward<Callback>(callback)) {}
void listen(std::stop_token st) const override {
while (!st.stop_requested()) {
std::this_thread::sleep_for(std::chrono::milliseconds(100u));
}
}
// Send to socket depending on implementation
void send(const msgpack::sbuffer &data) const override {};
};

10
src/port_factory.hpp Normal file
View File

@ -0,0 +1,10 @@
#pragma once
#include "port.hpp"
template <typename... Adapters, typename... Args>
auto makePort(enum port_types_e pt, const std::string &name, const std::string &endpoint, zmq::context_t &zmq_ctx, std::tuple<std::unique_ptr<Adapters>...> &&adapters,
std::tuple<Args...> &&args) {
return std::make_unique<Port<std::tuple<Adapters...>, std::tuple<Args...>>>(pt, name, endpoint, zmq_ctx, std::forward<std::tuple<std::unique_ptr<Adapters>...>>(adapters),
std::forward<std::tuple<Args...>>(args));
}

40
src/port_impl_base.hpp Normal file
View File

@ -0,0 +1,40 @@
#pragma once
#include <boost/callable_traits/args.hpp>
#include <future>
#include <msgpack.hpp>
#include <tuple>
#include <zmq.hpp>
template <typename Callback> class PortImplBase {
using port_data_type_t_ = std::remove_cvref_t<std::tuple_element_t<0, boost::callable_traits::args_t<Callback>>>;
public:
struct port_payload_s {
size_t typehash;
port_data_type_t_ data;
MSGPACK_DEFINE(typehash, data);
};
PortImplBase(zmq::context_t &zmq_ctx, const std::string &endpoint, Callback &&callback) : mc_endpoint__(endpoint), m_ctx__(zmq_ctx), mc_cbk__(callback) {}
virtual ~PortImplBase() = default;
virtual void listen(std::stop_token st) const = 0;
virtual void send(const msgpack::sbuffer &data) const = 0;
void close() {
m_sock__.close();
if (m_listener_thread__.valid()) {
m_listener_thread__.get();
}
};
protected:
mutable std::future<void> m_listener_thread__;
mutable zmq::socket_t m_sock__;
zmq::context_t &m_ctx__;
const std::string mc_endpoint__;
const Callback mc_cbk__;
};

21
src/port_pair_impl.hpp Normal file
View File

@ -0,0 +1,21 @@
#pragma once
#include "port_impl_base.hpp"
#include "port_types.hpp"
#define FMT_HEADER_ONLY
#include <fmt/format.h>
#include <fmt/ranges.h>
template <typename Callback> class PortImpl<port_types_e::PAIR, Callback> : public PortImplBase<Callback> {
public:
PortImpl(zmq::context_t &zmq_ctx, const std::string &endpoint, Callback &&callback) : PortImplBase<Callback>(zmq_ctx, endpoint, std::forward<Callback>(callback)) {}
void listen(std::stop_token st) const override {
while (!st.stop_requested()) {
std::this_thread::sleep_for(std::chrono::milliseconds(100u));
}
}
// Send to socket depending on implementation
void send(const msgpack::sbuffer &data) const override {};
};

View File

@ -0,0 +1,36 @@
#pragma once
#include "port_impl_base.hpp"
#include "port_types.hpp"
#define FMT_HEADER_ONLY
#include <fmt/format.h>
#include <fmt/ranges.h>
template <typename Callback> class PortImpl<port_types_e::PUB, Callback> final : public PortImplBase<Callback> {
public:
PortImpl(zmq::context_t &zmq_ctx, const std::string &endpoint, const std::list<std::string> &topics, Callback &&callback)
: PortImplBase<Callback>(zmq_ctx, endpoint, std::forward<Callback>(callback)), mc_topics_(topics) {
this->m_sock__ = zmq::socket_t(this->m_ctx__, zmq::socket_type::pub);
this->m_sock__.bind(this->mc_endpoint__);
}
~PortImpl() override {}
void listen(std::stop_token st) const override { throw std::runtime_error("Can't listen on PUBLISHER pattern socket"); }
// Send to socket depending on implementation
void send(const msgpack::sbuffer &data) const override {
try {
for (const auto &topic : mc_topics_) {
this->m_sock__.send(zmq::message_t(topic), zmq::send_flags::sndmore);
this->m_sock__.send(zmq::message_t(data.data(), data.size()), zmq::send_flags::dontwait);
}
} catch (const zmq::error_t &err) {
fmt::print("ZMQ error: {1} ({0})\r\n", err.num(), err.what());
}
};
private:
const std::list<std::string> mc_topics_;
};

21
src/port_pull_impl.hpp Normal file
View File

@ -0,0 +1,21 @@
#pragma once
#include "port_impl_base.hpp"
#include "port_types.hpp"
#define FMT_HEADER_ONLY
#include <fmt/format.h>
#include <fmt/ranges.h>
template <typename Callback> class PortImpl<port_types_e::PULL, Callback> : public PortImplBase<Callback> {
public:
PortImpl(zmq::context_t &zmq_ctx, const std::string &endpoint, Callback &&callback) : PortImplBase<Callback>(zmq_ctx, endpoint, std::forward<Callback>(callback)) {}
void listen(std::stop_token st) const override {
while (!st.stop_requested()) {
std::this_thread::sleep_for(std::chrono::milliseconds(100u));
}
}
// Send to socket depending on implementation
void send(const msgpack::sbuffer &data) const override {};
};

21
src/port_push_impl.hpp Normal file
View File

@ -0,0 +1,21 @@
#pragma once
#include "port_impl_base.hpp"
#include "port_types.hpp"
#define FMT_HEADER_ONLY
#include <fmt/format.h>
#include <fmt/ranges.h>
template <typename Callback> class PortImpl<port_types_e::PUSH, Callback> : public PortImplBase<Callback> {
public:
PortImpl(zmq::context_t &zmq_ctx, const std::string &endpoint, Callback &&callback) : PortImplBase<Callback>(zmq_ctx, endpoint, std::forward<Callback>(callback)) {}
void listen(std::stop_token st) const override {
while (!st.stop_requested()) {
std::this_thread::sleep_for(std::chrono::milliseconds(100u));
}
}
// Send to socket depending on implementation
void send(const msgpack::sbuffer &data) const override {};
};

22
src/port_rep_impl.hpp Normal file
View File

@ -0,0 +1,22 @@
#pragma once
#include "port_impl_base.hpp"
#include "port_types.hpp"
#define FMT_HEADER_ONLY
#include <fmt/format.h>
#include <fmt/ranges.h>
template <typename Callback> class PortImpl<port_types_e::REP, Callback> : public PortImplBase<Callback> {
public:
PortImpl(zmq::context_t &zmq_ctx, const std::string &endpoint, Callback &&callback) : PortImplBase<Callback>(zmq_ctx, endpoint, std::forward<Callback>(callback)) {}
void listen(std::stop_token st) const override {
while (!st.stop_requested()) {
std::this_thread::sleep_for(std::chrono::milliseconds(100u));
}
}
// Send to socket depending on implementation
void send(const msgpack::sbuffer &data) const override {};
};

22
src/port_req_impl.hpp Normal file
View File

@ -0,0 +1,22 @@
#pragma once
#include "port_impl_base.hpp"
#include "port_types.hpp"
#define FMT_HEADER_ONLY
#include <fmt/format.h>
#include <fmt/ranges.h>
template <typename Callback> class PortImpl<port_types_e::REQ, Callback> : public PortImplBase<Callback> {
public:
PortImpl(zmq::context_t &zmq_ctx, const std::string &endpoint, Callback &&callback) : PortImplBase<Callback>(zmq_ctx, endpoint, std::forward<Callback>(callback)) {}
void listen(std::stop_token st) const override {
while (!st.stop_requested()) {
std::this_thread::sleep_for(std::chrono::milliseconds(100u));
}
}
// Send to socket depending on implementation
void send(const msgpack::sbuffer &data) const override {};
};

22
src/port_router_impl.hpp Normal file
View File

@ -0,0 +1,22 @@
#pragma once
#include "port_impl_base.hpp"
#include "port_types.hpp"
#define FMT_HEADER_ONLY
#include <fmt/format.h>
#include <fmt/ranges.h>
template <typename Callback> class PortImpl<port_types_e::ROUTER, Callback> : public PortImplBase<Callback> {
public:
PortImpl(zmq::context_t &zmq_ctx, const std::string &endpoint, Callback &&callback) : PortImplBase<Callback>(zmq_ctx, endpoint, std::forward<Callback>(callback)) {}
void listen(std::stop_token st) const override {
while (!st.stop_requested()) {
std::this_thread::sleep_for(std::chrono::milliseconds(100u));
}
}
// Send to socket depending on implementation
void send(const msgpack::sbuffer &data) const override {};
};

View File

@ -0,0 +1,55 @@
#pragma once
#include "port_impl_base.hpp"
#include "port_types.hpp"
#define FMT_HEADER_ONLY
#include <fmt/format.h>
#include <fmt/ranges.h>
template <typename Callback> class PortImpl<port_types_e::SUB, Callback> : public PortImplBase<Callback> {
public:
using base_t = PortImplBase<Callback>;
PortImpl(zmq::context_t &zmq_ctx, const std::string &endpoint, const std::list<std::string> &topics, Callback &&callback)
: PortImplBase<Callback>(zmq_ctx, endpoint, std::forward<Callback>(callback)), mc_topics_(topics) {
this->m_sock__ = zmq::socket_t(this->m_ctx__, zmq::socket_type::sub);
this->m_sock__.connect(this->mc_endpoint__);
for (const auto &topic : mc_topics_) {
this->m_sock__.set(zmq::sockopt::subscribe, topic);
}
}
void listen(std::stop_token st) const override {
this->m_listener_thread__ = std::async(
std::launch::async,
[this](std::stop_token st) {
while (!st.stop_requested()) {
zmq::message_t msg;
this->m_sock__.recv(msg, zmq::recv_flags::dontwait).and_then([&](const auto &res) {
fmt::print("Received envelope: {}\r\n", std::string(static_cast<const char *>(msg.data()), msg.size()));
this->m_sock__.recv(msg).and_then([&](const auto &res) {
typename base_t::port_payload_s payload;
msgpack::sbuffer buf;
buf.write(reinterpret_cast<const char *>(msg.data()), msg.size());
const auto [typehash, data] = msgpack::unpack(buf.data(), buf.size()).get().convert(payload);
this->mc_cbk__(data, typehash);
return std::optional(res);
});
return std::optional(res);
});
}
},
st);
}
// Send to socket depending on implementation
void send(const msgpack::sbuffer &data) const override { throw std::runtime_error("Can't send anything on SUBSCRIBER pattern socket"); };
private:
const std::list<std::string> mc_topics_;
};

18
src/port_types.hpp Normal file
View File

@ -0,0 +1,18 @@
#pragma once
#include <cstdint>
enum class port_types_e : uint32_t {
UNKNOWN = 0,
PUB,
SUB,
REQ,
REP,
ROUTER,
DEALER,
PUSH,
PULL,
PAIR,
};
template <enum port_types_e, typename...> class PortImpl;

13
src/ports.hpp Normal file
View File

@ -0,0 +1,13 @@
#pragma once
#include "port_base.hpp"
#include "port_publisher_impl.hpp"
#include "port_subscriber_impl.hpp"
#include "port_push_impl.hpp"
#include "port_pull_impl.hpp"
#include "port_req_impl.hpp"
#include "port_rep_impl.hpp"
#include "port_router_impl.hpp"
#include "port_dealer_impl.hpp"
#include "port_pair_impl.hpp"

119
src/templates.cpp Normal file
View File

@ -0,0 +1,119 @@
#include <boost/signals2/signal_type.hpp>
#include <chrono>
#include <cstdint>
#include <string>
#include <thread>
#include <tuple>
#include <zmq.hpp>
#define FMT_HEADER_ONLY
#include <fmt/format.h>
#include <fmt/ranges.h>
#include "adapter_factory.hpp"
#include "module_factory.hpp"
#include "port_factory.hpp"
void entry(int32_t argc, char **argv, char **envp, const std::unordered_map<std::string, const PortBase<std::vector<uint8_t>> *> &ports) {
const auto &subscriber = *ports.at("subscriber");
const auto &publisher = *ports.at("publisher");
auto &a = subscriber.callback<int32_t>("int-string-int");
auto &b = subscriber.callback<std::string>("string-string-string");
a.connect([](const int32_t &i) -> void { fmt::print("Echo: {}, typename: {}\r\n", i, type_name<decltype(i)>()); });
b.connect([](const std::string &i) -> void { fmt::print("Echo: {}, typename: {}\r\n", i, type_name<decltype(i)>()); });
subscriber.listen();
publisher << 1 << 2 << double{3.f} << std::string("test");
std::this_thread::sleep_for(std::chrono::milliseconds(1000u));
subscriber.stop();
}
int main(int argc, char *argv[], char *envp[]) {
using enum port_types_e;
zmq::context_t zmq_ctx;
auto mod = makeModule(argc, argv, envp, "test_mod", zmq_ctx,
std::tuple{
makePort(PUB, "publisher", "inproc://publisher_port", zmq_ctx,
std::tuple{
makeAdapter("int-string-int", std::pair{
[](const int32_t &i) -> std::vector<uint8_t> {
auto str = std::to_string(i + 5);
return {str.begin(), str.end()};
},
[](const std::vector<uint8_t> &s) -> int32_t { return 5; },
}),
makeAdapter("string-string-string", std::pair{
[](const std::string &i) -> std::vector<uint8_t> {
auto str = i + "_test";
return {str.begin(), str.end()};
},
[](const std::vector<uint8_t> &i) -> std::string { return "works!"; },
}),
makeAdapter("double-string-double", std::pair{
[](const double &i) -> std::vector<uint8_t> {
auto str = std::to_string(i / 2.f);
return {str.begin(), str.end()};
},
[](const std::vector<uint8_t> &s) -> double { return .1f; },
}),
},
std::tuple{
std::list<std::string>{
"topic0",
"topic1",
"topic2",
},
}),
makePort(SUB, "subscriber", "inproc://publisher_port", zmq_ctx,
std::tuple{
makeAdapter("int-string-int", std::pair{
[](const int32_t &i) -> std::vector<uint8_t> {
auto str = std::to_string(i + 5);
return {str.begin(), str.end()};
},
[](const std::vector<uint8_t> &s) -> int32_t { return 5; },
}),
makeAdapter("string-string-string", std::pair{
[](const std::string &i) -> std::vector<uint8_t> {
auto str = i + "_test";
return {str.begin(), str.end()};
},
[](const std::vector<uint8_t> &i) -> std::string { return "works!"; },
}),
makeAdapter("double-string-double", std::pair{
[](const double &i) -> std::vector<uint8_t> {
auto str = std::to_string(i / 2.f);
return {str.begin(), str.end()};
},
[](const std::vector<uint8_t> &s) -> double { return .1f; },
}),
},
std::tuple{
std::list<std::string>{
"topic0",
"topic1",
"topic2",
},
}),
});
mod.run(entry);
return 0;
}