module-arch-POC/src/port.hpp

181 lines
8.7 KiB
C++

#pragma once
#include <boost/callable_traits.hpp>
#include <boost/callable_traits/args.hpp>
#include <boost/callable_traits/return_type.hpp>
#include <cstddef>
#include <stdexcept>
#include <tuple>
#include <type_traits>
#include <utility>
#include "port_base.hpp"
#include "ports.hpp"
#include "src/port_types.hpp"
#include "tuple.hpp"
using namespace boost::callable_traits;
template <typename...> class Port;
template <typename... Adapters, typename... Args>
requires(
(std::is_same_v<typename Adapters::base_t::callback_type_t::result_type, typename std::tuple_element_t<0, std::tuple<Adapters...>>::base_t::callback_type_t::result_type> &&
...) &&
((std::tuple_size_v<args_t<typename Adapters::base_t::callback_type_t::signature_type>> ==
std::tuple_size_v<args_t<typename std::tuple_element_t<0, std::tuple<Adapters...>>::callback_type_t::signature_type>>) &&
...))
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:
static constexpr auto callback_args_num = std::tuple_size_v<args_t<typename std::tuple_element_t<0, std::tuple<Adapters...>>::callback_type_t::signature_type>>;
using this_t = Port<std::tuple<Adapters...>, std::tuple<Args...>>;
using port_data_type_t = std::tuple_element_t<0, std::tuple<return_type_t<typename Adapters::encoder_type_t>...>>;
using callback_aargs_t = tp::tuple_tail<args_t<typename std::tuple_element_t<0, std::tuple<Adapters...>>::callback_type_t::signature_type>>::type;
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 = std::remove_cvref_t<return_type_t<typename adapter_type_t::decoder_type_t>>;
using adapter_callback_type_t = std::remove_cvref_t<typename adapter_type_t::base_t::callback_type_t>;
// fmt::print("Adding callback: name: {}, namehash: {}, typehash: {}, cbk_typehash: {}, cbk_type: {}\r\n", std::get<Idx>(adapters)->name(),
// std::hash<std::string>()(std::get<Idx>(adapters)->name()), typeid(adapter_input_type_t).hash_code(), typeid(adapter_callback_type_t).hash_code(),
// type_name<adapter_callback_type_t>());
return std::make_tuple(std::get<Idx>(adapters)->name(), std::hash<std::string>()(std::get<Idx>(adapters)->name()), typeid(adapter_input_type_t).hash_code(),
typeid(adapter_callback_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));
}
~Port() override { stop(); }
void stop() const override {
stop__();
m_impl__->close();
}
inline const auto &adapters() const { return mc_adapters_; }
protected:
void stop__() const override { m_impl__->stop_source().request_stop(); }
void send__(const std::string &addr, const void *data, size_t size, size_t hash) const override {
const void *adapter_ptr = nullptr;
tp::for_each(mc_adapters_, [&](const auto &e) {
const auto &[adapter_name, adapter_namehash, adapter_typehash, adapter_cbk_typehash, adapter] = e;
if (adapter_typehash == hash) {
using adapter_in_type_t = std::remove_cvref_t<return_type_t<decltype(adapter->decoder())>>;
adapter_ptr = &adapter;
typename PortImplBase<this_t, PortImplCallback_<callback_aargs_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);
m_impl__->send(addr, buf);
}
});
if (!adapter_ptr) {
throw std::runtime_error(fmt::format("No suitable adapter found for type #{}\r\n", hash));
}
}
void *get_adapter__(const std::string &name, size_t namehash, size_t typehash, size_t cbk_typehash) const override final {
void *ret = nullptr;
tp::for_each(mc_adapters_, [&](auto &a) {
if (!ret) {
auto &[adapter_name, adapter_namehash, adapter_typehash, adapter_cbk_typehash, adapter] = a;
if (adapter_typehash == typehash && adapter_namehash == namehash && adapter_cbk_typehash == cbk_typehash) {
ret = reinterpret_cast<void *>(adapter.get());
}
}
});
if (!ret) {
throw std::runtime_error(
fmt::format("No such callback in adapter '{}' in port '{}' (namehash: #{}, typehash: #{}, cbk_typehash: #{})\r\n", name, this->name(), namehash, typehash, cbk_typehash));
}
return ret;
};
private:
using base_t_ = PortBase<port_data_type_t>;
using cbk_return_type_t_ = typename std::tuple_element_t<0, std::tuple<Adapters...>>::base_t::callback_type_t::result_type;
template <typename...> class PortImplCallback_;
template <typename... Aargs> class PortImplCallback_<std::tuple<Aargs...>> {
public:
PortImplCallback_(const Port *port) : mc_port_(port) {}
using type_t = std::function<cbk_return_type_t_(const port_data_type_t &, size_t, Aargs &&...)>;
cbk_return_type_t_ operator()(const port_data_type_t &data, size_t hash, Aargs &&...callback_args) const {
std::conditional_t<!std::is_void_v<cbk_return_type_t_>, cbk_return_type_t_, std::false_type> ret;
tp::for_each(mc_port_->mc_adapters_, [&](const auto &e) {
const auto &[adapter_name, adapter_namehash, adapter_typehash, adapter_cbk_typehash, adapter] = e;
if (adapter_typehash == hash) {
if constexpr (std::is_void_v<std::invoke_result_t<std::remove_cvref_t<decltype(adapter->callback())>, decltype(adapter->decoder()(data)), Aargs &&...>>) {
adapter->callback()(adapter->decoder()(data), std::forward<Aargs>(callback_args)...);
} else {
ret = adapter->callback()(adapter->decoder()(data), std::forward<Aargs>(callback_args)...);
}
}
});
if constexpr (!std::is_void_v<cbk_return_type_t_>) {
return ret;
}
}
private:
const Port *mc_port_;
};
mutable std::unique_ptr<PortImplBase<this_t, PortImplCallback_<callback_aargs_t>>> m_impl__;
mutable std::tuple<std::tuple<std::string, size_t, size_t, size_t, std::unique_ptr<Adapters>>...> mc_adapters_;
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<this_t, PortImplCallback_<callback_aargs_t>> *>(nullptr));
} else {
return std::make_pair(port_type, static_cast<PortImpl<port_type, this_t, PortImplCallback_<callback_aargs_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 this_t *, const ImplArgs &..., PortImplCallback_<callback_aargs_t> &&>) {
m_impl__ = std::make_unique<impl_type_t>(this, std::forward<ImplArgs>(args)..., PortImplCallback_<callback_aargs_t>(this));
}
}
});
}
};
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...>>;