#pragma once // ObjectVariantNodeWrapper — variant-node adapter for *stateful* functors. // // VariantNodeWrapper (variant_node.hpp) wraps Node, where Func is a // default-constructible NTTP callable. That doesn't fit nodes whose functor must // be constructed with runtime state (a Config, a loaded gallery, etc.) — those use // ObjectNode, which takes `Obj& obj` at construction. // // This wrapper owns an Obj instance and exposes the same IVariantNode surface so a // stateful C++ node can live inside a PyNetwork. Build one via a factory that // constructs the functor from Python-supplied config, e.g.: // // auto n = std::make_shared, out<"matched">>>( // fifo_cap, gallery, cfg); // Obj ctor args forwarded // net.add("identity_matcher", n); // // The wrapper mirrors VariantNodeWrapper's channel plumbing exactly; only the // underlying node type (PoolObjectNode, holding Obj&) differs. #include "../channel.hpp" #include "../node.hpp" #include "../variant_node.hpp" #include #include #include #include #include #include #include namespace kpn { template, typename OutputTag = out<>> class ObjectVariantNodeWrapper; template class ObjectVariantNodeWrapper, out> : public IVariantNode { using NodeT = ObjectNode, out>; public: using args_tuple = typename NodeT::args_tuple; using return_tuple = typename NodeT::return_tuple; static constexpr std::size_t n_in = NodeT::input_count; static constexpr std::size_t n_out = NodeT::output_count; // Owns the functor; forwards remaining args to Obj's constructor. template explicit ObjectVariantNodeWrapper(std::size_t fifo_capacity, ObjArgs&&... obj_args) : obj_(std::forward(obj_args)...) , node_(obj_, fifo_capacity) , in_channels_(n_in) , out_channels_(n_out) , out_type_indices_(n_out, std::type_index(typeid(void))) { init_inputs(std::make_index_sequence{}, fifo_capacity); init_out_types(std::make_index_sequence{}); } // Access the owned functor so callers can invoke its runtime setters (e.g. to // change a threshold on a persistent pipeline without rebuilding the node). Obj& functor() { return obj_; } // ── INode ───────────────────────────────────────────────────────────────── void start() override { node_.start(); } void stop() override { node_.stop(); } bool running() const override { return node_.running(); } const NodeStats& stats() const override { return node_.stats(); } void set_name(std::string name) override { node_.set_name(std::move(name)); } NodeSnapshot node_snapshot(const std::string& name, double elapsed_s) const override { return node_.node_snapshot(name, elapsed_s); } // ── IVariantNode ────────────────────────────────────────────────────────── std::size_t input_count() const override { return n_in; } std::size_t output_count() const override { return n_out; } std::type_index input_type(std::size_t i) const override { return in_channels_[i]->type_index(); } std::type_index output_type(std::size_t i) const override { return out_type_indices_[i]; } std::shared_ptr> input_channel(std::size_t i) override { return in_channels_[i]; } void set_output_channel(std::size_t i, std::shared_ptr> ch) override { set_output_impl(i, std::move(ch), std::make_index_sequence{}); } private: template void init_inputs(std::index_sequence, std::size_t cap) { ((init_one_input(cap)), ...); } template void init_one_input(std::size_t cap) { using T = std::tuple_element_t; auto shared_ch = std::make_shared>(cap); node_.template set_input_channel(shared_ch); in_channels_[I] = std::make_shared>(std::move(shared_ch)); } template void init_out_types(std::index_sequence) { ((out_type_indices_[Is] = std::type_index(typeid(std::tuple_element_t))), ...); } template void set_output_impl(std::size_t port, std::shared_ptr> ch, std::index_sequence) { bool matched = false; ((Is == port && (set_output_at(std::move(ch)), matched = true)), ...); if (!matched) throw std::out_of_range("set_output_channel: port index out of range"); } template void set_output_at(std::shared_ptr> ch) { using T = std::tuple_element_t; auto* typed = dynamic_cast*>(ch.get()); if (!typed) throw std::runtime_error( "set_output_channel: type mismatch at output port " + std::to_string(I)); node_.template set_output_channel(typed->raw_ptr()); out_channels_[I] = std::move(ch); } Obj obj_; // owned; node_ holds Obj& — declaration order keeps obj_ alive first NodeT node_; std::vector>> in_channels_; std::vector>> out_channels_; std::vector out_type_indices_; }; } // namespace kpn