Skip to content

Commit 7027ef9

Browse files
committed
Bunch of updates and cleanup.
1 parent 9587a04 commit 7027ef9

File tree

11 files changed

+315
-107
lines changed

11 files changed

+315
-107
lines changed

include/flow/core/Module.hpp

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,11 @@ class NodeFactory;
1919

2020
class Module
2121
{
22+
struct HandleDelete
23+
{
24+
void operator()(void*);
25+
};
26+
2227
public:
2328
/**
2429
* @brief Constructs a flow module from a given directory.
@@ -42,6 +47,8 @@ class Module
4247
*/
4348
bool Unload();
4449

50+
bool IsLoaded() const noexcept { return _handle != nullptr; }
51+
4552
const std::string& GetName() const noexcept { return _name; }
4653

4754
const std::string& GetVersion() const noexcept { return _version; }
@@ -52,6 +59,9 @@ class Module
5259

5360
const std::vector<std::string>& GetDependencies() const noexcept { return _dependencies; }
5461

62+
private:
63+
void Validate(const json& module_json);
64+
5565
public:
5666
static const std::string FileExtension;
5767
static const std::string BinaryExtension;
@@ -64,7 +74,7 @@ class Module
6474
std::vector<std::string> _dependencies;
6575

6676
std::shared_ptr<NodeFactory> _factory;
67-
void* _handle = nullptr;
77+
std::unique_ptr<void, HandleDelete> _handle;
6878
};
6979

7080
FLOW_NAMESPACE_END

include/flow/core/Node.hpp

Lines changed: 147 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
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

217338
FLOW_NAMESPACE_END

include/flow/core/NodeData.hpp

Lines changed: 14 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -321,6 +321,20 @@ class NodeData<T&> : public INodeData
321321
return "Error: " + std::string(e.what());
322322
}
323323

324+
protected:
325+
void* AsPointer() const override { return std::bit_cast<void*>(&this->_value); }
326+
void FromPointer(void* value) override
327+
{
328+
if constexpr (std::is_copy_assignable_v<T>)
329+
{
330+
this->_value = *std::bit_cast<T*>(value);
331+
}
332+
else
333+
{
334+
this->_value = std::move(*std::bit_cast<T*>(value));
335+
}
336+
}
337+
324338
protected:
325339
T& _value;
326340
};
@@ -400,24 +414,6 @@ class NodeData<Ptr> : public detail::NodeData<Ptr>
400414
constexpr auto operator->() const noexcept { return this->_value.operator->(); }
401415
};
402416

403-
/**
404-
* @brief Specialisation for std::string to allow easy conversion to const char*.
405-
*/
406-
template<>
407-
class NodeData<std::string> : public detail::NodeData<std::string>
408-
{
409-
using Base = detail::NodeData<std::string>;
410-
411-
public:
412-
virtual ~NodeData() = default;
413-
414-
using Base::Base;
415-
using Base::operator=;
416-
417-
[[nodiscard]] operator const char*() const { return this->_value.c_str(); }
418-
[[nodiscard]] const char* Get() const { return this->_value.c_str(); }
419-
};
420-
421417
/**
422418
* @brief Disallow the use of const char* data type, and instead use the std::string version.
423419
*/

include/flow/core/NodeFactory.hpp

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ class NodeFactory
4646
* @param name The friendly name of the node to register it under.
4747
*/
4848
template<concepts::NodeType T>
49-
void RegisterNodeClass(const std::string& category, const std::string& name);
49+
void RegisterNodeClass(const std::string& category, const std::string& name = std::string{TypeName_v<T>});
5050

