From bf9e13b998a7b92fc0bc07233d3bef319cb90033 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David--Cl=C3=A9ris=20Timoth=C3=A9e?= Date: Sat, 22 Aug 2026 16:33:44 +0000 Subject: [PATCH 1/5] Add generic vector-based set_edges() to INode for Python-friendly wiring Nodes wire their edges through EXPAND_NODE_EDGES-generated typed setters that require exact shared_ptr per slot, which isn't expressible from Python bindings. Generate an additional set_edges overload taking plain vectors of IEdge shared_ptr, dynamic_cast each slot to the type the node actually expects, and raise a clear std::invalid_argument naming the slot, edge name, and expected/actual type on any mismatch (missing edge, null edge, wrong type, or wrong edge count). Optional slots accept a null entry as "no value". Assisted-by: Claude Code --- .../include/shamsolvergraph/node/INode.hpp | 127 ++++++++++++++++++ 1 file changed, 127 insertions(+) diff --git a/src/shamsolvergraph/include/shamsolvergraph/node/INode.hpp b/src/shamsolvergraph/include/shamsolvergraph/node/INode.hpp index 8d1d5eeb49..29a099c4ca 100644 --- a/src/shamsolvergraph/include/shamsolvergraph/node/INode.hpp +++ b/src/shamsolvergraph/include/shamsolvergraph/node/INode.hpp @@ -273,6 +273,73 @@ namespace shamrock::solvergraph { return sham::format("n_{}", this->get_uuid()); } + /// Cast a generic edge from a vector to the concrete type expected at a given slot, throwing + /// a message naming the slot, the edge name and the expected/actual type on any mismatch. + /// Used to implement the generic vector-based INode::set_edges() overload. + template + inline std::shared_ptr __node_edge_cast_checked( + const std::vector> &edges, + size_t slot, + const char *edge_name, + const char *type_name) { + if (slot >= edges.size()) { + throw shambase::make_except_with_loc(sham::format( + "set_edges: missing edge for slot {} (\"{}\"): expected type {}, but only {} " + "edge(s) were provided", + slot, + edge_name, + type_name, + edges.size())); + } + + const auto &edge = edges[slot]; + if (!edge) { + throw shambase::make_except_with_loc(sham::format( + "set_edges: edge at slot {} (\"{}\") is null: expected type {}", + slot, + edge_name, + type_name)); + } + + auto casted = std::dynamic_pointer_cast(edge); + if (!casted) { + throw shambase::make_except_with_loc(sham::format( + "set_edges: edge at slot {} (\"{}\") has the wrong type: expected {}, got {}", + slot, + edge_name, + type_name, + typeid(*edge).name())); + } + + return casted; + } + + /// Same as __node_edge_cast_checked, but for optional edge slots: a missing or null entry is + /// interpreted as "no value" and turned into a null-opt edge instead of raising an error. + template + inline std::shared_ptr __node_edge_cast_checked_optional( + const std::vector> &edges, + size_t slot, + const char *edge_name, + const char *type_name) { + if (slot >= edges.size() || !edges[slot]) { + return make_null_opt_edge(); + } + return __node_edge_cast_checked(edges, slot, edge_name, type_name); + } + + /// Ensure that all edges passed to the generic vector-based INode::set_edges() were consumed, + /// i.e. that neither too few nor too many edges were provided for a given side of the node. + inline void __node_edge_check_count(size_t provided, size_t expected, const char *side_name) { + if (provided != expected) { + throw shambase::make_except_with_loc(sham::format( + "set_edges: wrong number of {} edges provided: expected {}, got {}", + side_name, + expected, + provided)); + } + } + } // namespace shamrock::solvergraph #define INODE_DECL_RO(type, name) const type &name; @@ -286,6 +353,13 @@ namespace shamrock::solvergraph { #define INODE_GET_RO(type, name) get_ro_edge(ro++), #define INODE_GET_RW(type, name) get_rw_edge(rw++), +#define INODE_CHECK_RO1(type, name) \ + shamrock::solvergraph::__node_edge_cast_checked(ro_edges_in, ro_idx++, #name, #type), +#define INODE_CHECK_RW1(type, name) +#define INODE_CHECK_RO2(type, name) +#define INODE_CHECK_RW2(type, name) \ + shamrock::solvergraph::__node_edge_cast_checked(rw_edges_in, rw_idx++, #name, #type), + #define INODE_DECL_RO_OPTIONAL(type, name) \ const std::optional> name; #define INODE_DECL_RW_OPTIONAL(type, name) const std::optional> name; @@ -298,6 +372,15 @@ namespace shamrock::solvergraph { #define INODE_GET_RO_OPTIONAL(type, name) get_ro_edge_optional(ro++), #define INODE_GET_RW_OPTIONAL(type, name) get_rw_edge_optional(rw++), +#define INODE_CHECK_RO1_OPTIONAL(type, name) \ + shamrock::solvergraph::__node_edge_cast_checked_optional( \ + ro_edges_in, ro_idx++, #name, #type), +#define INODE_CHECK_RW1_OPTIONAL(type, name) +#define INODE_CHECK_RO2_OPTIONAL(type, name) +#define INODE_CHECK_RW2_OPTIONAL(type, name) \ + shamrock::solvergraph::__node_edge_cast_checked_optional( \ + rw_edges_in, rw_idx++, #name, #type), + #define EXPAND_NODE_EDGES(EDGES) \ \ struct Edges { \ @@ -312,6 +395,28 @@ namespace shamrock::solvergraph { __internal_set_rw_edges({EDGES(INODE_PUSH_RO2, INODE_PUSH_RW2)}); \ } \ \ + inline void set_edges( \ + std::vector> ro_edges_in, \ + std::vector> rw_edges_in, \ + SourceLocation loc = SourceLocation{}) { \ + __shamrock_log_callsite(loc); \ + \ + size_t ro_idx = 0; \ + size_t rw_idx = 0; \ + auto ro_casted = std::vector>{ \ + EDGES(INODE_CHECK_RO1, INODE_CHECK_RW1)}; \ + auto rw_casted = std::vector>{ \ + EDGES(INODE_CHECK_RO2, INODE_CHECK_RW2)}; \ + \ + shamrock::solvergraph::__node_edge_check_count( \ + ro_edges_in.size(), ro_idx, "read-only"); \ + shamrock::solvergraph::__node_edge_check_count( \ + rw_edges_in.size(), rw_idx, "read-write"); \ + \ + __internal_set_ro_edges(std::move(ro_casted)); \ + __internal_set_rw_edges(std::move(rw_casted)); \ + } \ + \ inline Edges get_edges() { \ int ro = 0; \ int rw = 0; \ @@ -335,6 +440,28 @@ namespace shamrock::solvergraph { INODE_PUSH_RO2, INODE_PUSH_RW2, INODE_PUSH_RO2_OPTIONAL, INODE_PUSH_RW2_OPTIONAL)}); \ } \ \ + inline void set_edges( \ + std::vector> ro_edges_in, \ + std::vector> rw_edges_in, \ + SourceLocation loc = SourceLocation{}) { \ + __shamrock_log_callsite(loc); \ + \ + size_t ro_idx = 0; \ + size_t rw_idx = 0; \ + auto ro_casted = std::vector>{EDGES( \ + INODE_CHECK_RO1, INODE_CHECK_RW1, INODE_CHECK_RO1_OPTIONAL, INODE_CHECK_RW1_OPTIONAL)};\ + auto rw_casted = std::vector>{EDGES( \ + INODE_CHECK_RO2, INODE_CHECK_RW2, INODE_CHECK_RO2_OPTIONAL, INODE_CHECK_RW2_OPTIONAL)};\ + \ + shamrock::solvergraph::__node_edge_check_count( \ + ro_edges_in.size(), ro_idx, "read-only"); \ + shamrock::solvergraph::__node_edge_check_count( \ + rw_edges_in.size(), rw_idx, "read-write"); \ + \ + __internal_set_ro_edges(std::move(ro_casted)); \ + __internal_set_rw_edges(std::move(rw_casted)); \ + } \ + \ inline Edges get_edges() { \ int ro = 0; \ int rw = 0; \ From 10733c90402581d5d28bf793771ec1f75539ada9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David--Cl=C3=A9ris=20Timoth=C3=A9e?= Date: Sat, 22 Aug 2026 16:50:32 +0000 Subject: [PATCH 2/5] Fix -Wpotentially-evaluated-expression in __node_edge_cast_checked Building shammodels_sph surfaced the warning on typeid(*edge).name(); bind a reference first, the same workaround INode::print_node_info() already uses for the identical typeid(*edge) pattern. Assisted-by: Claude Code --- src/shamsolvergraph/include/shamsolvergraph/node/INode.hpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/shamsolvergraph/include/shamsolvergraph/node/INode.hpp b/src/shamsolvergraph/include/shamsolvergraph/node/INode.hpp index 29a099c4ca..870e05eb74 100644 --- a/src/shamsolvergraph/include/shamsolvergraph/node/INode.hpp +++ b/src/shamsolvergraph/include/shamsolvergraph/node/INode.hpp @@ -303,12 +303,13 @@ namespace shamrock::solvergraph { auto casted = std::dynamic_pointer_cast(edge); if (!casted) { + const auto &e = *edge; // necessary to avoid -Wpotentially-evaluated-expression throw shambase::make_except_with_loc(sham::format( "set_edges: edge at slot {} (\"{}\") has the wrong type: expected {}, got {}", slot, edge_name, type_name, - typeid(*edge).name())); + typeid(e).name())); } return casted; From 3bcce8549e0b694d7e4ccb34fd36b00b7a6bf509 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David--Cl=C3=A9ris=20Timoth=C3=A9e?= Date: Sat, 22 Aug 2026 20:31:51 +0000 Subject: [PATCH 3/5] Apply clang-format to INode.hpp Assisted-by: Claude Code --- .../include/shamsolvergraph/node/INode.hpp | 32 ++++++++++--------- 1 file changed, 17 insertions(+), 15 deletions(-) diff --git a/src/shamsolvergraph/include/shamsolvergraph/node/INode.hpp b/src/shamsolvergraph/include/shamsolvergraph/node/INode.hpp index 870e05eb74..602222f6a8 100644 --- a/src/shamsolvergraph/include/shamsolvergraph/node/INode.hpp +++ b/src/shamsolvergraph/include/shamsolvergraph/node/INode.hpp @@ -354,7 +354,7 @@ namespace shamrock::solvergraph { #define INODE_GET_RO(type, name) get_ro_edge(ro++), #define INODE_GET_RW(type, name) get_rw_edge(rw++), -#define INODE_CHECK_RO1(type, name) \ +#define INODE_CHECK_RO1(type, name) \ shamrock::solvergraph::__node_edge_cast_checked(ro_edges_in, ro_idx++, #name, #type), #define INODE_CHECK_RW1(type, name) #define INODE_CHECK_RO2(type, name) @@ -402,17 +402,15 @@ namespace shamrock::solvergraph { SourceLocation loc = SourceLocation{}) { \ __shamrock_log_callsite(loc); \ \ - size_t ro_idx = 0; \ - size_t rw_idx = 0; \ + size_t ro_idx = 0; \ + size_t rw_idx = 0; \ auto ro_casted = std::vector>{ \ EDGES(INODE_CHECK_RO1, INODE_CHECK_RW1)}; \ auto rw_casted = std::vector>{ \ EDGES(INODE_CHECK_RO2, INODE_CHECK_RW2)}; \ \ - shamrock::solvergraph::__node_edge_check_count( \ - ro_edges_in.size(), ro_idx, "read-only"); \ - shamrock::solvergraph::__node_edge_check_count( \ - rw_edges_in.size(), rw_idx, "read-write"); \ + shamrock::solvergraph::__node_edge_check_count(ro_edges_in.size(), ro_idx, "read-only"); \ + shamrock::solvergraph::__node_edge_check_count(rw_edges_in.size(), rw_idx, "read-write"); \ \ __internal_set_ro_edges(std::move(ro_casted)); \ __internal_set_rw_edges(std::move(rw_casted)); \ @@ -447,17 +445,21 @@ namespace shamrock::solvergraph { SourceLocation loc = SourceLocation{}) { \ __shamrock_log_callsite(loc); \ \ - size_t ro_idx = 0; \ - size_t rw_idx = 0; \ + size_t ro_idx = 0; \ + size_t rw_idx = 0; \ auto ro_casted = std::vector>{EDGES( \ - INODE_CHECK_RO1, INODE_CHECK_RW1, INODE_CHECK_RO1_OPTIONAL, INODE_CHECK_RW1_OPTIONAL)};\ + INODE_CHECK_RO1, \ + INODE_CHECK_RW1, \ + INODE_CHECK_RO1_OPTIONAL, \ + INODE_CHECK_RW1_OPTIONAL)}; \ auto rw_casted = std::vector>{EDGES( \ - INODE_CHECK_RO2, INODE_CHECK_RW2, INODE_CHECK_RO2_OPTIONAL, INODE_CHECK_RW2_OPTIONAL)};\ + INODE_CHECK_RO2, \ + INODE_CHECK_RW2, \ + INODE_CHECK_RO2_OPTIONAL, \ + INODE_CHECK_RW2_OPTIONAL)}; \ \ - shamrock::solvergraph::__node_edge_check_count( \ - ro_edges_in.size(), ro_idx, "read-only"); \ - shamrock::solvergraph::__node_edge_check_count( \ - rw_edges_in.size(), rw_idx, "read-write"); \ + shamrock::solvergraph::__node_edge_check_count(ro_edges_in.size(), ro_idx, "read-only"); \ + shamrock::solvergraph::__node_edge_check_count(rw_edges_in.size(), rw_idx, "read-write"); \ \ __internal_set_ro_edges(std::move(ro_casted)); \ __internal_set_rw_edges(std::move(rw_casted)); \ From d420781cb05a51f54a78510bb1c3f4ec2f210c20 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David--Cl=C3=A9ris=20Timoth=C3=A9e?= Date: Sat, 22 Aug 2026 21:22:06 +0000 Subject: [PATCH 4/5] Layer INode::set_edges() so the generic cast utilities are reused Split the generic vector-based set_edges() into a three-tier chain instead of duplicating the per-slot casting logic: set_edges(vector, vector) picks each slot in order and forwards to set_edges_from_edges(shared_ptr...), which casts each slot to its concrete type via __node_edge_cast_checked(_optional) and forwards to the existing typed set_edges(shared_ptr...). set_edges_from_edges() needed a distinct name from set_edges(): for a node whose edge type already is IEdge (NodeFreeAlloc), the typed and untyped overloads would otherwise be identical and fail to compile. Vector slots are also bound to named local variables before the forwarding call, since argument evaluation order between call arguments is unspecified in C++ and the previous ro_idx++/rw_idx++ per-argument pattern relied on left-to-right order. Assisted-by: Claude Code --- .../include/shamsolvergraph/node/INode.hpp | 157 +++++++++--------- 1 file changed, 83 insertions(+), 74 deletions(-) diff --git a/src/shamsolvergraph/include/shamsolvergraph/node/INode.hpp b/src/shamsolvergraph/include/shamsolvergraph/node/INode.hpp index 602222f6a8..7352095e5f 100644 --- a/src/shamsolvergraph/include/shamsolvergraph/node/INode.hpp +++ b/src/shamsolvergraph/include/shamsolvergraph/node/INode.hpp @@ -273,40 +273,23 @@ namespace shamrock::solvergraph { return sham::format("n_{}", this->get_uuid()); } - /// Cast a generic edge from a vector to the concrete type expected at a given slot, throwing - /// a message naming the slot, the edge name and the expected/actual type on any mismatch. - /// Used to implement the generic vector-based INode::set_edges() overload. + /// Cast a single generic edge to the concrete type expected, throwing a message naming the + /// edge and the expected/actual type on any mismatch. Used to implement the untyped + /// per-slot INode::set_edges() overload's cast step, which the generic vector-based overload + /// forwards to. template inline std::shared_ptr __node_edge_cast_checked( - const std::vector> &edges, - size_t slot, - const char *edge_name, - const char *type_name) { - if (slot >= edges.size()) { - throw shambase::make_except_with_loc(sham::format( - "set_edges: missing edge for slot {} (\"{}\"): expected type {}, but only {} " - "edge(s) were provided", - slot, - edge_name, - type_name, - edges.size())); - } - - const auto &edge = edges[slot]; + const std::shared_ptr &edge, const char *edge_name, const char *type_name) { if (!edge) { throw shambase::make_except_with_loc(sham::format( - "set_edges: edge at slot {} (\"{}\") is null: expected type {}", - slot, - edge_name, - type_name)); + "set_edges: edge \"{}\" is null: expected type {}", edge_name, type_name)); } auto casted = std::dynamic_pointer_cast(edge); if (!casted) { const auto &e = *edge; // necessary to avoid -Wpotentially-evaluated-expression throw shambase::make_except_with_loc(sham::format( - "set_edges: edge at slot {} (\"{}\") has the wrong type: expected {}, got {}", - slot, + "set_edges: edge \"{}\" has the wrong type: expected {}, got {}", edge_name, type_name, typeid(e).name())); @@ -315,18 +298,15 @@ namespace shamrock::solvergraph { return casted; } - /// Same as __node_edge_cast_checked, but for optional edge slots: a missing or null entry is - /// interpreted as "no value" and turned into a null-opt edge instead of raising an error. + /// Same as above, but for optional edge slots: a null edge or a null-opt edge sentinel is + /// interpreted as "no value" instead of raising an error. template - inline std::shared_ptr __node_edge_cast_checked_optional( - const std::vector> &edges, - size_t slot, - const char *edge_name, - const char *type_name) { - if (slot >= edges.size() || !edges[slot]) { - return make_null_opt_edge(); + inline std::optional> __node_edge_cast_checked_optional( + const std::shared_ptr &edge, const char *edge_name, const char *type_name) { + if (!edge || is_null_opt_edge(edge)) { + return std::nullopt; } - return __node_edge_cast_checked(edges, slot, edge_name, type_name); + return __node_edge_cast_checked(edge, edge_name, type_name); } /// Ensure that all edges passed to the generic vector-based INode::set_edges() were consumed, @@ -354,12 +334,27 @@ namespace shamrock::solvergraph { #define INODE_GET_RO(type, name) get_ro_edge(ro++), #define INODE_GET_RW(type, name) get_rw_edge(rw++), -#define INODE_CHECK_RO1(type, name) \ - shamrock::solvergraph::__node_edge_cast_checked(ro_edges_in, ro_idx++, #name, #type), -#define INODE_CHECK_RW1(type, name) -#define INODE_CHECK_RO2(type, name) -#define INODE_CHECK_RW2(type, name) \ - shamrock::solvergraph::__node_edge_cast_checked(rw_edges_in, rw_idx++, #name, #type), +/// Param type used by the untyped, per-slot set_edges_from_edges() overload: same slot +/// count/order as the typed setter, but every slot is a plain IEdge, cast to its concrete type +/// before being forwarded to the typed setter. (Named differently from set_edges() itself: for a +/// node whose edge type already is IEdge, e.g. NodeFreeAlloc, the two would otherwise be the same +/// overload.) +#define INODE_PARAM_EDGE(type, name) const std::shared_ptr &name, + +#define INODE_CHECK_RO_ARG(type, name) \ + shamrock::solvergraph::__node_edge_cast_checked(name, #name, #type), +#define INODE_CHECK_RW_ARG(type, name) \ + shamrock::solvergraph::__node_edge_cast_checked(name, #name, #type), + +/// Count/declare/forward macros used by the generic vector-based set_edges() overload to forward +/// each vector slot, in order, to the untyped per-slot setter above. The slots are bound to named +/// local variables first (rather than picked directly as call arguments) because argument +/// evaluation order is unspecified in C++, and here it must match the vector's slot order. +#define INODE_COUNT_RO(type, name) ro_count++; +#define INODE_COUNT_RW(type, name) rw_count++; +#define INODE_DECLARE_PICK_RO(type, name) auto &&name = ro_edges_in[ro_idx++]; +#define INODE_DECLARE_PICK_RW(type, name) auto &&name = rw_edges_in[rw_idx++]; +#define INODE_FORWARD_ARG(type, name) name, #define INODE_DECL_RO_OPTIONAL(type, name) \ const std::optional> name; @@ -373,14 +368,10 @@ namespace shamrock::solvergraph { #define INODE_GET_RO_OPTIONAL(type, name) get_ro_edge_optional(ro++), #define INODE_GET_RW_OPTIONAL(type, name) get_rw_edge_optional(rw++), -#define INODE_CHECK_RO1_OPTIONAL(type, name) \ - shamrock::solvergraph::__node_edge_cast_checked_optional( \ - ro_edges_in, ro_idx++, #name, #type), -#define INODE_CHECK_RW1_OPTIONAL(type, name) -#define INODE_CHECK_RO2_OPTIONAL(type, name) -#define INODE_CHECK_RW2_OPTIONAL(type, name) \ - shamrock::solvergraph::__node_edge_cast_checked_optional( \ - rw_edges_in, rw_idx++, #name, #type), +#define INODE_CHECK_RO_ARG_OPTIONAL(type, name) \ + shamrock::solvergraph::__node_edge_cast_checked_optional(name, #name, #type), +#define INODE_CHECK_RW_ARG_OPTIONAL(type, name) \ + shamrock::solvergraph::__node_edge_cast_checked_optional(name, #name, #type), #define EXPAND_NODE_EDGES(EDGES) \ \ @@ -396,24 +387,31 @@ namespace shamrock::solvergraph { __internal_set_rw_edges({EDGES(INODE_PUSH_RO2, INODE_PUSH_RW2)}); \ } \ \ + inline void set_edges_from_edges( \ + EDGES(INODE_PARAM_EDGE, INODE_PARAM_EDGE) SourceLocation loc = SourceLocation{}) { \ + __shamrock_log_callsite(loc); \ + \ + set_edges(EDGES(INODE_CHECK_RO_ARG, INODE_CHECK_RW_ARG) loc); \ + } \ + \ inline void set_edges( \ std::vector> ro_edges_in, \ std::vector> rw_edges_in, \ SourceLocation loc = SourceLocation{}) { \ __shamrock_log_callsite(loc); \ \ - size_t ro_idx = 0; \ - size_t rw_idx = 0; \ - auto ro_casted = std::vector>{ \ - EDGES(INODE_CHECK_RO1, INODE_CHECK_RW1)}; \ - auto rw_casted = std::vector>{ \ - EDGES(INODE_CHECK_RO2, INODE_CHECK_RW2)}; \ + size_t ro_count = 0; \ + size_t rw_count = 0; \ + EDGES(INODE_COUNT_RO, INODE_COUNT_RW) \ \ - shamrock::solvergraph::__node_edge_check_count(ro_edges_in.size(), ro_idx, "read-only"); \ - shamrock::solvergraph::__node_edge_check_count(rw_edges_in.size(), rw_idx, "read-write"); \ + shamrock::solvergraph::__node_edge_check_count(ro_edges_in.size(), ro_count, "read-only"); \ + shamrock::solvergraph::__node_edge_check_count( \ + rw_edges_in.size(), rw_count, "read-write"); \ \ - __internal_set_ro_edges(std::move(ro_casted)); \ - __internal_set_rw_edges(std::move(rw_casted)); \ + size_t ro_idx = 0; \ + size_t rw_idx = 0; \ + EDGES(INODE_DECLARE_PICK_RO, INODE_DECLARE_PICK_RW) \ + set_edges_from_edges(EDGES(INODE_FORWARD_ARG, INODE_FORWARD_ARG) loc); \ } \ \ inline Edges get_edges() { \ @@ -439,30 +437,41 @@ namespace shamrock::solvergraph { INODE_PUSH_RO2, INODE_PUSH_RW2, INODE_PUSH_RO2_OPTIONAL, INODE_PUSH_RW2_OPTIONAL)}); \ } \ \ + inline void set_edges_from_edges( \ + EDGES(INODE_PARAM_EDGE, INODE_PARAM_EDGE, INODE_PARAM_EDGE, INODE_PARAM_EDGE) \ + SourceLocation loc = SourceLocation{}) { \ + __shamrock_log_callsite(loc); \ + \ + set_edges(EDGES( \ + INODE_CHECK_RO_ARG, \ + INODE_CHECK_RW_ARG, \ + INODE_CHECK_RO_ARG_OPTIONAL, \ + INODE_CHECK_RW_ARG_OPTIONAL) loc); \ + } \ + \ inline void set_edges( \ std::vector> ro_edges_in, \ std::vector> rw_edges_in, \ SourceLocation loc = SourceLocation{}) { \ __shamrock_log_callsite(loc); \ \ - size_t ro_idx = 0; \ - size_t rw_idx = 0; \ - auto ro_casted = std::vector>{EDGES( \ - INODE_CHECK_RO1, \ - INODE_CHECK_RW1, \ - INODE_CHECK_RO1_OPTIONAL, \ - INODE_CHECK_RW1_OPTIONAL)}; \ - auto rw_casted = std::vector>{EDGES( \ - INODE_CHECK_RO2, \ - INODE_CHECK_RW2, \ - INODE_CHECK_RO2_OPTIONAL, \ - INODE_CHECK_RW2_OPTIONAL)}; \ + size_t ro_count = 0; \ + size_t rw_count = 0; \ + EDGES(INODE_COUNT_RO, INODE_COUNT_RW, INODE_COUNT_RO, INODE_COUNT_RW) \ \ - shamrock::solvergraph::__node_edge_check_count(ro_edges_in.size(), ro_idx, "read-only"); \ - shamrock::solvergraph::__node_edge_check_count(rw_edges_in.size(), rw_idx, "read-write"); \ + shamrock::solvergraph::__node_edge_check_count(ro_edges_in.size(), ro_count, "read-only"); \ + shamrock::solvergraph::__node_edge_check_count( \ + rw_edges_in.size(), rw_count, "read-write"); \ \ - __internal_set_ro_edges(std::move(ro_casted)); \ - __internal_set_rw_edges(std::move(rw_casted)); \ + size_t ro_idx = 0; \ + size_t rw_idx = 0; \ + EDGES( \ + INODE_DECLARE_PICK_RO, \ + INODE_DECLARE_PICK_RW, \ + INODE_DECLARE_PICK_RO, \ + INODE_DECLARE_PICK_RW) \ + set_edges_from_edges(EDGES( \ + INODE_FORWARD_ARG, INODE_FORWARD_ARG, INODE_FORWARD_ARG, INODE_FORWARD_ARG) loc); \ } \ \ inline Edges get_edges() { \ From 6d96dd14ee194c95793ea44f627b6015da43aaed Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David--Cl=C3=A9ris=20Timoth=C3=A9e?= Date: Sat, 22 Aug 2026 21:26:08 +0000 Subject: [PATCH 5/5] Rename set_edges_from_edges() to set_edges_untyped() Assisted-by: Claude Code --- .../include/shamsolvergraph/node/INode.hpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/shamsolvergraph/include/shamsolvergraph/node/INode.hpp b/src/shamsolvergraph/include/shamsolvergraph/node/INode.hpp index 7352095e5f..7bf1f17c39 100644 --- a/src/shamsolvergraph/include/shamsolvergraph/node/INode.hpp +++ b/src/shamsolvergraph/include/shamsolvergraph/node/INode.hpp @@ -334,7 +334,7 @@ namespace shamrock::solvergraph { #define INODE_GET_RO(type, name) get_ro_edge(ro++), #define INODE_GET_RW(type, name) get_rw_edge(rw++), -/// Param type used by the untyped, per-slot set_edges_from_edges() overload: same slot +/// Param type used by the untyped, per-slot set_edges_untyped() overload: same slot /// count/order as the typed setter, but every slot is a plain IEdge, cast to its concrete type /// before being forwarded to the typed setter. (Named differently from set_edges() itself: for a /// node whose edge type already is IEdge, e.g. NodeFreeAlloc, the two would otherwise be the same @@ -387,7 +387,7 @@ namespace shamrock::solvergraph { __internal_set_rw_edges({EDGES(INODE_PUSH_RO2, INODE_PUSH_RW2)}); \ } \ \ - inline void set_edges_from_edges( \ + inline void set_edges_untyped( \ EDGES(INODE_PARAM_EDGE, INODE_PARAM_EDGE) SourceLocation loc = SourceLocation{}) { \ __shamrock_log_callsite(loc); \ \ @@ -411,7 +411,7 @@ namespace shamrock::solvergraph { size_t ro_idx = 0; \ size_t rw_idx = 0; \ EDGES(INODE_DECLARE_PICK_RO, INODE_DECLARE_PICK_RW) \ - set_edges_from_edges(EDGES(INODE_FORWARD_ARG, INODE_FORWARD_ARG) loc); \ + set_edges_untyped(EDGES(INODE_FORWARD_ARG, INODE_FORWARD_ARG) loc); \ } \ \ inline Edges get_edges() { \ @@ -437,7 +437,7 @@ namespace shamrock::solvergraph { INODE_PUSH_RO2, INODE_PUSH_RW2, INODE_PUSH_RO2_OPTIONAL, INODE_PUSH_RW2_OPTIONAL)}); \ } \ \ - inline void set_edges_from_edges( \ + inline void set_edges_untyped( \ EDGES(INODE_PARAM_EDGE, INODE_PARAM_EDGE, INODE_PARAM_EDGE, INODE_PARAM_EDGE) \ SourceLocation loc = SourceLocation{}) { \ __shamrock_log_callsite(loc); \ @@ -470,7 +470,7 @@ namespace shamrock::solvergraph { INODE_DECLARE_PICK_RW, \ INODE_DECLARE_PICK_RO, \ INODE_DECLARE_PICK_RW) \ - set_edges_from_edges(EDGES( \ + set_edges_untyped(EDGES( \ INODE_FORWARD_ARG, INODE_FORWARD_ARG, INODE_FORWARD_ARG, INODE_FORWARD_ARG) loc); \ } \ \