1111#include " Port.hpp"
1212#include " UUID.hpp"
1313
14- #include < nlohmann/json_fwd .hpp>
14+ #include < nlohmann/json .hpp>
1515
1616#include < mutex>
1717#include < string>
@@ -165,15 +165,29 @@ class Node
165165 void EmitUpdate (const IndexableName& key, const SharedNodeData& data);
166166
167167 public:
168+ // / Event broadcasted called on Compute.
168169 EventDispatcher<> OnCompute;
170+
171+ // / Event broadcasted on setting an input.
169172 EventDispatcher<const IndexableName&, const SharedNodeData&> OnSetInput;
173+
174+ // / Event broadcasted on setting an output.
170175 EventDispatcher<const IndexableName&, const SharedNodeData&> OnSetOutput;
176+
177+ // / Event broadcasted on Compute throwing an error.
171178 EventDispatcher<const std::exception&> OnError;
179+
180+ // / Event broadcasted on output updates being emitted.
172181 EventDispatcher<const UUID&, const IndexableName&, const SharedNodeData&> OnEmitOutput;
173182
174183 protected:
175184 mutable std::mutex _mutex;
176185
186+ // / Event to be used by the graph to propagate output updates.
187+ Event<const UUID&, const IndexableName&, const SharedNodeData&> _propagate_output_update;
188+
189+ friend class Graph ;
190+
177191 private:
178192 UUID _id;
179193 std::string _class_name;
@@ -185,33 +199,140 @@ class Node
185199 PortMap _output_ports;
186200};
187201
188- #define OVERLOAD_PORT_TYPE (Original, As ) \
189- template <> \
190- inline auto FLOW_NAMESPACE::Node::GetInputData<Original>(const IndexableName& key) const noexcept \
191- { \
192- return GetInputData<As>(key); \
193- } \
194- template <> \
195- inline auto FLOW_NAMESPACE::Node::GetOutputData<Original>(const IndexableName& key) const noexcept \
196- { \
197- return GetOutputData<As>(key); \
198- } \
199- template <> \
200- inline void FLOW_NAMESPACE::Node::AddInput<Original>(std::string_view key, const std::string& caption, \
201- SharedNodeData data) \
202- { \
203- return AddInput<As>(key, caption, std::move (data)); \
204- } \
205- template <> \
206- inline void FLOW_NAMESPACE::Node::AddOutput<Original>(std::string_view key, const std::string& caption, \
207- SharedNodeData data) \
208- { \
209- return AddOutput<As>(key, caption, std::move (data)); \
210- }
202+ /* *
203+ * @brief Structure that reveals the types of the return value and arguments of a function at compile-time.
204+ * @tparam F The function type being analyzed.
205+ */
206+ template <typename F>
207+ struct FunctionTraits ;
208+
209+ template <typename R, typename ... Args>
210+ struct FunctionTraits <R(Args...)>
211+ {
212+ using ReturnType = std::invoke_result_t <R(Args...), Args...>;
213+ using ArgTypes = std::tuple<Args...>;
214+ };
211215
212216/* *
213- * Specialise const char* to use std::string for type safety.
217+ * @brief Node class that wraps a declared function.
218+ *
219+ * @details Wraps a declared function as a node type with inputs labeled in increasing alphabetical order starting at
220+ * 'a'. Requires that the function node be overloaded. If the function is overloaded, then supplying the
221+ * specific overload to wrap is required.
222+ *
223+ * @tparam F
224+ * @tparam Func
214225 */
215- OVERLOAD_PORT_TYPE (const char *, std::string);
226+
227+ template <typename F, std::add_pointer_t <std::remove_pointer_t <F>> Func>
228+ class FunctionWrapperNode : public Node
229+ {
230+ protected:
231+ using traits = FunctionTraits<std::remove_pointer_t <F>>;
232+ using output_t = typename traits::ReturnType;
233+ using arg_ts = typename traits::ArgTypes;
234+
235+ private:
236+ template <int ... Idx>
237+ void ParseArguments (std::integer_sequence<int , Idx...>)
238+ {
239+ (
240+ [&] {
241+ if constexpr (std::is_lvalue_reference_v<std::tuple_element_t <Idx, arg_ts>> &&
242+ !std::is_const_v<std::remove_reference_t <std::tuple_element_t <Idx, arg_ts>>>)
243+ {
244+ AddOutput<std::tuple_element_t <Idx, arg_ts>>({output_names[Idx] = ' a' + Idx}, " " );
245+ }
246+ else
247+ {
248+ AddInput<std::tuple_element_t <Idx, arg_ts>>({input_names[Idx] = ' a' + Idx}, " " );
249+ }
250+ }(),
251+ ...);
252+ }
253+
254+ template <int ... Idx>
255+ auto GetInputs (std::integer_sequence<int , Idx...>)
256+ {
257+ return std::make_tuple (GetInputData<std::tuple_element_t <Idx, arg_ts>>(IndexableName{input_names[Idx]})...);
258+ }
259+
260+ template <int ... Idx>
261+ json SaveInputs (std::integer_sequence<int , Idx...>) const
262+ {
263+ json inputs_json;
264+ (
265+ [&, this ] {
266+ const auto & key = input_names[Idx];
267+ if (auto x = GetInputData<std::tuple_element_t <Idx, arg_ts>>(IndexableName{key}))
268+ {
269+ inputs_json[key] = x->Get ();
270+ }
271+ }(),
272+ ...);
273+
274+ return inputs_json;
275+ }
276+
277+ template <int ... Idx>
278+ void RestoreInputs (json& j, std::integer_sequence<int , Idx...>)
279+ {
280+ (
281+ [&, this ] {
282+ const auto & key = input_names[Idx];
283+ if (!j.contains (key))
284+ {
285+ return ;
286+ }
287+
288+ if constexpr (!std::is_lvalue_reference_v<std::tuple_element_t <Idx, arg_ts>>)
289+ {
290+ SetInputData (IndexableName{key}, MakeNodeData<std::tuple_element_t <Idx, arg_ts>>(j[key]), false );
291+ }
292+ }(),
293+ ...);
294+ }
295+
296+ public:
297+ explicit FunctionWrapperNode (const UUID& uuid, const std::string& name, std::shared_ptr<Env> env)
298+ : Node(uuid, TypeName_v<FunctionWrapperNode<F, Func>>, name, std::move(env)), _func{Func}
299+ {
300+ ParseArguments (std::make_integer_sequence<int , std::tuple_size_v<arg_ts>>{});
301+ AddOutput<output_t >(" result" , " result" );
302+ }
303+
304+ virtual ~FunctionWrapperNode () = default ;
305+
306+ protected:
307+ void Compute () override
308+ {
309+ auto inputs = GetInputs (std::make_integer_sequence<int , std::tuple_size_v<arg_ts>>{});
310+
311+ if (std::apply ([](auto &&... args) { return (!args || ...); }, inputs))
312+ {
313+ return ;
314+ }
315+
316+ auto result = std::apply ([&](auto &&... args) { return _func (args->Get ()...); }, inputs);
317+ this ->SetOutputData (" result" , MakeNodeData (std::move (result)));
318+ }
319+
320+ json SaveInputs () const override
321+ {
322+ return SaveInputs (std::make_integer_sequence<int , std::tuple_size_v<arg_ts>>{});
323+ }
324+
325+ void RestoreInputs (const json& j) override
326+ {
327+ RestoreInputs (const_cast <json&>(j), std::make_integer_sequence<int , std::tuple_size_v<arg_ts>>{});
328+ }
329+
330+ private:
331+ std::add_pointer_t <std::remove_pointer_t <F>> _func;
332+ static inline std::array<std::string, std::tuple_size_v<arg_ts>> input_names{" " };
333+ static inline std::array<std::string, std::tuple_size_v<arg_ts>> output_names{" " };
334+ };
335+
336+ #define DECLARE_FUNCTION_NODE_TYPE (func ) FunctionWrapperNode<decltype (func), func>
216337
217338FLOW_NAMESPACE_END
0 commit comments