5151
/**
5252
* @brief Removes a node's construction method from the factory.
@@ -246,7 +246,8 @@ class Category
246246
* @param name The friendly name of the node to register it under.
247247
*/
248248
template<concepts::NodeType T>
249-
void RegisterNodeClass(const std::shared_ptr<NodeFactory>& factory, const std::string& friendly_name)
249+
void RegisterNodeClass(const std::shared_ptr<NodeFactory>& factory,
250+
const std::string& friendly_name = std::string{TypeName_v<T>})
250251
{
251252
factory->RegisterNodeClass<T>(_category_name, friendly_name);
252253
_classes.insert(std::make_pair(std::string{TypeName_v<T>}, friendly_name));

include/flow/core/TypeName.hpp

Lines changed: 48 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -40,15 +40,30 @@ constexpr std::string_view wrapped_type_name()
4040
#endif
4141
}
4242

43-
constexpr std::size_t wrapped_type_name_prefix_length()
43+
constexpr std::size_t wrapped_type_name_prefix_length() noexcept
4444
{
4545
return wrapped_type_name<void>().find(TypeName<void>::value);
4646
}
4747

48-
constexpr std::size_t wrapped_type_name_suffix_length()
48+
constexpr std::size_t wrapped_type_name_suffix_length() noexcept
4949
{
5050
return wrapped_type_name<void>().length() - wrapped_type_name_prefix_length() - TypeName<void>::value.length();
5151
}
52+
53+
template<typename T>
54+
constexpr std::string_view get_typename() noexcept
55+
{
56+
constexpr auto wrapped_name = detail::wrapped_type_name<T>();
57+
constexpr auto prefix_length = detail::wrapped_type_name_prefix_length();
58+
constexpr auto suffix_length = detail::wrapped_type_name_suffix_length();
59+
constexpr auto TypeName_length = wrapped_name.length() - prefix_length - suffix_length;
60+
constexpr auto name = wrapped_name.substr(prefix_length, TypeName_length);
61+
62+
constexpr auto adjusted_prefix_length = (name.starts_with("class") ? 6 : (name.starts_with("struct") ? 7 : 0));
63+
constexpr auto adjusted_TypeName_length = name.length() - adjusted_prefix_length;
64+
65+
return name.substr(adjusted_prefix_length, adjusted_TypeName_length);
66+
}
5267
} // namespace detail
5368

5469
template<typename T>
@@ -57,18 +72,37 @@ struct TypeName
5772
/**
5873
* @brief The string representation of the given type.
5974
*/
60-
static constexpr std::string_view value = [] {
61-
constexpr auto wrapped_name = detail::wrapped_type_name<T>();
62-
constexpr auto prefix_length = detail::wrapped_type_name_prefix_length();
63-
constexpr auto suffix_length = detail::wrapped_type_name_suffix_length();
64-
constexpr auto TypeName_length = wrapped_name.length() - prefix_length - suffix_length;
65-
constexpr auto name = wrapped_name.substr(prefix_length, TypeName_length);
66-
67-
constexpr auto adjusted_prefix_length = (name.starts_with("class") ? 6 : (name.starts_with("struct") ? 7 : 0));
68-
constexpr auto adjusted_TypeName_length = name.length() - adjusted_prefix_length;
69-
70-
return name.substr(adjusted_prefix_length, adjusted_TypeName_length);
71-
}();
75+
static constexpr std::string_view value = detail::get_typename<T>();
76+
77+
static constexpr bool is_reference = false;
78+
79+
static constexpr bool is_const = std::is_const_v<T>;
80+
};
81+
82+
template<typename T>
83+
struct TypeName<T&>
84+
{
85+
/**
86+
* @brief The string representation of the given type.
87+
*/
88+
static constexpr std::string_view value = detail::get_typename<T&>();
89+
90+
static constexpr bool is_reference = true;
91+
92+
static constexpr bool is_const = std::is_const_v<T>;
93+
};
94+
95+
template<typename T>
96+
struct TypeName<T&&>
97+
{
98+
/**
99+
* @brief The string representation of the given type.
100+
*/
101+
static constexpr std::string_view value = detail::get_typename<T&>();
102+
103+
static constexpr bool is_reference = true;
104+
105+
static constexpr bool is_const = std::is_const_v<T>;
72106
};
73107

74108
template<typename T, typename U>

0 commit comments

Comments
 (0)