From dad7fee983eeaec5364dfa04c41c2d243ae52697 Mon Sep 17 00:00:00 2001 From: Phil Ratzloff Date: Wed, 15 Nov 2023 13:08:27 -0500 Subject: [PATCH 01/10] Replace old tag_invoke-based partition_id(g,u) and partition_id(g,uid) with new style CPO --- include/graph/detail/graph_cpo.hpp | 252 +++++++++++++++++++---------- 1 file changed, 165 insertions(+), 87 deletions(-) diff --git a/include/graph/detail/graph_cpo.hpp b/include/graph/detail/graph_cpo.hpp index 78f32ff..179897b 100644 --- a/include/graph/detail/graph_cpo.hpp +++ b/include/graph/detail/graph_cpo.hpp @@ -182,7 +182,7 @@ namespace _Vertex_id { { _Fake_copy_init(ui->vertex_id(__g)) }; }; template - concept _Has_ref_ADL = _Has_class_or_enum_type<_G> // + concept _Has_ref_ADL = _Has_class_or_enum_type<_G> // && requires(_G&& __g, const vertex_iterator_t<_G> ui) { { _Fake_copy_init(vertex_id(__g, ui)) }; // intentional ADL }; @@ -297,7 +297,7 @@ namespace _Find_vertex { }; template - concept _Has_ADL = _Has_class_or_enum_type<_G> // + concept _Has_ADL = _Has_class_or_enum_type<_G> // && requires(_G&& __g, const vertex_id_t<_G>& uid) { { _Fake_copy_init(find_vertex(__g, uid)) }; // intentional ADL }; @@ -367,68 +367,146 @@ inline namespace _Cpos { // partition_id(g,uid) -> ? default = vertex_id_t() if not overridden; must be overridden for bipartite or multipartite graphs // partition_id(g,u) default = partition_id(g,vertex_id(u)) // -namespace tag_invoke { - TAG_INVOKE_DEF(partition_id); +namespace _Partition_id { +# if defined(__clang__) || defined(__EDG__) // TRANSITION, VSO-1681199 + void partition_id() = delete; // Block unqualified name lookup +# else // ^^^ no workaround / workaround vvv + void partition_id(); +# endif // ^^^ workaround ^^^ - template - concept _has_partition_id_uid_adl = requires(G&& g, vertex_id_t uid) { - { partition_id(g, uid) }; + template + concept _Has_ref_member = requires(_G&& __g, vertex_reference_t<_G> u) { + { _Fake_copy_init(u.partition_id(__g)) }; }; + template + concept _Has_ref_ADL = _Has_class_or_enum_type<_G> // + && requires(_G&& __g, const vertex_reference_t<_G>& u) { + { _Fake_copy_init(partition_id(__g, u)) }; // intentional ADL + }; + template + concept _Can_ref_eval = _Has_class_or_enum_type<_G> // + && requires(_G&& __g, vertex_id_t<_G> uid) { + { _Fake_copy_init(vertex_id_t<_G>{0}) }; + }; - template - concept _has_partition_id_uref_adl = requires(G&& g, vertex_reference_t u) { - { partition_id(g, u) }; - }; -} // namespace tag_invoke + template + concept _Has_id_ADL = _Has_class_or_enum_type<_G> // + && requires(_G&& __g, const vertex_id_t<_G>& uid) { + { _Fake_copy_init(partition_id(__g, uid)) }; // intentional ADL + }; + template + concept _Can_id_eval = _Has_class_or_enum_type<_G> // + && requires(_G&& __g, vertex_id_t<_G> uid) { + { _Fake_copy_init(vertex_id_t<_G>{0}) }; + }; -/** - * @brief Get's the parition_id of a vertex. - * - * Complexity: O(1) - * - * Default implementation: partition_id(g,vertex_id(g,u)) - * - * This is a customization point function that may be overriden if graph G supports bi-partite - * or multi-partite graphs. If it doesn't then a value of 0 is returned. - * - * @tparam G The graph type. - * @param g A graph instance. - * @param uid A vertex id for a vertex in graph G. - * @return The partition id of a vertex. 0 if G doesn't support partitioning. -*/ -template -//requires tag_invoke::_has_partition_id_uref_adl -auto partition_id(G&& g, vertex_reference_t u) { - if constexpr (tag_invoke::_has_partition_id_uref_adl) - return tag_invoke::partition_id(g, u); - else if constexpr (is_integral_v>) { - return vertex_id_t(); - } else - return size_t(0); -} + class _Cpo { + private: + enum class _St_id { _None, _Non_member, _Auto_eval }; + enum class _St_ref { _None, _Member, _Non_member, _Auto_eval }; -/** - * @brief Get's the parition_id of a vertex_id. - * - * Complexity: O(1) - * - * Default implementation: 0; graph container must define when supported - * - * This is a customization point function that may be overriden if graph G supports bi-partite - * or multi-partite graphs. If it doesn't then a value of 0 is returned. - * - * @tparam G The graph type. - * @param g A graph instance. - * @param uid A vertex id for a vertex in graph G. - * @return The partition id of a vertex. 0 if G doesn't support partitioning. -*/ -template -//requires tag_invoke::_has_partition_id_uid_adl -auto partition_id(G&& g, vertex_id_t uid) { - if constexpr (tag_invoke::_has_partition_id_uid_adl) - return tag_invoke::partition_id(g, uid); - else - return partition_id(g, *find_vertex(g, uid)); + template + [[nodiscard]] static consteval _Choice_t<_St_id> _Choose_id() noexcept { + static_assert(is_lvalue_reference_v<_G>); + using _UnCV = remove_cvref_t<_G>; + + if constexpr (_Has_id_ADL<_G, _UnCV>) { + return {_St_id::_Non_member, + noexcept(_Fake_copy_init(partition_id(declval<_G>(), declval>())))}; // intentional ADL + } else if constexpr (_Can_id_eval<_G, _UnCV>) { + return {_St_id::_Auto_eval, noexcept(_Fake_copy_init(vertex_id_t<_G>(0)))}; // default impl + } else { + return {_St_id::_None}; + } + } + + template + static constexpr _Choice_t<_St_id> _Choice_id = _Choose_id<_G>(); + + template + [[nodiscard]] static consteval _Choice_t<_St_ref> _Choose_ref() noexcept { + static_assert(is_lvalue_reference_v<_G>); + using _UnCV = remove_cvref_t<_G>; + + if constexpr (_Has_ref_member<_G, _UnCV>) { + return {_St_ref::_Member, + noexcept(_Fake_copy_init(declval>().partition_id(declval<_G>())))}; + } else if constexpr (_Has_ref_ADL<_G, _UnCV>) { + return {_St_ref::_Non_member, noexcept(_Fake_copy_init(partition_id( + declval<_G>(), declval>())))}; // intentional ADL + } else if constexpr (_Can_ref_eval<_G, _UnCV>) { + return {_St_ref::_Auto_eval, noexcept(_Fake_copy_init(vertex_id_t<_G>(0)))}; + } else { + return {_St_ref::_None}; + } + } + + template + static constexpr _Choice_t<_St_ref> _Choice_ref = _Choose_ref<_G>(); + + public: + /** + * @brief The number of outgoing edges of a vertex. + * + * Complexity: O(1) + * + * Default implementation: vertex_id_t<_G>(0) + * + * @tparam G The graph type. + * @param g A graph instance. + * @param u A vertex instance. + * @return The number of outgoing edges of vertex u. + */ + template + requires(_Choice_ref<_G&>._Strategy != _St_ref::_None) + [[nodiscard]] constexpr auto operator()(_G&& __g, vertex_reference_t<_G> u) const + noexcept(_Choice_ref<_G&>._No_throw) { + constexpr _St_ref _Strat_ref = _Choice_ref<_G&>._Strategy; + + if constexpr (_Strat_ref == _St_ref::_Member) { + return u.partition_id(__g); + } else if constexpr (_Strat_ref == _St_ref::_Non_member) { + return partition_id(__g, u); // intentional ADL + } else if constexpr (_Strat_ref == _St_ref::_Auto_eval) { + return vertex_id_t<_G>(0); // default impl + } else { + static_assert(_Always_false<_G>, + "partition_id(g,u) is not defined and the default implementation cannot be evaluated"); + } + } + + /** + * @brief Get the outgoing partition_id of a vertex id. + * + * Complexity: O(1) + * + * Default implementation: vertex_id_t<_G>(0) + * + * @tparam G The graph type. + * @param g A graph instance. + * @param uid Vertex id. + * @return A range of the outgoing partition_id. + */ + template + requires(_Choice_id<_G&>._Strategy != _St_id::_None) + [[nodiscard]] constexpr auto operator()(_G&& __g, const vertex_id_t<_G>& uid) const + noexcept(_Choice_id<_G&>._No_throw) { + constexpr _St_id _Strat_id = _Choice_id<_G&>._Strategy; + + if constexpr (_Strat_id == _St_id::_Non_member) { + return partition_id(__g, uid); // intentional ADL + } else if constexpr (_Strat_id == _St_id::_Auto_eval) { + return vertex_id_t<_G>(0); // default impl + } else { + static_assert(_Always_false<_G>, + "partition_id(g,uid) is not defined and the default implementation cannot be evaluated"); + } + } + }; +} // namespace _Partition_id + +inline namespace _Cpos { + inline constexpr _Partition_id::_Cpo partition_id; } @@ -544,7 +622,7 @@ namespace _Target_id { { _Fake_copy_init(uv.target_id(__g)) }; }; template - concept _Has_ref_ADL = _Has_class_or_enum_type<_G> // + concept _Has_ref_ADL = _Has_class_or_enum_type<_G> // && requires(_G&& __g, const edge_reference_t<_G>& uv) { { _Fake_copy_init(target_id(__g, uv)) }; // intentional ADL }; @@ -621,7 +699,7 @@ namespace _Target { # endif // ^^^ workaround ^^^ template - concept _Has_ref_ADL = _Has_class_or_enum_type<_G> // + concept _Has_ref_ADL = _Has_class_or_enum_type<_G> // && requires(_G&& __g, const edge_reference_t<_G>& uv) { { _Fake_copy_init(target(__g, uv)) }; // intentional ADL }; @@ -708,7 +786,7 @@ namespace _EL_Source_id { { _Fake_copy_init(uv.source_id(__g)) }; }; template - concept _Has_ref_ADL = _Has_class_or_enum_type<_G> // + concept _Has_ref_ADL = _Has_class_or_enum_type<_G> // && requires(_G&& __g, const edge_reference_t<_G>& uv) { { _Fake_copy_init(source_id(__g, uv)) }; // intentional ADL }; @@ -785,7 +863,7 @@ namespace _Source { # endif // ^^^ workaround ^^^ template - concept _Has_ref_ADL = _Has_class_or_enum_type<_G> // + concept _Has_ref_ADL = _Has_class_or_enum_type<_G> // && requires(_G&& __g, const edge_reference_t<_G>& uv) { { _Fake_copy_init(source(__g, uv)) }; // intentional ADL }; @@ -881,7 +959,7 @@ namespace _Edge_id { { uv.edge_id(__g) } -> convertible_to>; }; template - concept _Has_ref_ADL = _Has_class_or_enum_type<_G> // + concept _Has_ref_ADL = _Has_class_or_enum_type<_G> // && requires(_G&& __g, const edge_reference_t<_G>& uv) { { _Fake_copy_init(edge_id(__g, uv)) } -> convertible_to>; // intentional ADL }; @@ -977,7 +1055,7 @@ namespace _Find_vertex_edge { }; template - concept _Has_ref_ADL = _Has_class_or_enum_type<_G> // + concept _Has_ref_ADL = _Has_class_or_enum_type<_G> // && requires(_G&& __g, vertex_reference_t<_G> u, const vertex_id_t<_G>& vid) { { _Fake_copy_init(find_vertex_edge(__g, u, vid)) }; // intentional ADL }; @@ -988,7 +1066,7 @@ namespace _Find_vertex_edge { }; template - concept _Has_id_ADL = _Has_class_or_enum_type<_G> // + concept _Has_id_ADL = _Has_class_or_enum_type<_G> // && requires(_G&& __g, vertex_id_t<_G> uid, const vertex_id_t<_G>& vid) { { _Fake_copy_init(find_vertex_edge(__g, uid, vid)) }; // intentional ADL }; @@ -1129,7 +1207,7 @@ namespace _Contains_edge { # endif // ^^^ workaround ^^^ template - concept _Has_ref_ADL = _Has_class_or_enum_type<_G> // + concept _Has_ref_ADL = _Has_class_or_enum_type<_G> // && requires(_G&& __g, const vertex_id_t<_G>& uid, const vertex_id_t<_G>& vid) { { _Fake_copy_init(contains_edge(__g, uid, vid)) }; // intentional ADL }; @@ -1231,7 +1309,7 @@ namespace _NumVertices { { _Fake_copy_init(__g.num_vertices(__g)) }; }; template - concept _Has_ref_ADL = _Has_class_or_enum_type<_G> // + concept _Has_ref_ADL = _Has_class_or_enum_type<_G> // && requires(_G&& __g) { { _Fake_copy_init(num_vertices(__g)) }; // intentional ADL }; @@ -1242,7 +1320,7 @@ namespace _NumVertices { }; template - concept _Has_id_ADL = _Has_class_or_enum_type<_G> // + concept _Has_id_ADL = _Has_class_or_enum_type<_G> // && requires(_G&& __g, partition_id_t<_G> pid) { { _Fake_copy_init(num_vertices(__g, pid)) }; // intentional ADL }; @@ -1317,7 +1395,7 @@ namespace _NumVertices { static_assert(_Strat_id == _St_id::_Auto_eval); if constexpr (_Strat_id == _St_id::_Non_member) { - return num_vertices(__g, pid); // intentional ADL + return num_vertices(__g, pid); // intentional ADL } else if constexpr (_Strat_id == _St_id::_Auto_eval) { return ranges::size(vertices(__g, pid)); // default impl } else { @@ -1345,7 +1423,7 @@ namespace _NumVertices { if constexpr (_Strat_id == _St_ref::_Member) { return __g.num_vertices(); } else if constexpr (_Strat_id == _St_ref::_Non_member) { - return num_vertices(__g); // intentional ADL + return num_vertices(__g); // intentional ADL } else if constexpr (_Strat_id == _St_ref::_Auto_eval) { return ranges::size(vertices(__g)); // default impl } else { @@ -1376,7 +1454,7 @@ namespace _Degree { { _Fake_copy_init(u.degree(__g)) }; }; template - concept _Has_ref_ADL = _Has_class_or_enum_type<_G> // + concept _Has_ref_ADL = _Has_class_or_enum_type<_G> // && requires(_G&& __g, const vertex_reference_t<_G>& u) { { _Fake_copy_init(degree(__g, u)) }; // intentional ADL }; @@ -1387,7 +1465,7 @@ namespace _Degree { }; template - concept _Has_id_ADL = _Has_class_or_enum_type<_G> // + concept _Has_id_ADL = _Has_class_or_enum_type<_G> // && requires(_G&& __g, const vertex_id_t<_G>& uid) { { _Fake_copy_init(degree(__g, uid)) }; // intentional ADL }; @@ -1464,7 +1542,7 @@ namespace _Degree { if constexpr (_Strat_ref == _St_ref::_Member) { return u.degree(__g); } else if constexpr (_Strat_ref == _St_ref::_Non_member) { - return degree(__g, u); // intentional ADL + return degree(__g, u); // intentional ADL } else if constexpr (_Strat_ref == _St_ref::_Auto_eval) { return ranges::size(edges(__g, u)); // default impl } else { @@ -1492,7 +1570,7 @@ namespace _Degree { constexpr _St_id _Strat_id = _Choice_id<_G&>._Strategy; if constexpr (_Strat_id == _St_id::_Non_member) { - return degree(__g, uid); // intentional ADL + return degree(__g, uid); // intentional ADL } else if constexpr (_Strat_id == _St_id::_Auto_eval) { return ranges::size(edges(__g, uid)); // default impl } else { @@ -1525,7 +1603,7 @@ namespace _Vertex_value { { _Fake_copy_init(u.vertex_value(__g)) }; }; template - concept _Has_ref_ADL = _Has_class_or_enum_type<_G> // + concept _Has_ref_ADL = _Has_class_or_enum_type<_G> // && requires(_G&& __g, vertex_reference_t<_G> u) { { _Fake_copy_init(vertex_value(__g, u)) }; // intentional ADL }; @@ -1607,7 +1685,7 @@ namespace _Edge_value { { _Fake_copy_init(uv.edge_value(__g)) }; }; template - concept _Has_ref_ADL = _Has_class_or_enum_type<_G> // + concept _Has_ref_ADL = _Has_class_or_enum_type<_G> // && requires(_G&& __g, edge_reference_t<_G> uv) { { _Fake_copy_init(edge_value(__g, uv)) }; // intentional ADL }; @@ -1692,7 +1770,7 @@ namespace _Graph_value { { _Fake_copy_init(__g.graph_value()) }; }; template - concept _Has_ref_ADL = _Has_class_or_enum_type<_G> // + concept _Has_ref_ADL = _Has_class_or_enum_type<_G> // && requires(_G&& __g) { { _Fake_copy_init(graph_value(__g)) }; // intentional ADL }; @@ -1786,7 +1864,7 @@ namespace edgelist { { _Fake_copy_init(uv.source_id(__g)) }; }; template - concept _Has_ref_ADL = _Has_class_or_enum_type<_G> // + concept _Has_ref_ADL = _Has_class_or_enum_type<_G> // && requires(_G&& __g, const edge_reference_t<_G>& uv) { { _Fake_copy_init(source_id(__g, uv)) }; // intentional ADL }; @@ -1986,7 +2064,7 @@ namespace _Partition_vertex_id { }; template - concept _Has_UId_ADL = _Has_class_or_enum_type<_G> // + concept _Has_UId_ADL = _Has_class_or_enum_type<_G> // && requires(_G&& __g, vertex_id_t<_G> uid) { { _Fake_copy_init(partition_vertex_id(__g, uid)) }; // intentional ADL }; @@ -1997,7 +2075,7 @@ namespace _Partition_vertex_id { }; template - concept _Has_UIter_ADL = _Has_class_or_enum_type<_G> // + concept _Has_UIter_ADL = _Has_class_or_enum_type<_G> // && requires(_G&& __g, vertex_iterator_t<_G> ui) { { _Fake_copy_init(partition_vertex_id(__g, ui)) }; // intentional ADL }; @@ -2104,7 +2182,7 @@ namespace _Partition_vertex_id { if constexpr (_Strat == _StIter::_Member) { return __g.partition_vertex_id(ui); } else if constexpr (_Strat == _StIter::_Non_member) { - return partition_vertex_id(__g, ui); // intentional ADL + return partition_vertex_id(__g, ui); // intentional ADL } else { return (*this)(__g, vertex_id(__g, ui)); // use partition_vertex_id(g, vertex_id(g,ui)) } @@ -2134,7 +2212,7 @@ namespace _Find_partition_vertex { }; template - concept _Has_UId_ADL = _Has_class_or_enum_type<_G> // + concept _Has_UId_ADL = _Has_class_or_enum_type<_G> // && requires(_G&& __g, partition_vertex_id_t<_G> puid) { { _Fake_copy_init(find_partition_vertex(__g, puid)) }; // intentional ADL }; @@ -2291,7 +2369,7 @@ namespace _Partition_target_id { }; template - concept _Has_UVRef__ADL = _Has_class_or_enum_type<_G> // + concept _Has_UVRef__ADL = _Has_class_or_enum_type<_G> // && requires(_G&& __g, edge_reference_t<_G> uv) { { _Fake_copy_init(partition_target_id(__g, uv)) }; // intentional ADL }; @@ -2347,7 +2425,7 @@ namespace _Partition_target_id { if constexpr (_Strat == _StRef::_Member) { return __g.partition_target_id(uv); } else if constexpr (_Strat == _StRef::_Non_member) { - return partition_target_id(__g, uv); // intentional ADL + return partition_target_id(__g, uv); // intentional ADL } else { return partition_vertex_id_t<_G>{0, target_id(__g, uv)}; // assume 1 partition with all vertices } @@ -2377,7 +2455,7 @@ namespace _Partition_source_id { }; template - concept _Has_UVRef__ADL = _Has_class_or_enum_type<_G> // + concept _Has_UVRef__ADL = _Has_class_or_enum_type<_G> // && requires(_G&& __g, edge_reference_t<_G> uv) { { _Fake_copy_init(partition_source_id(__g, uv)) }; // intentional ADL }; @@ -2433,7 +2511,7 @@ namespace _Partition_source_id { if constexpr (_Strat == _StRef::_Member) { return __g.partition_source_id(uv); } else if constexpr (_Strat == _StRef::_Non_member) { - return partition_source_id(__g, uv); // intentional ADL + return partition_source_id(__g, uv); // intentional ADL } else { return partition_vertex_id_t<_G>{0, source_id(__g, uv)}; // assume 1 partition with all vertices } From 227ad97ddf2fdde9444f15f866d9fd6245fee668 Mon Sep 17 00:00:00 2001 From: Phil Ratzloff Date: Wed, 15 Nov 2023 13:23:47 -0500 Subject: [PATCH 02/10] Replace old tag_invoke-based partition_count(g,u) with new style CPO Correct documentation --- include/graph/detail/graph_cpo.hpp | 135 +++++++++++++++++++---------- 1 file changed, 91 insertions(+), 44 deletions(-) diff --git a/include/graph/detail/graph_cpo.hpp b/include/graph/detail/graph_cpo.hpp index 179897b..f069a62 100644 --- a/include/graph/detail/graph_cpo.hpp +++ b/include/graph/detail/graph_cpo.hpp @@ -368,11 +368,11 @@ inline namespace _Cpos { // partition_id(g,u) default = partition_id(g,vertex_id(u)) // namespace _Partition_id { -# if defined(__clang__) || defined(__EDG__) // TRANSITION, VSO-1681199 - void partition_id() = delete; // Block unqualified name lookup -# else // ^^^ no workaround / workaround vvv +# if defined(__clang__) || defined(__EDG__) // TRANSITION, VSO-1681199 + void partition_id() = delete; // Block unqualified name lookup +# else // ^^^ no workaround / workaround vvv void partition_id(); -# endif // ^^^ workaround ^^^ +# endif // ^^^ workaround ^^^ template concept _Has_ref_member = requires(_G&& __g, vertex_reference_t<_G> u) { @@ -384,7 +384,7 @@ namespace _Partition_id { { _Fake_copy_init(partition_id(__g, u)) }; // intentional ADL }; template - concept _Can_ref_eval = _Has_class_or_enum_type<_G> // + concept _Can_ref_eval = _Has_class_or_enum_type<_G> && integral> // && requires(_G&& __g, vertex_id_t<_G> uid) { { _Fake_copy_init(vertex_id_t<_G>{0}) }; }; @@ -395,8 +395,8 @@ namespace _Partition_id { { _Fake_copy_init(partition_id(__g, uid)) }; // intentional ADL }; template - concept _Can_id_eval = _Has_class_or_enum_type<_G> // - && requires(_G&& __g, vertex_id_t<_G> uid) { + concept _Can_id_eval = _Has_class_or_enum_type<_G> && integral> // + && requires(_G&& __g) { { _Fake_copy_init(vertex_id_t<_G>{0}) }; }; @@ -446,16 +446,16 @@ namespace _Partition_id { public: /** - * @brief The number of outgoing edges of a vertex. + * @brief The partition id of a vertex * * Complexity: O(1) * - * Default implementation: vertex_id_t<_G>(0) + * Default implementation: vertex_id_t<_G>(0) if vertex_id_t<_G> is integral * * @tparam G The graph type. * @param g A graph instance. * @param u A vertex instance. - * @return The number of outgoing edges of vertex u. + * @return The partition id of u. */ template requires(_Choice_ref<_G&>._Strategy != _St_ref::_None) @@ -468,7 +468,7 @@ namespace _Partition_id { } else if constexpr (_Strat_ref == _St_ref::_Non_member) { return partition_id(__g, u); // intentional ADL } else if constexpr (_Strat_ref == _St_ref::_Auto_eval) { - return vertex_id_t<_G>(0); // default impl + return vertex_id_t<_G>{0}; // default impl } else { static_assert(_Always_false<_G>, "partition_id(g,u) is not defined and the default implementation cannot be evaluated"); @@ -480,12 +480,12 @@ namespace _Partition_id { * * Complexity: O(1) * - * Default implementation: vertex_id_t<_G>(0) + * Default implementation: vertex_id_t<_G>(0) if vertex_id_t<_G> is integral * * @tparam G The graph type. * @param g A graph instance. * @param uid Vertex id. - * @return A range of the outgoing partition_id. + * @return The partition id of uid. */ template requires(_Choice_id<_G&>._Strategy != _St_id::_None) @@ -496,7 +496,7 @@ namespace _Partition_id { if constexpr (_Strat_id == _St_id::_Non_member) { return partition_id(__g, uid); // intentional ADL } else if constexpr (_Strat_id == _St_id::_Auto_eval) { - return vertex_id_t<_G>(0); // default impl + return vertex_id_t<_G>{0}; // default impl } else { static_assert(_Always_false<_G>, "partition_id(g,uid) is not defined and the default implementation cannot be evaluated"); @@ -1958,41 +1958,88 @@ namespace edgelist { // partition_count(g) -> ? default = vertex_id_t(1) when vertex_id_t is integral, size_t(0) otherwise // -namespace tag_invoke { - TAG_INVOKE_DEF(partition_count); +namespace _Partition_count { +# if defined(__clang__) || defined(__EDG__) // TRANSITION, VSO-1681199 + void partition_count() = delete; // Block unqualified name lookup +# else // ^^^ no workaround / workaround vvv + void partition_count(); +# endif // ^^^ workaround ^^^ - template - concept _has_partition_count_adl = requires(G&& g) { - { partition_count(g) }; + template + concept _Has_ref_member = requires(_G&& __g, vertex_reference_t<_G> u) { + { _Fake_copy_init(__g.partition_count()) }; }; -} // namespace tag_invoke + template + concept _Has_ref_ADL = _Has_class_or_enum_type<_G> // + && requires(_G&& __g) { + { _Fake_copy_init(partition_count(__g)) }; // intentional ADL + }; + template + concept _Can_ref_eval = integral> // + && requires(_G&& __g) { + { _Fake_copy_init(vertex_id_t<_G>(1)) }; + }; -/** - * @brief Get's the number of partitions in a graph. - * - * Complexity: O(1) - * - * Default implementation: 0; graph container must override if it supports bi-partite - * or multipartite graphs. - * - * This is a customization point function that may be overriden if graph G supports bi-partite - * or multi-partite graphs. If it doesn't then a value of 0 is returned. - * - * @tparam G The graph type. - * @param g A graph instance. - * @return The number of partitions in a graph. 0 if G doesn't support partitioning. -*/ -template -requires tag_invoke::_has_partition_count_adl -auto partition_count(G&& g) { - if constexpr (tag_invoke::_has_partition_count_adl) - return tag_invoke::partition_count(g); - else if constexpr (is_integral_v>) - return vertex_id_t(1); - else - return size_t(1); + class _Cpo { + private: + enum class _St_ref { _None, _Member, _Non_member, _Auto_eval }; + + template + [[nodiscard]] static consteval _Choice_t<_St_ref> _Choose_ref() noexcept { + static_assert(is_lvalue_reference_v<_G>); + using _UnCV = remove_cvref_t<_G>; + + if constexpr (_Has_ref_member<_G, _UnCV>) { + return {_St_ref::_Member, noexcept(_Fake_copy_init(declval<_G>().partition_count()))}; + } else if constexpr (_Has_ref_ADL<_G, _UnCV>) { + return {_St_ref::_Non_member, noexcept(_Fake_copy_init(partition_count( + declval<_G>(), declval>())))}; // intentional ADL + } else if constexpr (_Can_ref_eval<_G, _UnCV>) { + return {_St_ref::_Auto_eval, noexcept(_Fake_copy_init(vertex_id_t<_G>(1)))}; + } else { + return {_St_ref::_None}; + } + } + + template + static constexpr _Choice_t<_St_ref> _Choice_ref = _Choose_ref<_G>(); + + public: + /** + * @brief The number of partitions in a graph. + * + * Complexity: O(1) + * + * Default implementation: vertex_id_t<_G>(0) + * + * @tparam G The graph type. + * @param g A graph instance. + * @return The number of partitions in the graph. + */ + template + requires(_Choice_ref<_G&>._Strategy != _St_ref::_None) + [[nodiscard]] constexpr auto operator()(_G&& __g) const noexcept(_Choice_ref<_G&>._No_throw) { + constexpr _St_ref _Strat_ref = _Choice_ref<_G&>._Strategy; + + if constexpr (_Strat_ref == _St_ref::_Member) { + return __g.partition_count(); + } else if constexpr (_Strat_ref == _St_ref::_Non_member) { + return partition_count(__g); // intentional ADL + } else if constexpr (_Strat_ref == _St_ref::_Auto_eval) { + return vertex_id_t<_G>(1); // default impl + } else { + static_assert(_Always_false<_G>, + "partition_count(g) is not defined and the default implementation cannot be evaluated"); + } + } + }; +} // namespace _Partition_count + +inline namespace _Cpos { + inline constexpr _Partition_count::_Cpo partition_count; } + // vertices(g,pid) -> range of vertices; graph container must override if it supports bi-partite or // multi-partite graph. // From 97d76474fca2f36df7a728c54b8f3a12e1c09d18 Mon Sep 17 00:00:00 2001 From: Phil Ratzloff Date: Wed, 15 Nov 2023 15:21:10 -0500 Subject: [PATCH 03/10] Replace old tag_invoke-based edges(g,u) and edges(g,uid) with new style CPO "returning address of local variable or temporary" warnings remain and need to be tracked down Leave EDGES_CPO #ifs until it's resolved --- example/CppCon2022/rr_adaptor.hpp | 26 ++- include/graph/container/compressed_graph.hpp | 46 +++- include/graph/container/dynamic_graph.hpp | 18 +- include/graph/container/utility_edgelist.hpp | 4 + include/graph/detail/graph_cpo.hpp | 222 ++++++++++++++++++- include/graph/graph.hpp | 2 +- 6 files changed, 299 insertions(+), 19 deletions(-) diff --git a/example/CppCon2022/rr_adaptor.hpp b/example/CppCon2022/rr_adaptor.hpp index a3d9ed0..cb2b884 100644 --- a/example/CppCon2022/rr_adaptor.hpp +++ b/example/CppCon2022/rr_adaptor.hpp @@ -6,6 +6,8 @@ // https://www.reddit.com/r/cpp/comments/4yp7fv/c17_structured_bindings_convert_struct_to_a_tuple/ // https://gist.github.com/utilForever/1a058050b8af3ef46b58bcfa01d5375d +#define EDGES_CPO 1 + template decltype(void(T{std::declval()...}), std::true_type{}) test_is_braces_constructible(int); @@ -135,6 +137,13 @@ class rr_adaptor { std::ranges::begin(g.vertices_)); // overriden to assure correct type returned } +#if EDGES_CPO + friend constexpr edges_range& edges(graph_type& g, vertex_type& u) { return u; } + friend constexpr const edges_range& edges(const graph_type& g, const vertex_type& u) { return u; } + + friend constexpr edges_range& edges(graph_type& g, const vertex_id_type uid) { return g.vertices_[uid]; } + friend constexpr const edges_range& edges(const graph_type& g, const vertex_id_type uid) { return g.vertices_[uid]; } +#else friend constexpr edges_range& tag_invoke(std::graph::tag_invoke::edges_fn_t, graph_type& g, vertex_type& u) { return u; } @@ -151,6 +160,7 @@ class rr_adaptor { tag_invoke(std::graph::tag_invoke::edges_fn_t, const graph_type& g, const vertex_id_type uid) { return g.vertices_[uid]; } +#endif friend constexpr vertex_id_type target_id(const graph_type& g, const edge_type& uv) noexcept { return get<0>(to_tuple(uv)); @@ -166,11 +176,11 @@ class rr_adaptor { } // edge_value(g,uv) - friend constexpr edge_value_type& edge_value(graph_type& g, edge_type& uv) { + friend constexpr edge_value_type edge_value(graph_type& g, edge_type& uv) { auto t = to_tuple(uv); return get<1>(t); } - friend constexpr const edge_value_type& edge_value(const graph_type& g, const edge_type& uv) { + friend constexpr const edge_value_type edge_value(const graph_type& g, const edge_type& uv) { auto t = to_tuple(uv); return get<1>(t); } @@ -366,6 +376,17 @@ class rr_adaptor2 { std::ranges::begin(g.vertices_)); // overriden to assure correct type returned } +#if EDGES_CPO + friend constexpr edges_range& edges(graph_type& g, vertex_type& u) { return get<0>(to_tuple(u)); } + friend constexpr const edges_range& edges(const graph_type& g, const vertex_type& u) { return get<0>(to_tuple(u)); } + + friend constexpr edges_range& edges(graph_type& g, const vertex_id_type uid) { + return get<0>(to_tuple(g.vertices_[uid])); + } + friend constexpr const edges_range& edges(const graph_type& g, const vertex_id_type uid) { + return get<0>(to_tuple(g.vertices_[uid])); + } +#else friend constexpr edges_range& tag_invoke(std::graph::tag_invoke::edges_fn_t, graph_type& g, vertex_type& u) { return get<0>(to_tuple(u)); } @@ -382,6 +403,7 @@ class rr_adaptor2 { tag_invoke(std::graph::tag_invoke::edges_fn_t, const graph_type& g, const vertex_id_type uid) { return get<0>(to_tuple(g.vertices_[uid])); } +#endif friend constexpr vertex_id_type target_id(const graph_type& g, const edge_type& uv) noexcept { return get<0>(to_tuple(uv)); diff --git a/include/graph/container/compressed_graph.hpp b/include/graph/container/compressed_graph.hpp index c3e89a2..67a7b6b 100644 --- a/include/graph/container/compressed_graph.hpp +++ b/include/graph/container/compressed_graph.hpp @@ -230,7 +230,7 @@ class csr_row_values { using value_type = void; using size_type = size_t; //VId; -public: // Properties +public: // Properties [[nodiscard]] constexpr size_type size() const noexcept { return 0; } [[nodiscard]] constexpr bool empty() const noexcept { return true; } [[nodiscard]] constexpr size_type capacity() const noexcept { return 0; } @@ -342,7 +342,7 @@ class csr_col_values { using value_type = void; using size_type = size_t; //VId; -public: // Properties +public: // Properties [[nodiscard]] constexpr size_type size() const noexcept { return 0; } [[nodiscard]] constexpr bool empty() const noexcept { return true; } [[nodiscard]] constexpr size_type capacity() const noexcept { return 0; } @@ -481,7 +481,7 @@ class compressed_graph_base } public: -public: // Operations +public: // Operations void reserve_vertices(size_type count) { row_index_.reserve(count + 1); // +1 for terminating row row_values_base::reserve(count); @@ -741,14 +741,14 @@ class compressed_graph_base private: // tag_invoke properties friend constexpr vertices_type tag_invoke(::std::graph::tag_invoke::vertices_fn_t, compressed_graph_base& g) { if (g.row_index_.empty()) - return vertices_type(g.row_index_); // really empty + return vertices_type(g.row_index_); // really empty else return vertices_type(g.row_index_.begin(), g.row_index_.end() - 1); // don't include terminating row } friend constexpr const_vertices_type tag_invoke(::std::graph::tag_invoke::vertices_fn_t, const compressed_graph_base& g) { if (g.row_index_.empty()) - return const_vertices_type(g.row_index_); // really empty + return const_vertices_type(g.row_index_); // really empty else return const_vertices_type(g.row_index_.begin(), g.row_index_.end() - 1); // don't include terminating row } @@ -757,10 +757,41 @@ class compressed_graph_base return static_cast(ui - g.row_index_.begin()); } +#if EDGES_CPO + friend constexpr edges_type edges(graph_type& g, vertex_type& u) { + static_assert(ranges::contiguous_range, "row_index_ must be a contiguous range to get next row"); + vertex_type* u2 = &u + 1; + assert(static_cast(u2 - &u) < g.row_index_.size()); // in row_index_ bounds? + assert(static_cast(u.index) <= g.col_index_.size() && + static_cast(u2->index) <= g.col_index_.size()); // in col_index_ bounds? + return edges_type(g.col_index_.begin() + u.index, g.col_index_.begin() + u2->index); + } + friend constexpr const_edges_type edges(const graph_type& g, const vertex_type& u) { + static_assert(ranges::contiguous_range, "row_index_ must be a contiguous range to get next row"); + const vertex_type* u2 = &u + 1; + assert(static_cast(u2 - &u) < g.row_index_.size()); // in row_index_ bounds? + assert(static_cast(u.index) <= g.col_index_.size() && + static_cast(u2->index) <= g.col_index_.size()); // in col_index_ bounds? + return const_edges_type(g.col_index_.begin() + u.index, g.col_index_.begin() + u2->index); + } + + friend constexpr edges_type edges(graph_type& g, const vertex_id_type uid) { + assert(static_cast(uid + 1) < g.row_index_.size()); // in row_index_ bounds? + assert(static_cast(g.row_index_[uid + 1].index) <= g.col_index_.size()); // in col_index_ bounds? + return edges_type(g.col_index_.begin() + g.row_index_[uid].index, + g.col_index_.begin() + g.row_index_[uid + 1].index); + } + friend constexpr const_edges_type edges(const graph_type& g, const vertex_id_type uid) { + assert(static_cast(uid + 1) < g.row_index_.size()); // in row_index_ bounds? + assert(static_cast(g.row_index_[uid + 1].index) <= g.col_index_.size()); // in col_index_ bounds? + return const_edges_type(g.col_index_.begin() + g.row_index_[uid].index, + g.col_index_.begin() + g.row_index_[uid + 1].index); + } +#else friend constexpr edges_type tag_invoke(::std::graph::tag_invoke::edges_fn_t, graph_type& g, vertex_type& u) { static_assert(ranges::contiguous_range, "row_index_ must be a contiguous range to get next row"); vertex_type* u2 = &u + 1; - assert(static_cast(u2 - &u) < g.row_index_.size()); // in row_index_ bounds? + assert(static_cast(u2 - &u) < g.row_index_.size()); // in row_index_ bounds? assert(static_cast(u.index) <= g.col_index_.size() && static_cast(u2->index) <= g.col_index_.size()); // in col_index_ bounds? return edges_type(g.col_index_.begin() + u.index, g.col_index_.begin() + u2->index); @@ -769,7 +800,7 @@ class compressed_graph_base tag_invoke(::std::graph::tag_invoke::edges_fn_t, const graph_type& g, const vertex_type& u) { static_assert(ranges::contiguous_range, "row_index_ must be a contiguous range to get next row"); const vertex_type* u2 = &u + 1; - assert(static_cast(u2 - &u) < g.row_index_.size()); // in row_index_ bounds? + assert(static_cast(u2 - &u) < g.row_index_.size()); // in row_index_ bounds? assert(static_cast(u.index) <= g.col_index_.size() && static_cast(u2->index) <= g.col_index_.size()); // in col_index_ bounds? return const_edges_type(g.col_index_.begin() + u.index, g.col_index_.begin() + u2->index); @@ -789,6 +820,7 @@ class compressed_graph_base return const_edges_type(g.col_index_.begin() + g.row_index_[uid].index, g.col_index_.begin() + g.row_index_[uid + 1].index); } +#endif // target_id(g,uv), target(g,uv) diff --git a/include/graph/container/dynamic_graph.hpp b/include/graph/container/dynamic_graph.hpp index f4890ef..6a74073 100644 --- a/include/graph/container/dynamic_graph.hpp +++ b/include/graph/container/dynamic_graph.hpp @@ -679,6 +679,10 @@ class dynamic_vertex_base { edges_type edges_; private: // tag_invoke properties +#if EDGES_CPO + friend constexpr edges_type& edges(graph_type& g, vertex_type& u) { return u.edges_; } + friend constexpr const edges_type& edges(const graph_type& g, const vertex_type& u) { return u.edges_; } +#else friend constexpr edges_type& tag_invoke(::std::graph::tag_invoke::edges_fn_t, graph_type& g, vertex_type& u) { return u.edges_; } @@ -686,6 +690,7 @@ class dynamic_vertex_base { tag_invoke(::std::graph::tag_invoke::edges_fn_t, const graph_type& g, const vertex_type& u) { return u.edges_; } +#endif friend constexpr typename edges_type::iterator find_vertex_edge(graph_type& g, vertex_id_type uid, vertex_id_type vid) { @@ -1212,7 +1217,7 @@ class dynamic_graph_base { constexpr typename vertices_type::value_type& operator[](size_type i) noexcept { return vertices_[i]; } constexpr const typename vertices_type::value_type& operator[](size_type i) const noexcept { return vertices_[i]; } -public: // Operations +public: // Operations void reserve_vertices(size_type count) { if constexpr (reservable) // reserve if we can; otherwise ignored vertices_.reserve(count); @@ -1245,6 +1250,14 @@ class dynamic_graph_base { return static_cast(ui - g.vertices_.begin()); } +#if EDGES_CPO + friend constexpr edges_type& edges(graph_type& g, const vertex_id_type uid) { // + return g.vertices_[uid].edges(); + } + friend constexpr const edges_type& edges(const graph_type& g, const vertex_id_type uid) { + return g.vertices_[uid].edges(); + } +#else friend constexpr edges_type& tag_invoke(::std::graph::tag_invoke::edges_fn_t, graph_type& g, const vertex_id_type uid) { return g.vertices_[uid].edges(); @@ -1253,6 +1266,7 @@ class dynamic_graph_base { tag_invoke(::std::graph::tag_invoke::edges_fn_t, const graph_type& g, const vertex_id_type uid) { return g.vertices_[uid].edges(); } +#endif }; /** @@ -1612,7 +1626,7 @@ class dynamic_graph : public dynamic_graph_base(e); } diff --git a/include/graph/detail/graph_cpo.hpp b/include/graph/detail/graph_cpo.hpp index f069a62..5e053c9 100644 --- a/include/graph/detail/graph_cpo.hpp +++ b/include/graph/detail/graph_cpo.hpp @@ -3,8 +3,10 @@ // (included from graph.hpp) #include "tag_invoke.hpp" -#ifndef GRAPH_INVOKE_HPP -# define GRAPH_INVOKE_HPP +#ifndef GRAPH_CPO_HPP +# define GRAPH_CPO_HPP + +# define EDGES_CPO 1 namespace std::graph { @@ -509,7 +511,6 @@ inline namespace _Cpos { inline constexpr _Partition_id::_Cpo partition_id; } - template using partition_id_t = decltype(partition_id(declval(), declval>())); @@ -524,6 +525,147 @@ using partition_id_t = decltype(partition_id(declval(), declval> // edge_reference_t = ranges::range_reference_t> // +# if EDGES_CPO +namespace _Edges { +# if defined(__clang__) || defined(__EDG__) // TRANSITION, VSO-1681199 + void edges() = delete; // Block unqualified name lookup +# else // ^^^ no workaround / workaround vvv + void edges(); +# endif // ^^^ workaround ^^^ + + template + concept _Has_ref_member = requires(_G&& __g, vertex_reference_t<_G> u) { + { _Fake_copy_init(u.edges(__g)) }; + }; + template + concept _Has_ref_ADL = _Has_class_or_enum_type<_G> // + && requires(_G&& __g, const vertex_reference_t<_G>& u) { + { _Fake_copy_init(edges(__g, u)) }; // intentional ADL + }; + template + concept _Can_ref_eval = _Has_class_or_enum_type<_G> && ranges::forward_range>; + + template + concept _Has_id_ADL = _Has_class_or_enum_type<_G> // + && requires(_G&& __g, const vertex_id_t<_G>& uid) { + { _Fake_copy_init(edges(__g, uid)) }; // intentional ADL + }; + template + concept _Can_id_eval = _Has_class_or_enum_type<_G> && ranges::forward_range> // + && requires(_G&& __g, vertex_id_t<_G> uid) { + { _Fake_copy_init(find_vertex(__g, uid)) }; + }; + + class _Cpo { + private: + enum class _St_id { _None, _Non_member, _Auto_eval }; + enum class _St_ref { _None, _Member, _Non_member, _Auto_eval }; + + template + [[nodiscard]] static consteval _Choice_t<_St_id> _Choose_id() noexcept { + static_assert(is_lvalue_reference_v<_G>); + using _UnCV = remove_cvref_t<_G>; + + if constexpr (_Has_id_ADL<_G, _UnCV>) { + return {_St_id::_Non_member, + noexcept(_Fake_copy_init(edges(declval<_G>(), declval>())))}; // intentional ADL + } else if constexpr (_Can_id_eval<_G, _UnCV>) { + return {_St_id::_Auto_eval, + noexcept(_Fake_copy_init(*find_vertex(declval<_G>(), declval>())))}; // default impl + } else { + return {_St_id::_None}; + } + } + + template + static constexpr _Choice_t<_St_id> _Choice_id = _Choose_id<_G>(); + + template + [[nodiscard]] static consteval _Choice_t<_St_ref> _Choose_ref() noexcept { + static_assert(is_lvalue_reference_v<_G>); + using _UnCV = remove_cvref_t<_G>; + + if constexpr (_Has_ref_member<_G, _UnCV>) { + return {_St_ref::_Member, noexcept(_Fake_copy_init(declval>().edges(declval<_G>())))}; + } else if constexpr (_Has_ref_ADL<_G, _UnCV>) { + return {_St_ref::_Non_member, + noexcept(_Fake_copy_init(edges(declval<_G>(), declval>())))}; // intentional ADL + } else if constexpr (_Can_ref_eval<_G, _UnCV>) { + return {_St_ref::_Auto_eval, + noexcept(_Fake_copy_init(*find_vertex(declval<_G>(), declval>())))}; + } else { + return {_St_ref::_None}; + } + } + + template + static constexpr _Choice_t<_St_ref> _Choice_ref = _Choose_ref<_G>(); + + public: + /** + * @brief The number of outgoing edges of a vertex. + * + * Complexity: O(1) + * + * Default implementation: size(edges(g, u)) + * + * @tparam G The graph type. + * @param g A graph instance. + * @param u A vertex instance. + * @return The number of outgoing edges of vertex u. + */ + template + requires(_Choice_ref<_G&>._Strategy != _St_ref::_None) + [[nodiscard]] constexpr auto&& operator()(_G&& __g, vertex_reference_t<_G> u) const + noexcept(_Choice_ref<_G&>._No_throw) { + constexpr _St_ref _Strat_ref = _Choice_ref<_G&>._Strategy; + + if constexpr (_Strat_ref == _St_ref::_Member) { + return u.edges(__g); + } else if constexpr (_Strat_ref == _St_ref::_Non_member) { + return edges(__g, u); // intentional ADL + } else if constexpr (_Strat_ref == _St_ref::_Auto_eval) { + return u; // default impl + } else { + static_assert(_Always_false<_G>, + "edges(g,u) is not defined and the default implementation cannot be evaluated"); + } + } + + /** + * @brief Get the outgoing edges of a vertex id. + * + * Complexity: O(1) + * + * Default implementation: edges(g, *find_vertex(g, uid)) + * + * @tparam G The graph type. + * @param g A graph instance. + * @param uid Vertex id. + * @return A range of the outgoing edges. + */ + template + requires(_Choice_id<_G&>._Strategy != _St_id::_None) + [[nodiscard]] constexpr auto&& operator()(_G&& __g, const vertex_id_t<_G>& uid) const + noexcept(_Choice_id<_G&>._No_throw) { + constexpr _St_id _Strat_id = _Choice_id<_G&>._Strategy; + + if constexpr (_Strat_id == _St_id::_Non_member) { + return edges(__g, uid); // intentional ADL + } else if constexpr (_Strat_id == _St_id::_Auto_eval) { + return *find_vertex(__g, uid); // default impl + } else { + static_assert(_Always_false<_G>, + "edges(g,uid) is not defined and the default implementation cannot be evaluated"); + } + } + }; +} // namespace _Edges + +inline namespace _Cpos { + inline constexpr _Edges::_Cpo edges; +} +# else namespace tag_invoke { TAG_INVOKE_DEF(edges); @@ -575,6 +717,7 @@ auto edges(G&& g, vertex_id_t uid) -> decltype(tag_invoke::edges(g, uid)) { else return edges(g, *find_vertex(g, uid)); } +# endif /** * @brief The outgoing edge range type of a vertex for graph G. @@ -1471,7 +1614,7 @@ namespace _Degree { }; template concept _Can_id_eval = ranges::sized_range> // - && requires(_G&& __g, vertex_id_t<_G> uid) { + && requires(_G&& __g, vertex_id_t<_G>& uid) { { _Fake_copy_init(edges(__g, uid)) }; }; @@ -1831,6 +1974,69 @@ inline namespace _Cpos { namespace edgelist { +# if EDGES_CPO + namespace _Edges { +# if defined(__clang__) || defined(__EDG__) // TRANSITION, VSO-1681199 + void edges() = delete; // Block unqualified name lookup +# else // ^^^ no workaround / workaround vvv + void edges(); +# endif // ^^^ workaround ^^^ + + template + concept _Has_ref_ADL = _Has_class_or_enum_type // + && ranges::forward_range && requires(EL&& el) { + { _Fake_copy_init(edges(el)) }; // intentional ADL + }; + + class _Cpo { + private: + enum class _St_ref { _None, _Non_member }; + + template + [[nodiscard]] static consteval _Choice_t<_St_ref> _Choose_ref() noexcept { + using _UnCV = remove_cvref_t; + + if constexpr (_Has_ref_ADL) { + return {_St_ref::_Non_member, noexcept(_Fake_copy_init(edges(declval())))}; // intentional ADL + } else { + return {_St_ref::_None}; + } + } + + template + static constexpr _Choice_t<_St_ref> _Choice_ref = _Choose_ref(); + + public: + /** + * @brief The number of outgoing edges of a vertex. + * + * Complexity: O(1) + * + * Default implementation: none + * + * @tparam G The graph type. + * @param EL An edgelist instance. + * @return The edgelist passed. + */ + template + requires(_Choice_ref._Strategy != _St_ref::_None) + [[nodiscard]] constexpr auto&& operator()(EL&& el) const noexcept(_Choice_ref._No_throw) { + constexpr _St_ref _Strat_ref = _Choice_ref._Strategy; + + if constexpr (_Strat_ref == _St_ref::_Non_member) { + return el; // intentional ADL + } else { + static_assert(_Always_false, + "edges(el) is not defined and the default implementation cannot be evaluated"); + } + } + }; + } // namespace _Edges + + inline namespace _Cpos { + inline constexpr _Edges::_Cpo edges; + } +# else namespace tag_invoke { TAG_INVOKE_DEF(edges); // edges(e) -> [edge list vertices] } @@ -1839,6 +2045,7 @@ namespace edgelist { auto edges(EL&& el) { return el; } +# endif template using edgelist_range_t = decltype(edges(declval())); @@ -2342,8 +2549,9 @@ using partition_edge_range_t = vertex_edge_range_t; // edge_t = ranges::range_value_t> // edge_reference_t = ranges::range_reference_t> // +# if 0 namespace tag_invoke { - //TAG_INVOKE_DEF(edges); + TAG_INVOKE_DEF(edges); template concept _has_edges_vtxref_part_adl = requires(G&& g, vertex_reference_t u, partition_id_t p) { @@ -2397,7 +2605,7 @@ auto edges(G&& g, vertex_id_t uid, partition_id_t p) -> decltype(tag_invok else return edges(g, *find_vertex(g, uid), p); } - +# endif // // partition_target_id(g,puid) -> partition_vertex_id_t<_G> @@ -2573,4 +2781,4 @@ inline namespace _Cpos { } // namespace std::graph -#endif //GRAPH_INVOKE_HPP +#endif //GRAPH_CPO_HPP diff --git a/include/graph/graph.hpp b/include/graph/graph.hpp index e871ffe..4522c53 100644 --- a/include/graph/graph.hpp +++ b/include/graph/graph.hpp @@ -170,7 +170,7 @@ inline constexpr bool is_sourced_edge_v = is_sourced_edge::value; * @tparam G The graph type. */ template -concept index_adjacency_list = vertex_range && targeted_edge> && requires(G&& g, vertex_id_t uid) { +concept index_adjacency_list = vertex_range && targeted_edge> && requires(G&& g, vertex_id_t& uid) { { edges(g, uid) } -> ranges::forward_range; }; From d02f0b062ee68a90239922aa248f32695f86e565 Mon Sep 17 00:00:00 2001 From: Phil Ratzloff Date: Wed, 15 Nov 2023 16:36:27 -0500 Subject: [PATCH 04/10] Replace old tag_invoke-based vertices(g) with new style CPO Replace return type of auto&& with decltype(auto) to allow both reference and value type returns. Warning still remains when when edges CPO enabled --- example/CppCon2022/rr_adaptor.hpp | 46 ++++++++- include/graph/container/compressed_graph.hpp | 15 +++ include/graph/container/dynamic_graph.hpp | 5 + include/graph/detail/graph_cpo.hpp | 102 ++++++++++++++++--- tests/incidence_tests.cpp | 2 +- tests/vertexlist_tests.cpp | 2 +- 6 files changed, 156 insertions(+), 16 deletions(-) diff --git a/example/CppCon2022/rr_adaptor.hpp b/example/CppCon2022/rr_adaptor.hpp index cb2b884..44482e1 100644 --- a/example/CppCon2022/rr_adaptor.hpp +++ b/example/CppCon2022/rr_adaptor.hpp @@ -41,10 +41,42 @@ auto to_tuple(T&& object) noexcept { return std::make_tuple(); } } - template using to_tuple_t = decltype(to_tuple(std::declval())); + +template +constexpr auto& to_tuple_value(T&& object, const int param) noexcept { + using type = std::decay_t; + if constexpr (is_braces_constructible{}) { + auto&& [p1, p2, p3, p4] = object; + switch (param) { + case 0: return p1; + case 1: return p2; + case 2: return p3; + case 3: return p4; + } + } else if constexpr (is_braces_constructible{}) { + auto&& [p1, p2, p3] = object; + switch (param) { + case 0: return p1; + case 1: return p2; + case 2: return p3; + } + } else if constexpr (is_braces_constructible{}) { + auto&& [p1, p2] = object; + switch (param) { + case 0: return p1; + case 1: return p2; + } + } else if constexpr (is_braces_constructible{}) { + auto&& [p1] = object; + return p1; + } else { + return std::make_tuple(); + } +} + //template //concept has_push_back = requires(C& container, std::ranges::range_reference_t val) { // { container.push_back(val) }; @@ -125,12 +157,17 @@ class rr_adaptor { } private: // tag_invoke definitions +#if VERTICES_CPO + friend constexpr vertices_range& vertices(graph_type& g) { return g.vertices_; } + friend constexpr const vertices_range& vertices(const graph_type& g) { return g.vertices_; } +#else friend constexpr vertices_range& tag_invoke(std::graph::tag_invoke::vertices_fn_t, graph_type& g) { return g.vertices_; } friend constexpr const vertices_range& tag_invoke(std::graph::tag_invoke::vertices_fn_t, const graph_type& g) { return g.vertices_; } +#endif friend vertex_id_type vertex_id(const graph_type& g, std::ranges::iterator_t ui) { return static_cast(ui - @@ -140,7 +177,7 @@ class rr_adaptor { #if EDGES_CPO friend constexpr edges_range& edges(graph_type& g, vertex_type& u) { return u; } friend constexpr const edges_range& edges(const graph_type& g, const vertex_type& u) { return u; } - + friend constexpr edges_range& edges(graph_type& g, const vertex_id_type uid) { return g.vertices_[uid]; } friend constexpr const edges_range& edges(const graph_type& g, const vertex_id_type uid) { return g.vertices_[uid]; } #else @@ -364,12 +401,17 @@ class rr_adaptor2 { } private: +#if VERTICES_CPO + friend constexpr vertices_range& vertices(graph_type& g) { return g.vertices_; } + friend constexpr const vertices_range& vertices(const graph_type& g) { return g.vertices_; } +#else friend constexpr vertices_range& tag_invoke(std::graph::tag_invoke::vertices_fn_t, graph_type& g) { return g.vertices_; } friend constexpr const vertices_range& tag_invoke(std::graph::tag_invoke::vertices_fn_t, const graph_type& g) { return g.vertices_; } +#endif friend vertex_id_type vertex_id(const graph_type& g, std::ranges::iterator_t ui) { return static_cast(ui - diff --git a/include/graph/container/compressed_graph.hpp b/include/graph/container/compressed_graph.hpp index 67a7b6b..1ba0480 100644 --- a/include/graph/container/compressed_graph.hpp +++ b/include/graph/container/compressed_graph.hpp @@ -739,6 +739,20 @@ class compressed_graph_base //row_values_type row_value_; // row_value_[r] holds the value for row_index_[r], for VV!=void private: // tag_invoke properties +#if VERTICES_CPO + friend constexpr vertices_type vertices(compressed_graph_base& g) { + if (g.row_index_.empty()) + return vertices_type(g.row_index_); // really empty + else + return vertices_type(g.row_index_.begin(), g.row_index_.end() - 1); // don't include terminating row + } + friend constexpr const_vertices_type vertices(const compressed_graph_base& g) { + if (g.row_index_.empty()) + return const_vertices_type(g.row_index_); // really empty + else + return const_vertices_type(g.row_index_.begin(), g.row_index_.end() - 1); // don't include terminating row + } +#else friend constexpr vertices_type tag_invoke(::std::graph::tag_invoke::vertices_fn_t, compressed_graph_base& g) { if (g.row_index_.empty()) return vertices_type(g.row_index_); // really empty @@ -752,6 +766,7 @@ class compressed_graph_base else return const_vertices_type(g.row_index_.begin(), g.row_index_.end() - 1); // don't include terminating row } +#endif friend vertex_id_type vertex_id(const compressed_graph_base& g, const_iterator ui) { return static_cast(ui - g.row_index_.begin()); diff --git a/include/graph/container/dynamic_graph.hpp b/include/graph/container/dynamic_graph.hpp index 6a74073..1a37193 100644 --- a/include/graph/container/dynamic_graph.hpp +++ b/include/graph/container/dynamic_graph.hpp @@ -1238,6 +1238,10 @@ class dynamic_graph_base { vertices_type vertices_; private: // tag_invoke properties +#if VERTICES_CPO + friend constexpr vertices_type& vertices(dynamic_graph_base& g) { return g.vertices_; } + friend constexpr const vertices_type& vertices(const dynamic_graph_base& g) { return g.vertices_; } +#else friend constexpr vertices_type& tag_invoke(::std::graph::tag_invoke::vertices_fn_t, dynamic_graph_base& g) { return g.vertices_; } @@ -1245,6 +1249,7 @@ class dynamic_graph_base { const dynamic_graph_base& g) { return g.vertices_; } +#endif friend vertex_id_type vertex_id(const dynamic_graph_base& g, typename vertices_type::const_iterator ui) { return static_cast(ui - g.vertices_.begin()); diff --git a/include/graph/detail/graph_cpo.hpp b/include/graph/detail/graph_cpo.hpp index 5e053c9..739026b 100644 --- a/include/graph/detail/graph_cpo.hpp +++ b/include/graph/detail/graph_cpo.hpp @@ -6,7 +6,8 @@ #ifndef GRAPH_CPO_HPP # define GRAPH_CPO_HPP -# define EDGES_CPO 1 +# define VERTICES_CPO 1 // warnings need to be tracked down +# define EDGES_CPO 1 // warnings need to be tracked down namespace std::graph { @@ -117,6 +118,81 @@ concept adjacency_matrix = is_adjacency_matrix_v; // vertex_t = ranges::range_value_t> // vertex_reference_t = ranges::range_reference_t> // +# if VERTICES_CPO +namespace _Vertices { +# if defined(__clang__) || defined(__EDG__) // TRANSITION, VSO-1681199 + void vertices() = delete; // Block unqualified name lookup +# else // ^^^ no workaround / workaround vvv + void vertices(); +# endif // ^^^ workaround ^^^ + + template + concept _Has_ref_member = _Has_class_or_enum_type<_G> && // + requires(_G&& __g) { + { _Fake_copy_init(__g.vertices()) }; + }; + template + concept _Has_ref_ADL = _Has_class_or_enum_type<_G> // + && requires(_G&& __g) { + { _Fake_copy_init(vertices(__g)) }; // intentional ADL + }; + + class _Cpo { + private: + enum class _St_ref { _None, _Member, _Non_member }; + + template + [[nodiscard]] static consteval _Choice_t<_St_ref> _Choose_ref() noexcept { + static_assert(is_lvalue_reference_v<_G>); + using _UnCV = remove_cvref_t<_G>; + + if constexpr (_Has_ref_member<_G, _UnCV>) { + return {_St_ref::_Member, noexcept(_Fake_copy_init(declval<_G>().vertices()))}; + } else if constexpr (_Has_ref_ADL<_G, _UnCV>) { + return {_St_ref::_Non_member, noexcept(_Fake_copy_init(vertices(declval<_G>())))}; // intentional ADL + } else { + return {_St_ref::_None}; + } + } + + template + static constexpr _Choice_t<_St_ref> _Choice_ref = _Choose_ref<_G>(); + + public: + /** + * @brief Returns the vertices range for a graph G. + * + * Default implementation: n/a. + * + * Complexity: O(1) + * + * This is a customization point function that is required to be overridden for each + * graph type. + * + * @tparam G The graph type + * @param g A graph instance + */ + template + requires(_Choice_ref<_G&>._Strategy != _St_ref::_None) + [[nodiscard]] constexpr auto operator()(_G&& __g) const noexcept(_Choice_ref<_G&>._No_throw) -> decltype(auto) { + constexpr _St_ref _Strat_ref = _Choice_ref<_G&>._Strategy; + + if constexpr (_Strat_ref == _St_ref::_Member) { + return __g.vertices(); + } else if constexpr (_Strat_ref == _St_ref::_Non_member) { + //static_assert(is_reference_v); + return vertices(__g); // intentional ADL + } else { + static_assert(_Always_false<_G>, "vertices(g) is not defined"); + } + } + }; +} // namespace _Vertices + +inline namespace _Cpos { + inline constexpr _Vertices::_Cpo vertices; +} +# else namespace tag_invoke { TAG_INVOKE_DEF(vertices); // vertices(g) -> [graph vertices] } @@ -138,6 +214,7 @@ template auto vertices(G&& g) -> decltype(tag_invoke::vertices(g)) { return tag_invoke::vertices(g); } +# endif /** * @brief The vertex range type for a graph G. @@ -151,7 +228,7 @@ using vertex_range_t = decltype(std::graph::vertices(declval())); * @tparam G The graph type. */ template -using vertex_iterator_t = ranges::iterator_t>; +using vertex_iterator_t = ranges::iterator_t>; /** * @brief The vertex type for a graph G. @@ -616,8 +693,8 @@ namespace _Edges { */ template requires(_Choice_ref<_G&>._Strategy != _St_ref::_None) - [[nodiscard]] constexpr auto&& operator()(_G&& __g, vertex_reference_t<_G> u) const - noexcept(_Choice_ref<_G&>._No_throw) { + [[nodiscard]] constexpr auto operator()(_G&& __g, vertex_reference_t<_G> u) const + noexcept(_Choice_ref<_G&>._No_throw) -> decltype(auto) { constexpr _St_ref _Strat_ref = _Choice_ref<_G&>._Strategy; if constexpr (_Strat_ref == _St_ref::_Member) { @@ -646,8 +723,8 @@ namespace _Edges { */ template requires(_Choice_id<_G&>._Strategy != _St_id::_None) - [[nodiscard]] constexpr auto&& operator()(_G&& __g, const vertex_id_t<_G>& uid) const - noexcept(_Choice_id<_G&>._No_throw) { + [[nodiscard]] constexpr auto operator()(_G&& __g, const vertex_id_t<_G>& uid) const + noexcept(_Choice_id<_G&>._No_throw) -> decltype(auto) { constexpr _St_id _Strat_id = _Choice_id<_G&>._Strategy; if constexpr (_Strat_id == _St_id::_Non_member) { @@ -1789,8 +1866,8 @@ namespace _Vertex_value { */ template requires(_Choice_ref<_G&>._Strategy != _St_ref::_None) - [[nodiscard]] constexpr auto&& operator()(_G&& __g, vertex_reference_t<_G> u) const - noexcept(_Choice_ref<_G&>._No_throw) { + [[nodiscard]] constexpr auto operator()(_G&& __g, vertex_reference_t<_G> u) const + noexcept(_Choice_ref<_G&>._No_throw) -> decltype(auto) { constexpr _St_ref _Strat_ref = _Choice_ref<_G&>._Strategy; if constexpr (_Strat_ref == _St_ref::_Member) { @@ -1872,8 +1949,8 @@ namespace _Edge_value { */ template requires(_Choice_ref<_G&>._Strategy != _St_ref::_None) - [[nodiscard]] constexpr auto&& operator()(_G&& __g, edge_reference_t<_G> uv) const - noexcept(_Choice_ref<_G&>._No_throw) { + [[nodiscard]] constexpr auto operator()(_G&& __g, edge_reference_t<_G> uv) const + noexcept(_Choice_ref<_G&>._No_throw) -> decltype(auto) { constexpr _St_ref _Strat_ref = _Choice_ref<_G&>._Strategy; if constexpr (_Strat_ref == _St_ref::_Member) { @@ -1954,7 +2031,7 @@ namespace _Graph_value { */ template requires(_Choice_ref<_G&>._Strategy != _St_ref::_None) - [[nodiscard]] constexpr auto&& operator()(_G&& __g) const noexcept(_Choice_ref<_G&>._No_throw) { + [[nodiscard]] constexpr auto operator()(_G&& __g) const noexcept(_Choice_ref<_G&>._No_throw) -> decltype(auto) { constexpr _St_ref _Strat_ref = _Choice_ref<_G&>._Strategy; if constexpr (_Strat_ref == _St_ref::_Member) { @@ -2275,6 +2352,7 @@ namespace tag_invoke { * @param g A graph instance. * @return The number of partitions in a graph. 0 if G doesn't support partitioning. */ +# if 0 template requires tag_invoke::_has_vertices_pid_adl auto vertices(G&& g, partition_id_t pid) { @@ -2286,7 +2364,7 @@ auto vertices(G&& g, partition_id_t pid) { template using partition_vertex_range_t = decltype(vertices(declval(), declval>())); - +# endif template struct _partition_vertex_id { diff --git a/tests/incidence_tests.cpp b/tests/incidence_tests.cpp index 24b1784..17699bc 100644 --- a/tests/incidence_tests.cpp +++ b/tests/incidence_tests.cpp @@ -55,7 +55,7 @@ TEST_CASE("incidence test", "[csr][incidence]") { init_console(); using G = routes_compressed_graph_type; - auto&& g = load_ordered_graph(TEST_DATA_ROOT_DIR "germany_routes.csv", name_order_policy::source_order_found); + auto g = load_ordered_graph(TEST_DATA_ROOT_DIR "germany_routes.csv", name_order_policy::source_order_found); // name_order_policy::source_order_found gives best output with least overlap for germany routes const auto frankfurt = find_frankfurt(g); diff --git a/tests/vertexlist_tests.cpp b/tests/vertexlist_tests.cpp index 5108b3c..bc54667 100644 --- a/tests/vertexlist_tests.cpp +++ b/tests/vertexlist_tests.cpp @@ -54,7 +54,7 @@ TEST_CASE("vertexlist test", "[csr][vertexlist]") { init_console(); using G = routes_compressed_graph_type; - auto&& g = load_ordered_graph(TEST_DATA_ROOT_DIR "germany_routes.csv", name_order_policy::source_order_found); + auto g = load_ordered_graph(TEST_DATA_ROOT_DIR "germany_routes.csv", name_order_policy::source_order_found); // name_order_policy::source_order_found gives best output with least overlap for germany routes const auto frankfurt = find_frankfurt(g); From 092dae8076d5b8631877a1f69187b96118ff914d Mon Sep 17 00:00:00 2001 From: Phil Ratzloff Date: Wed, 15 Nov 2023 17:20:41 -0500 Subject: [PATCH 05/10] Fix type punning warning for vertexlist Use union for value & shadow_value instead of reinterpret_cast(value_) --- include/graph/views/vertexlist.hpp | 54 ++++++++++++++++++++---------- 1 file changed, 36 insertions(+), 18 deletions(-) diff --git a/include/graph/views/vertexlist.hpp b/include/graph/views/vertexlist.hpp index 15ee1df..1687069 100644 --- a/include/graph/views/vertexlist.hpp +++ b/include/graph/views/vertexlist.hpp @@ -59,11 +59,19 @@ class vertexlist_iterator { using shadow_value_type = vertex_descriptor, shadow_vertex_type*, _detail::ref_to_ptr>; + union internal_value { + value_type value_; + shadow_value_type shadow_; + + internal_value(vertex_id_type start_at) : shadow_{} { shadow_.id = start_at; } + internal_value(const internal_value& rhs) : shadow_(rhs.shadow_) {} + internal_value() : shadow_{} {} + internal_value& operator=(const internal_value& rhs) { value_.shadow = rhs.value_.shadow; } + }; + public: vertexlist_iterator(graph_type& g, const VVF& value_fn, vertex_iterator_type iter, vertex_id_type start_at = 0) - : iter_(iter), value_fn_(&value_fn) { - value_.id = start_at; - } + : value_{start_at}, iter_(iter), value_fn_(&value_fn) {} constexpr vertexlist_iterator() = default; constexpr vertexlist_iterator(const vertexlist_iterator&) = default; @@ -75,15 +83,15 @@ class vertexlist_iterator { public: constexpr reference operator*() const { - value_.vertex = &*iter_; + value_.shadow_.vertex = &*iter_; if constexpr (!is_void_v) - value_.value = invoke(*this->value_fn_, *iter_); - return reinterpret_cast(value_); + value_.shadow_.value = invoke(*this->value_fn_, *iter_); + return value_.value_; } constexpr vertexlist_iterator& operator++() { ++iter_; - ++value_.id; + ++value_.shadow_.id; // leave value_.vertex as-is to avoid dereferencing iter_ when it's at end() return *this; } @@ -96,9 +104,10 @@ class vertexlist_iterator { constexpr bool operator==(const vertexlist_iterator& rhs) const { return iter_ == rhs.iter_; } protected: - mutable shadow_value_type value_ = {}; - vertex_iterator_type iter_; - const VVF* value_fn_ = nullptr; + //mutable shadow_value_type value_ = {}; + mutable internal_value value_; + vertex_iterator_type iter_ = vertex_iterator_type(); + const VVF* value_fn_ = nullptr; friend bool operator==(const vertex_iterator_type& lhs, const vertexlist_iterator& rhs) { return lhs == rhs.iter_; } }; @@ -132,10 +141,19 @@ class vertexlist_iterator { using shadow_vertex_type = remove_reference_t; using shadow_value_type = vertex_descriptor; + union internal_value { + value_type value_; + shadow_value_type shadow_; + + internal_value(vertex_id_type start_at) : shadow_{start_at, nullptr} {} + internal_value(const internal_value& rhs) : shadow_(rhs.shadow_) {} + internal_value() : shadow_{} {} + internal_value& operator=(const internal_value& rhs) { value_.shadow = rhs.value_.shadow; } + }; + public: vertexlist_iterator(graph_type& g) : iter_(ranges::begin(vertices(const_cast(g)))) {} - vertexlist_iterator(vertex_iterator_type iter, vertex_id_type start_at = 0) - : value_{start_at, nullptr}, iter_(iter) {} + vertexlist_iterator(vertex_iterator_type iter, vertex_id_type start_at = 0) : value_{start_at}, iter_(iter) {} constexpr vertexlist_iterator() = default; constexpr vertexlist_iterator(const vertexlist_iterator&) = default; @@ -147,15 +165,15 @@ class vertexlist_iterator { public: constexpr reference operator*() const { - value_.vertex = &*iter_; + value_.shadow_.vertex = &*iter_; if constexpr (!is_void_v) - value_.value = this->value_fn_(*iter_); - return reinterpret_cast(value_); + value_.shadow_.value = this->value_fn_(*iter_); + return value_.value_; } constexpr vertexlist_iterator& operator++() { ++iter_; - ++value_.id; + ++value_.shadow_.id; // leave value_.vertex as-is to avoid dereferencing iter_ to get value_.vertex when it's at end() return *this; } @@ -168,8 +186,8 @@ class vertexlist_iterator { constexpr bool operator==(const vertexlist_iterator& rhs) const { return iter_ == rhs.iter_; } protected: - mutable shadow_value_type value_ = {}; - vertex_iterator_type iter_; + mutable internal_value value_; + vertex_iterator_type iter_ = vertex_iterator_type(); friend bool operator==(const vertex_iterator_type& lhs, const vertexlist_iterator& rhs) { return lhs == rhs.iter_; } }; From 127555f1e8620809fa48546892e04a29954680ae Mon Sep 17 00:00:00 2001 From: Phil Ratzloff Date: Wed, 15 Nov 2023 17:38:28 -0500 Subject: [PATCH 06/10] Fix type punning warning for incidence Use union for value & shadow_value instead of reinterpret_cast(value_) --- include/graph/views/incidence.hpp | 69 +++++++++++++++++++----------- include/graph/views/vertexlist.hpp | 1 - 2 files changed, 44 insertions(+), 26 deletions(-) diff --git a/include/graph/views/incidence.hpp b/include/graph/views/incidence.hpp index 7f37f03..fa7d98a 100644 --- a/include/graph/views/incidence.hpp +++ b/include/graph/views/incidence.hpp @@ -75,30 +75,39 @@ class incidence_iterator : source_vertex>; + union internal_value { + value_type value_; + shadow_value_type shadow_; + + internal_value(const internal_value& rhs) : shadow_(rhs.shadow_) {} + internal_value() : shadow_{} {} + internal_value& operator=(const internal_value& rhs) { value_.shadow = rhs.value_.shadow; } + }; + public: constexpr reference operator*() const { if constexpr (unordered_edge) { if (target_id(g_, *iter_) != this->source_vertex_id()) { - value_.source_id = source_id(g_, *iter_); - value_.target_id = target_id(g_, *iter_); + value_.shadow_.source_id = source_id(g_, *iter_); + value_.shadow_.target_id = target_id(g_, *iter_); } else { - value_.source_id = target_id(g_, *iter_); - value_.target_id = source_id(g_, *iter_); + value_.shadow_.source_id = target_id(g_, *iter_); + value_.shadow_.target_id = source_id(g_, *iter_); } } else if constexpr (Sourced) { if constexpr (sourced_adjacency_list) { - value_.source_id = source_id(g_, *iter_); - value_.target_id = target_id(g_, *iter_); + value_.shadow_.source_id = source_id(g_, *iter_); + value_.shadow_.target_id = target_id(g_, *iter_); } else { - value_.source_id = this->source_vertex_id(); - value_.target_id = target_id(g_, *iter_); + value_.shadow_.source_id = this->source_vertex_id(); + value_.shadow_.target_id = target_id(g_, *iter_); } } else { - value_.target_id = target_id(g_, *iter_); + value_.shadow_.target_id = target_id(g_, *iter_); } - value_.edge = &*iter_; - value_.value = invoke(*value_fn_, *iter_); - return reinterpret_cast(value_); + value_.shadow_.edge = &*iter_; + value_.shadow_.value = invoke(*value_fn_, *iter_); + return value_.value_; } constexpr incidence_iterator& operator++() { @@ -115,7 +124,8 @@ class incidence_iterator : source_vertex g_; edge_iterator iter_; const EVF* value_fn_ = nullptr; @@ -156,6 +166,15 @@ class incidence_iterator using shadow_edge_type = remove_reference_t; using shadow_value_type = edge_descriptor; + union internal_value { + value_type value_; + shadow_value_type shadow_; + + internal_value(const internal_value& rhs) : shadow_(rhs.shadow_) {} + internal_value() : shadow_{} {} + internal_value& operator=(const internal_value& rhs) { value_.shadow = rhs.value_.shadow; } + }; + public: incidence_iterator(graph_type& g, vertex_iterator ui, edge_iterator iter) : base_type(vertex_id(g, ui)), g_(g), iter_(iter) {} @@ -174,25 +193,25 @@ class incidence_iterator if constexpr (unordered_edge) { static_assert(sourced_adjacency_list); if (target_id(g_, *iter_) != this->source_vertex_id()) { - value_.source_id = source_id(g_.*iter_); - value_.target_id = target_id(g_, *iter_); + value_.shadow_.source_id = source_id(g_.*iter_); + value_.shadow_.target_id = target_id(g_, *iter_); } else { - value_.source_id = target_id(g_.*iter_); - value_.target_id = source_id(g_, *iter_); + value_.shadow_.source_id = target_id(g_.*iter_); + value_.shadow_.target_id = source_id(g_, *iter_); } } else if constexpr (Sourced) { if constexpr (sourced_adjacency_list) { - value_.source_id = source_id(g_, *iter_); - value_.target_id = target_id(g_, *iter_); + value_.shadow_.source_id = source_id(g_, *iter_); + value_.shadow_.target_id = target_id(g_, *iter_); } else { - value_.source_id = this->source_vertex_id(); - value_.target_id = target_id(g_, *iter_); + value_.shadow_.source_id = this->source_vertex_id(); + value_.target_id = target_id(g_, *iter_); } } else { - value_.target_id = target_id(g_, *iter_); + value_.shadow_.target_id = target_id(g_, *iter_); } - value_.edge = &*iter_; - return reinterpret_cast(value_); + value_.shadow_.edge = &*iter_; + return value_.value_; } constexpr incidence_iterator& operator++() { @@ -209,7 +228,7 @@ class incidence_iterator //constexpr bool operator==(const incidence_iterator& rhs) const { return iter_ == rhs; } private: // member variables - mutable shadow_value_type value_ = {}; + mutable internal_value value_; _detail::ref_to_ptr g_; edge_iterator iter_; diff --git a/include/graph/views/vertexlist.hpp b/include/graph/views/vertexlist.hpp index 1687069..b344561 100644 --- a/include/graph/views/vertexlist.hpp +++ b/include/graph/views/vertexlist.hpp @@ -104,7 +104,6 @@ class vertexlist_iterator { constexpr bool operator==(const vertexlist_iterator& rhs) const { return iter_ == rhs.iter_; } protected: - //mutable shadow_value_type value_ = {}; mutable internal_value value_; vertex_iterator_type iter_ = vertex_iterator_type(); const VVF* value_fn_ = nullptr; From 755b616ed5a66da4fc1ae8992f8a739fc180b6bb Mon Sep 17 00:00:00 2001 From: Phil Ratzloff Date: Wed, 15 Nov 2023 17:44:56 -0500 Subject: [PATCH 07/10] Fix type punning warning for neighbors in gcc release Use union for value & shadow_value instead of reinterpret_cast(value_) --- include/graph/views/neighbors.hpp | 80 +++++++++++++++++++------------ 1 file changed, 49 insertions(+), 31 deletions(-) diff --git a/include/graph/views/neighbors.hpp b/include/graph/views/neighbors.hpp index 892a597..f8a5c0b 100644 --- a/include/graph/views/neighbors.hpp +++ b/include/graph/views/neighbors.hpp @@ -80,6 +80,15 @@ class neighbor_iterator using shadow_value_type = neighbor_descriptor>; + union internal_value { + value_type value_; + shadow_value_type shadow_; + + internal_value(const internal_value& rhs) : shadow_(rhs.shadow_) {} + internal_value() : shadow_{} {} + internal_value& operator=(const internal_value& rhs) { value_.shadow = rhs.value_.shadow; } + }; + public: constexpr reference operator*() const { // const in this functions signature causes target() to always return a const value, which isn't always what we want @@ -88,29 +97,29 @@ class neighbor_iterator if constexpr (unordered_edge) { static_assert(sourced_adjacency_list); if (target_id(g_, *iter_) != this->source_vertex_id()) { - value_.source_id = source_id(g_.*iter_); - value_.target_id = target_id(g_, *iter_); - value_.target = const_cast(&target(g_, *iter_)); + value_.shadow_.source_id = source_id(g_.*iter_); + value_.shadow_.target_id = target_id(g_, *iter_); + value_.shadow_.target = const_cast(&target(g_, *iter_)); } else { - value_.source_id = target_id(g_.*iter_); - value_.target_id = source_id(g_, *iter_); - value_.target = const_cast(&source(g_, *iter_)); + value_.shadow_.source_id = target_id(g_.*iter_); + value_.shadow_.target_id = source_id(g_, *iter_); + value_.shadow_.target = const_cast(&source(g_, *iter_)); } } else if constexpr (Sourced) { if constexpr (sourced_adjacency_list) { - value_.source_id = source_id(g_, *iter_); - value_.target_id = target_id(g_, *iter_); + value_.shadow_.source_id = source_id(g_, *iter_); + value_.shadow_.target_id = target_id(g_, *iter_); } else { - value_.source_id = this->source_vertex_id(); - value_.target_id = target_id(g_, *iter_); + value_.shadow_.source_id = this->source_vertex_id(); + value_.shadow_.target_id = target_id(g_, *iter_); } - value_.target = const_cast(&target(g_, *iter_)); + value_.shadow_.target = const_cast(&target(g_, *iter_)); } else { - value_.target_id = target_id(g_, *iter_); - value_.target = const_cast(&target(g_, *iter_)); + value_.shadow_.target_id = target_id(g_, *iter_); + value_.shadow_.target = const_cast(&target(g_, *iter_)); } - value_.value = invoke(*value_fn_, *value_.target); // 'value' undeclared identifier (.value not in struct?) - return reinterpret_cast(value_); + value_.shadow_.value = invoke(*value_fn_, *value_.shadow_.target); // 'value' undeclared identifier (.value not in struct?) + return value_.value_; } constexpr neighbor_iterator& operator++() { @@ -127,7 +136,7 @@ class neighbor_iterator //constexpr bool operator==(const neighbor_iterator& rhs) const { return iter_ == rhs; } private: // member variables - mutable shadow_value_type value_ = {}; + mutable internal_value value_; _detail::ref_to_ptr g_; edge_iterator iter_; const VVF* value_fn_ = nullptr; @@ -168,6 +177,15 @@ class neighbor_iterator using shadow_vertex_type = remove_reference_t; using shadow_value_type = neighbor_descriptor; + union internal_value { + value_type value_; + shadow_value_type shadow_; + + internal_value(const internal_value& rhs) : shadow_(rhs.shadow_) {} + internal_value() : shadow_{} {} + internal_value& operator=(const internal_value& rhs) { value_.shadow = rhs.value_.shadow; } + }; + public: neighbor_iterator(graph_type& g, vertex_iterator ui, edge_iterator iter) : base_type(vertex_id(g, ui)), g_(g), iter_(iter) {} @@ -189,28 +207,28 @@ class neighbor_iterator if constexpr (unordered_edge) { static_assert(sourced_adjacency_list); if (target_id(g_, *iter_) != this->source_vertex_id()) { - value_.source_id = source_id(g_.*iter_); - value_.target_id = target_id(g_, *iter_); - value_.target = const_cast(&target(g_, *iter_)); + value_.shadow_.source_id = source_id(g_.*iter_); + value_.shadow_.target_id = target_id(g_, *iter_); + value_.shadow_.target = const_cast(&target(g_, *iter_)); } else { - value_.source_id = target_id(g_.*iter_); - value_.target_id = source_id(g_, *iter_); - value_.target = const_cast(&source(g_, *iter_)); + value_.shadow_.source_id = target_id(g_.*iter_); + value_.shadow_.target_id = source_id(g_, *iter_); + value_.shadow_.target = const_cast(&source(g_, *iter_)); } } else if constexpr (Sourced) { if constexpr (sourced_adjacency_list) { - value_.source_id = source_id(g_, *iter_); - value_.target_id = target_id(g_, *iter_); + value_.shadow_.source_id = source_id(g_, *iter_); + value_.shadow_.target_id = target_id(g_, *iter_); } else { - value_.source_id = this->source_vertex_id(); - value_.target_id = target_id(g_, *iter_); + value_.shadow_.source_id = this->source_vertex_id(); + value_.shadow_.target_id = target_id(g_, *iter_); } - value_.target = const_cast(&target(g_, *iter_)); + value_.shadow_.target = const_cast(&target(g_, *iter_)); } else { - value_.target_id = target_id(g_, *iter_); - value_.target = const_cast(&target(g_, *iter_)); + value_.shadow_.target_id = target_id(g_, *iter_); + value_.shadow_.target = const_cast(&target(g_, *iter_)); } - return reinterpret_cast(value_); + return value_.value_; } constexpr neighbor_iterator& operator++() { @@ -227,7 +245,7 @@ class neighbor_iterator //constexpr bool operator==(const neighbor_iterator& rhs) const { return iter_ == rhs; } private: // member variables - mutable shadow_value_type value_ = {}; + mutable internal_value value_; _detail::ref_to_ptr g_; edge_iterator iter_; From 02039e42cd30a84739e2532ec7e9f685d01608ed Mon Sep 17 00:00:00 2001 From: Phil Ratzloff Date: Wed, 15 Nov 2023 17:53:36 -0500 Subject: [PATCH 08/10] Fix type punning warning for edgelist in gcc release Use union for value & shadow_value instead of reinterpret_cast(value_) --- include/graph/views/edgelist.hpp | 52 +++++++++++++++++++++----------- 1 file changed, 35 insertions(+), 17 deletions(-) diff --git a/include/graph/views/edgelist.hpp b/include/graph/views/edgelist.hpp index 58ca5d5..6293cfc 100644 --- a/include/graph/views/edgelist.hpp +++ b/include/graph/views/edgelist.hpp @@ -130,22 +130,31 @@ class edgelist_iterator : public edgelist_iterator_base { using shadow_value_type = edge_descriptor>; + union internal_value { + value_type value_; + shadow_value_type shadow_; + + internal_value(const internal_value& rhs) : shadow_(rhs.shadow_) {} + internal_value() : shadow_{} {} + internal_value& operator=(const internal_value& rhs) { value_.shadow = rhs.value_.shadow; } + }; + public: constexpr reference operator*() const { if constexpr (unordered_edge) { if (target_id(g_, *uvi_) != vertex_id(g_, ui_)) { - value_.source_id = source_id(g_, *uvi_); - value_.target_id = target_id(g_, *uvi_); + value_.shadow_.source_id = source_id(g_, *uvi_); + value_.shadow_.target_id = target_id(g_, *uvi_); } else { - value_.source_id = target_id(g_, *uvi_); - value_.target_id = source_id(g_, *uvi_); + value_.shadow_.source_id = target_id(g_, *uvi_); + value_.shadow_.target_id = source_id(g_, *uvi_); } - value_.edge = &*uvi_; - value_.value = invoke(*value_fn_, *uvi_); + value_.shadow_.edge = &*uvi_; + value_.shadow_.value = invoke(*value_fn_, *uvi_); } else { - value_ = {vertex_id(g_, ui_), target_id(g_, *uvi_), &*uvi_, invoke(*value_fn_, *uvi_)}; + value_.shadow_ = {vertex_id(g_, ui_), target_id(g_, *uvi_), &*uvi_, invoke(*value_fn_, *uvi_)}; } - return reinterpret_cast(value_); + return value_.value_; } constexpr edgelist_iterator& operator++() { @@ -162,7 +171,7 @@ class edgelist_iterator : public edgelist_iterator_base { //constexpr bool operator==(const edgelist_iterator& rhs) const { return uvi_ == rhs; } private: // member variables - mutable shadow_value_type value_ = {}; + mutable internal_value value_; _detail::ref_to_ptr g_; vertex_iterator ui_; edge_iterator uvi_; @@ -203,6 +212,15 @@ class edgelist_iterator : public edgelist_iterator_base { using shadow_edge_type = remove_reference_t; using shadow_value_type = edge_descriptor; + union internal_value { + value_type value_; + shadow_value_type shadow_; + + internal_value(const internal_value& rhs) : shadow_(rhs.shadow_) {} + internal_value() : shadow_{} {} + internal_value& operator=(const internal_value& rhs) { value_.shadow = rhs.value_.shadow; } + }; + public: edgelist_iterator(graph_type& g, vertex_iterator ui) : base_type(), g_(g), ui_(ui), uvi_() { this->find_non_empty_vertex(g_, ui_, uvi_); @@ -221,17 +239,17 @@ class edgelist_iterator : public edgelist_iterator_base { constexpr reference operator*() const { if constexpr (unordered_edge) { if (target_id(g_, *uvi_) != vertex_id(g_, ui_)) { - value_.source_id = source_id(g_, *uvi_); - value_.target_id = target_id(g_, *uvi_); + value_.shadow_.source_id = source_id(g_, *uvi_); + value_.shadow_.target_id = target_id(g_, *uvi_); } else { - value_.source_id = target_id(g_, *uvi_); - value_.target_id = source_id(g_, *uvi_); + value_.shadow_.source_id = target_id(g_, *uvi_); + value_.shadow_.target_id = source_id(g_, *uvi_); } - value_.edge = &*uvi_; + value_.shadow_.edge = &*uvi_; } else { - value_ = {vertex_id(g_, ui_), target_id(g_, *uvi_), &*uvi_}; + value_.shadow_ = {vertex_id(g_, ui_), target_id(g_, *uvi_), &*uvi_}; } - return reinterpret_cast(value_); + return value_.value_; } constexpr edgelist_iterator& operator++() { @@ -248,7 +266,7 @@ class edgelist_iterator : public edgelist_iterator_base { //constexpr bool operator==(const edgelist_iterator& rhs) const { return uvi_ == rhs; } private: // member variables - mutable shadow_value_type value_ = {}; + mutable internal_value value_; _detail::ref_to_ptr g_; vertex_iterator ui_; edge_iterator uvi_; From 20f4b82bdda1a044df454875c2d1913f713bb665 Mon Sep 17 00:00:00 2001 From: Phil Ratzloff Date: Wed, 15 Nov 2023 20:19:22 -0500 Subject: [PATCH 09/10] Fix type punning warning for dfs in gcc release Use union for value & shadow_value instead of reinterpret_cast(value_) Also added ~internal_value() to all uses of internal_value to address warning/error --- include/graph/views/depth_first_search.hpp | 82 ++++++++++++++++------ include/graph/views/edgelist.hpp | 2 + include/graph/views/incidence.hpp | 2 + include/graph/views/neighbors.hpp | 2 + include/graph/views/vertexlist.hpp | 2 + 5 files changed, 69 insertions(+), 21 deletions(-) diff --git a/include/graph/views/depth_first_search.hpp b/include/graph/views/depth_first_search.hpp index a132125..de108ed 100644 --- a/include/graph/views/depth_first_search.hpp +++ b/include/graph/views/depth_first_search.hpp @@ -251,6 +251,16 @@ class vertices_depth_first_search_view : public dfs_base { using shadow_value_type = vertex_descriptor, shadow_vertex_type*, _detail::ref_to_ptr>; + union internal_value { + value_type value_; + shadow_value_type shadow_; + + internal_value(const internal_value& rhs) : shadow_(rhs.shadow_) {} + internal_value() : shadow_{} {} + ~internal_value() {} + internal_value& operator=(const internal_value& rhs) { value_.shadow = rhs.value_.shadow; } + }; + public: iterator(const dfs_range_type& range) : the_range_(&const_cast(range)) {} iterator() = default; @@ -276,16 +286,16 @@ class vertices_depth_first_search_view : public dfs_base { auto&& [u_id, uvi] = the_range_->S_.top(); vertex_id_type v_id = the_range_->real_target_id(*uvi, u_id); auto& v = *find_vertex(g, v_id); - value_ = {v_id, &v, invoke(*the_range_->value_fn_, v)}; - return reinterpret_cast(value_); + value_.shadow_ = {v_id, &v, invoke(*the_range_->value_fn_, v)}; + return value_.value_; } constexpr bool operator==(const end_sentinel&) const noexcept { return the_range_->S_.empty(); } //constexpr bool operator!=(const end_sentinel& rhs) const noexcept { return !operator==(rhs); } private: - mutable shadow_value_type value_ = {}; - dfs_range_type* the_range_ = nullptr; + mutable internal_value value_; + dfs_range_type* the_range_ = nullptr; friend end_sentinel; }; @@ -350,6 +360,16 @@ class vertices_depth_first_search_view : public dfs_base< using shadow_vertex_type = remove_reference_t; using shadow_value_type = vertex_descriptor, shadow_vertex_type*, void>; + union internal_value { + value_type value_; + shadow_value_type shadow_; + + internal_value(const internal_value& rhs) : shadow_(rhs.shadow_) {} + internal_value() : shadow_{} {} + ~internal_value() {} + internal_value& operator=(const internal_value& rhs) { value_.shadow = rhs.value_.shadow; } + }; + public: iterator(const dfs_range_type& range) : the_range_(&const_cast(range)) {} iterator() = default; @@ -375,16 +395,16 @@ class vertices_depth_first_search_view : public dfs_base< auto&& [u_id, uvi] = the_range_->S_.top(); vertex_id_type v_id = the_range_->real_target_id(*uvi, u_id); auto& v = *find_vertex(g, v_id); - value_ = {v_id, &v}; - return reinterpret_cast(value_); + value_.shadow_ = {v_id, &v}; + return value_.value_; } bool operator==(const end_sentinel&) const noexcept { return the_range_->S_.empty(); } //bool operator!=(const end_sentinel& rhs) const noexcept { return !operator==(rhs); } private: - mutable shadow_value_type value_ = {}; - dfs_range_type* the_range_ = nullptr; + mutable internal_value value_; + dfs_range_type* the_range_ = nullptr; friend end_sentinel; }; @@ -462,6 +482,16 @@ class edges_depth_first_search_view : public dfs_base { using shadow_value_type = edge_descriptor>; + union internal_value { + value_type value_; + shadow_value_type shadow_; + + internal_value(const internal_value& rhs) : shadow_(rhs.shadow_) {} + internal_value() : shadow_{} {} + ~internal_value() {} + internal_value& operator=(const internal_value& rhs) { value_.shadow = rhs.value_.shadow; } + }; + public: iterator(const dfs_range_type& range) : the_range_(&const_cast(range)) {} iterator() = default; @@ -485,20 +515,20 @@ class edges_depth_first_search_view : public dfs_base { reference operator*() const noexcept { auto&& [u_id, uvi] = the_range_->S_.top(); if constexpr (Sourced) { - value_.source_id = u_id; + value_.shadow_.source_id = u_id; } - value_.target_id = the_range_->real_target_id(*uvi, u_id); - value_.edge = &*uvi; - value_.value = invoke(*the_range_->value_fn_, *uvi); - return reinterpret_cast(value_); + value_.shadow_.target_id = the_range_->real_target_id(*uvi, u_id); + value_.shadow_.edge = &*uvi; + value_.shadow_.value = invoke(*the_range_->value_fn_, *uvi); + return value_.value_; } bool operator==(const end_sentinel&) const noexcept { return the_range_->S_.empty(); } //bool operator!=(const end_sentinel& rhs) const noexcept { return !operator==(rhs); } private: - mutable shadow_value_type value_ = {}; - dfs_range_type* the_range_ = nullptr; + mutable internal_value value_; + dfs_range_type* the_range_ = nullptr; friend end_sentinel; }; @@ -560,6 +590,16 @@ class edges_depth_first_search_view : public dfs using shadow_edge_type = remove_reference_t; using shadow_value_type = edge_descriptor; + union internal_value { + value_type value_; + shadow_value_type shadow_; + + internal_value(const internal_value& rhs) : shadow_(rhs.shadow_) {} + internal_value() : shadow_{} {} + ~internal_value() {} + internal_value& operator=(const internal_value& rhs) { value_.shadow = rhs.value_.shadow; } + }; + public: iterator(const dfs_range_type& range) : the_range_(&const_cast(range)) {} iterator() = default; @@ -583,19 +623,19 @@ class edges_depth_first_search_view : public dfs reference operator*() const noexcept { auto&& [u_id, uvi] = the_range_->S_.top(); if constexpr (Sourced) { - value_.source_id = u_id; + value_.shadow_.source_id = u_id; } - value_.target_id = the_range_->real_target_id(*uvi, u_id); - value_.edge = &*uvi; - return reinterpret_cast(value_); + value_.shadow_.target_id = the_range_->real_target_id(*uvi, u_id); + value_.shadow_.edge = &*uvi; + return value_.value_; } bool operator==(const end_sentinel&) const noexcept { return the_range_->S_.empty(); } //bool operator!=(const end_sentinel& rhs) const noexcept { return !operator==(rhs); } private: - mutable shadow_value_type value_ = {}; - dfs_range_type* the_range_ = nullptr; + mutable internal_value value_; + dfs_range_type* the_range_ = nullptr; friend end_sentinel; }; diff --git a/include/graph/views/edgelist.hpp b/include/graph/views/edgelist.hpp index 6293cfc..9807ddc 100644 --- a/include/graph/views/edgelist.hpp +++ b/include/graph/views/edgelist.hpp @@ -136,6 +136,7 @@ class edgelist_iterator : public edgelist_iterator_base { internal_value(const internal_value& rhs) : shadow_(rhs.shadow_) {} internal_value() : shadow_{} {} + ~internal_value() {} internal_value& operator=(const internal_value& rhs) { value_.shadow = rhs.value_.shadow; } }; @@ -218,6 +219,7 @@ class edgelist_iterator : public edgelist_iterator_base { internal_value(const internal_value& rhs) : shadow_(rhs.shadow_) {} internal_value() : shadow_{} {} + ~internal_value() {} internal_value& operator=(const internal_value& rhs) { value_.shadow = rhs.value_.shadow; } }; diff --git a/include/graph/views/incidence.hpp b/include/graph/views/incidence.hpp index fa7d98a..1513725 100644 --- a/include/graph/views/incidence.hpp +++ b/include/graph/views/incidence.hpp @@ -81,6 +81,7 @@ class incidence_iterator : source_vertex internal_value(const internal_value& rhs) : shadow_(rhs.shadow_) {} internal_value() : shadow_{} {} + ~internal_value() {} internal_value& operator=(const internal_value& rhs) { value_.shadow = rhs.value_.shadow; } }; diff --git a/include/graph/views/neighbors.hpp b/include/graph/views/neighbors.hpp index f8a5c0b..568e843 100644 --- a/include/graph/views/neighbors.hpp +++ b/include/graph/views/neighbors.hpp @@ -86,6 +86,7 @@ class neighbor_iterator internal_value(const internal_value& rhs) : shadow_(rhs.shadow_) {} internal_value() : shadow_{} {} + ~internal_value() {} internal_value& operator=(const internal_value& rhs) { value_.shadow = rhs.value_.shadow; } }; @@ -183,6 +184,7 @@ class neighbor_iterator internal_value(const internal_value& rhs) : shadow_(rhs.shadow_) {} internal_value() : shadow_{} {} + ~internal_value() {} internal_value& operator=(const internal_value& rhs) { value_.shadow = rhs.value_.shadow; } }; diff --git a/include/graph/views/vertexlist.hpp b/include/graph/views/vertexlist.hpp index b344561..fda9858 100644 --- a/include/graph/views/vertexlist.hpp +++ b/include/graph/views/vertexlist.hpp @@ -66,6 +66,7 @@ class vertexlist_iterator { internal_value(vertex_id_type start_at) : shadow_{} { shadow_.id = start_at; } internal_value(const internal_value& rhs) : shadow_(rhs.shadow_) {} internal_value() : shadow_{} {} + ~internal_value() {} internal_value& operator=(const internal_value& rhs) { value_.shadow = rhs.value_.shadow; } }; @@ -147,6 +148,7 @@ class vertexlist_iterator { internal_value(vertex_id_type start_at) : shadow_{start_at, nullptr} {} internal_value(const internal_value& rhs) : shadow_(rhs.shadow_) {} internal_value() : shadow_{} {} + ~internal_value() {} internal_value& operator=(const internal_value& rhs) { value_.shadow = rhs.value_.shadow; } }; From 75b14a9f4669bb0ce9fc35d5817508f79a8e0c7b Mon Sep 17 00:00:00 2001 From: Phil Ratzloff Date: Wed, 15 Nov 2023 20:43:26 -0500 Subject: [PATCH 10/10] Fix type punning warning for bfs in gcc release Use union for value & shadow_value instead of reinterpret_cast(value_) Also removed meaningless double casts in pagerank tests --- include/graph/views/breadth_first_search.hpp | 86 +++++++++++++++----- tests/pagerank_tests.cpp | 2 +- 2 files changed, 66 insertions(+), 22 deletions(-) diff --git a/include/graph/views/breadth_first_search.hpp b/include/graph/views/breadth_first_search.hpp index 0fd0489..47ed966 100644 --- a/include/graph/views/breadth_first_search.hpp +++ b/include/graph/views/breadth_first_search.hpp @@ -260,6 +260,17 @@ class vertices_breadth_first_search_view : public bfs_base { using shadow_value_type = vertex_descriptor, shadow_vertex_type*, _detail::ref_to_ptr>; + union internal_value { + value_type value_; + shadow_value_type shadow_; + + internal_value(vertex_id_type start_at) : shadow_{start_at, nullptr} {} + internal_value(const internal_value& rhs) : shadow_(rhs.shadow_) {} + internal_value() : shadow_{} {} + ~internal_value() {} + internal_value& operator=(const internal_value& rhs) { value_.shadow = rhs.value_.shadow; } + }; + public: iterator(const bfs_range_type& range) : the_range_(&const_cast(range)) {} iterator() = default; @@ -286,16 +297,16 @@ class vertices_breadth_first_search_view : public bfs_base { auto&& uvi = the_range_->uv_; vertex_id_type v_id = the_range_->real_target_id(*uvi, u_id); auto& v = *find_vertex(g, v_id); - value_ = {v_id, &v, invoke(*the_range_->value_fn_, v)}; - return reinterpret_cast(value_); + value_.shadow_ = {v_id, &v, invoke(*the_range_->value_fn_, v)}; + return value_.value_; } constexpr bool operator==(const end_sentinel&) const noexcept { return the_range_->Q_.empty(); } constexpr bool operator!=(const end_sentinel& rhs) const noexcept { return !operator==(rhs); } private: - mutable shadow_value_type value_ = {}; - bfs_range_type* the_range_ = nullptr; + mutable internal_value value_; + bfs_range_type* the_range_ = nullptr; friend end_sentinel; }; @@ -367,6 +378,17 @@ class vertices_breadth_first_search_view : public bfs_bas using shadow_vertex_type = remove_reference_t; using shadow_value_type = vertex_descriptor, shadow_vertex_type*, void>; + union internal_value { + value_type value_; + shadow_value_type shadow_; + + internal_value(vertex_id_type start_at) : shadow_{start_at, nullptr} {} + internal_value(const internal_value& rhs) : shadow_(rhs.shadow_) {} + internal_value() : shadow_{} {} + ~internal_value() {} + internal_value& operator=(const internal_value& rhs) { value_.shadow = rhs.value_.shadow; } + }; + public: iterator(const bfs_range_type& range) : the_range_(&const_cast(range)) {} iterator() = default; @@ -393,16 +415,16 @@ class vertices_breadth_first_search_view : public bfs_bas auto&& uvi = the_range_->uv_; vertex_id_type v_id = the_range_->real_target_id(*uvi, u_id); auto& v = *find_vertex(g, v_id); - value_ = {v_id, &v}; - return reinterpret_cast(value_); + value_.shadow_ = {v_id, &v}; + return value_.value_; } bool operator==(const end_sentinel&) const noexcept { return the_range_->Q_.empty(); } //bool operator!=(const end_sentinel& rhs) const noexcept { return !operator==(rhs); } private: - mutable shadow_value_type value_ = {}; - bfs_range_type* the_range_ = nullptr; + mutable internal_value value_; + bfs_range_type* the_range_ = nullptr; friend end_sentinel; }; @@ -482,6 +504,17 @@ class edges_breadth_first_search_view : public bfs_base { using shadow_value_type = edge_descriptor>; + union internal_value { + value_type value_; + shadow_value_type shadow_; + + internal_value(vertex_id_type start_at) : shadow_{start_at, nullptr} {} + internal_value(const internal_value& rhs) : shadow_(rhs.shadow_) {} + internal_value() : shadow_{} {} + ~internal_value() {} + internal_value& operator=(const internal_value& rhs) { value_.shadow = rhs.value_.shadow; } + }; + public: iterator(const bfs_range_type& range) : the_range_(&const_cast(range)) {} iterator() = default; @@ -506,20 +539,20 @@ class edges_breadth_first_search_view : public bfs_base { auto&& u_id = the_range_->Q_.front(); auto&& uvi = the_range_->uv_; if constexpr (Sourced) { - value_.source_id = u_id; + value_.shadow_.source_id = u_id; } - value_.target_id = the_range_->real_target_id(*uvi, u_id); - value_.edge = &*uvi; - value_.value = invoke(*the_range_->value_fn_, *uvi); - return reinterpret_cast(value_); + value_.shadow_.target_id = the_range_->real_target_id(*uvi, u_id); + value_.shadow_.edge = &*uvi; + value_.shadow_.value = invoke(*the_range_->value_fn_, *uvi); + return value_.value_; } bool operator==(const end_sentinel&) const noexcept { return the_range_->Q_.empty(); } bool operator!=(const end_sentinel& rhs) const noexcept { return !operator==(rhs); } private: - mutable shadow_value_type value_ = {}; - bfs_range_type* the_range_ = nullptr; + mutable internal_value value_; + bfs_range_type* the_range_ = nullptr; friend end_sentinel; }; @@ -584,6 +617,17 @@ class edges_breadth_first_search_view : public b using shadow_edge_type = remove_reference_t; using shadow_value_type = edge_descriptor; + union internal_value { + value_type value_; + shadow_value_type shadow_; + + internal_value(vertex_id_type start_at) : shadow_{start_at, nullptr} {} + internal_value(const internal_value& rhs) : shadow_(rhs.shadow_) {} + internal_value() : shadow_{} {} + ~internal_value() {} + internal_value& operator=(const internal_value& rhs) { value_.shadow = rhs.value_.shadow; } + }; + public: iterator(const bfs_range_type& range) : the_range_(&const_cast(range)) {} iterator() = default; @@ -608,19 +652,19 @@ class edges_breadth_first_search_view : public b auto&& u_id = the_range_->Q_.front(); auto&& uvi = the_range_->uv_; if constexpr (Sourced) { - value_.source_id = u_id; + value_.shadow_.source_id = u_id; } - value_.target_id = the_range_->real_target_id(*uvi, u_id); - value_.edge = &*uvi; - return reinterpret_cast(value_); + value_.shadow_.target_id = the_range_->real_target_id(*uvi, u_id); + value_.shadow_.edge = &*uvi; + return value_.value_; } bool operator==(const end_sentinel&) const noexcept { return the_range_->Q_.empty(); } bool operator!=(const end_sentinel& rhs) const noexcept { return !operator==(rhs); } private: - mutable shadow_value_type value_ = {}; - bfs_range_type* the_range_ = nullptr; + mutable internal_value value_; + bfs_range_type* the_range_ = nullptr; friend end_sentinel; }; diff --git a/tests/pagerank_tests.cpp b/tests/pagerank_tests.cpp index 750953e..853d521 100644 --- a/tests/pagerank_tests.cpp +++ b/tests/pagerank_tests.cpp @@ -43,7 +43,7 @@ TEST_CASE("PageRank", "[pagerank]") { auto&& g = load_ordered_graph(TEST_DATA_ROOT_DIR "germany_routes.csv", name_order_policy::source_order_found); std::vector page_rank(size(vertices(g))); - std::graph::pagerank(g, page_rank, double(0.85), double(1e-4), 10); + std::graph::pagerank(g, page_rank, 0.85, 1e-4, 10); std::vector answer = {0.051086017487729, 0.065561667371485, 0.106818581147795, 0.141889899564636, 0.065561667371485, 0.078952299317762, 0.065561667371485, 0.078952299317762,