From 3261506296ef9c77cd0df85bf396a7e0b8b3aad6 Mon Sep 17 00:00:00 2001 From: Phil Ratzloff Date: Sun, 12 Nov 2023 12:09:38 -0500 Subject: [PATCH 01/11] Replace old tag_invoke-based graph_value with new style CPO --- include/graph/container/compressed_graph.hpp | 4 +- include/graph/container/dynamic_graph.hpp | 4 +- include/graph/detail/graph_cpo.hpp | 100 ++++++++++++++----- 3 files changed, 79 insertions(+), 29 deletions(-) diff --git a/include/graph/container/compressed_graph.hpp b/include/graph/container/compressed_graph.hpp index 8f61b5f..bd3fd26 100644 --- a/include/graph/container/compressed_graph.hpp +++ b/include/graph/container/compressed_graph.hpp @@ -922,10 +922,10 @@ class compressed_graph : public compressed_graph_base>; // Graph data structure must define // namespace _Target_id { -# if defined(__clang__) || defined(__EDG__) // TRANSITION, VSO-1681199 - void target_id() = delete; // Block unqualified name lookup -# else // ^^^ no workaround / workaround vvv +# if defined(__clang__) || defined(__EDG__) // TRANSITION, VSO-1681199 + void target_id() = delete; // Block unqualified name lookup +# else // ^^^ no workaround / workaround vvv void target_id(); -# endif // ^^^ workaround ^^^ +# endif // ^^^ workaround ^^^ template concept _Has_ref_member = requires(_G&& __g, edge_reference_t<_G> uv) { @@ -543,8 +543,7 @@ namespace _Target_id { } else if constexpr (_Strat_ref == _St_ref::_Non_member) { return target_id(__g, uv); // intentional ADL } else { - static_assert(_Always_false<_G>, - "target_id(g,uv) or g.target_id(uv) is not defined"); + static_assert(_Always_false<_G>, "target_id(g,uv) or g.target_id(uv) is not defined"); } } }; @@ -1215,27 +1214,78 @@ using edge_value_t = decltype(edge_value(declval(), declval = decltype(graph_value(g)) // -namespace tag_invoke { - TAG_INVOKE_DEF(graph_value); // graph_value(g) -> GV& -} +namespace _Graph_value { +# if defined(__clang__) || defined(__EDG__) // TRANSITION, VSO-1681199 + void graph_value() = delete; // Block unqualified name lookup +# else // ^^^ no workaround / workaround vvv + void graph_value(); +# endif // ^^^ workaround ^^^ -/** - * @brief The user-defined value for a graph, if it exists. - * - * Complexity: O(1) - * - * Default implementation: n/a. This must be specialized for each graph type, if available. - * - * @tparam G The graph type. - * @param g A graph instance. - * @return The value associated with graph g. -*/ -template -auto&& graph_value(G&& g) { - return tag_invoke::graph_value(g); + template + concept _Has_ref_member = requires(_G&& __g) { + { _Fake_copy_init(__g.graph_value()) }; + }; + template + concept _Has_ref_ADL = _Has_class_or_enum_type<_G> // + && requires(_G&& __g) { + { _Fake_copy_init(graph_value(__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>().graph_value()))}; + } else if constexpr (_Has_ref_ADL<_G, _UnCV>) { + return {_St_ref::_Non_member, noexcept(_Fake_copy_init(graph_value(declval<_G>())))}; // intentional ADL + } 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) 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.graph_value(); + } else if constexpr (_Strat_ref == _St_ref::_Non_member) { + return graph_value(__g); // intentional ADL + } else { + static_assert(_Always_false<_G>, + "graph_value(g,u) is not defined and the default implementation cannot be evaluated"); + } + } + }; +} // namespace _Graph_value + +inline namespace _Cpos { + inline constexpr _Graph_value::_Cpo graph_value; } -template -using graph_value_t = decltype(graph_value(declval())); + namespace edgelist { namespace tag_invoke { From 354b2f2703f6a7fca3869969b7fb4eb1111d400a Mon Sep 17 00:00:00 2001 From: Phil Ratzloff Date: Sun, 12 Nov 2023 12:48:32 -0500 Subject: [PATCH 02/11] Replace old tag_invoke-based vertex_value with new style CPO --- example/CppCon2022/rr_adaptor.hpp | 10 +- include/graph/container/compressed_graph.hpp | 14 +-- include/graph/container/dynamic_graph.hpp | 17 +--- include/graph/detail/graph_cpo.hpp | 97 +++++++++++++++----- tests/csv_routes.hpp | 4 +- 5 files changed, 89 insertions(+), 53 deletions(-) diff --git a/example/CppCon2022/rr_adaptor.hpp b/example/CppCon2022/rr_adaptor.hpp index 8dba9f4..ae7e7ca 100644 --- a/example/CppCon2022/rr_adaptor.hpp +++ b/example/CppCon2022/rr_adaptor.hpp @@ -157,13 +157,11 @@ class rr_adaptor { return get<0>(to_tuple(uv)); } - friend constexpr vertex_value_type& - tag_invoke(std::graph::tag_invoke::vertex_value_fn_t, graph_type& g, vertex_type& u) { + friend constexpr vertex_value_type& vertex_value(graph_type& g, vertex_type& u) { size_t uidx = static_cast(&u - g.vertices_.data()); return g.vertex_values_[uidx]; } - friend constexpr const vertex_value_type& - tag_invoke(std::graph::tag_invoke::vertex_value_fn_t, const graph_type& g, const vertex_type& u) { + friend constexpr const vertex_value_type& vertex_value(const graph_type& g, const vertex_type& u) { size_t uidx = static_cast(&u - g.vertices_.data()); return g.vertex_values_[uidx]; } @@ -393,11 +391,11 @@ class rr_adaptor2 { } friend constexpr vertex_value_type& - tag_invoke(std::graph::tag_invoke::vertex_value_fn_t, graph_type& g, vertex_type& u) { + vertex_value(graph_type& g, vertex_type& u) { return get<1>(to_tuple(u)); } friend constexpr const vertex_value_type& - tag_invoke(std::graph::tag_invoke::vertex_value_fn_t, const graph_type& g, const vertex_type& u) { + vertex_value(const graph_type& g, const vertex_type& u) { return get<1>(to_tuple(u)); } diff --git a/include/graph/container/compressed_graph.hpp b/include/graph/container/compressed_graph.hpp index bd3fd26..68aee34 100644 --- a/include/graph/container/compressed_graph.hpp +++ b/include/graph/container/compressed_graph.hpp @@ -204,15 +204,13 @@ class csr_row_values { constexpr const_reference operator[](size_type pos) const { return v_[pos]; } private: - friend constexpr vertex_value_type& - tag_invoke(::std::graph::tag_invoke::vertex_value_fn_t, graph_type& g, vertex_type& u) { + friend constexpr vertex_value_type& vertex_value(graph_type& g, vertex_type& u) { static_assert(ranges::contiguous_range, "row_index_ must be a contiguous range to evaluate uidx"); auto uidx = g.index_of(u); csr_row_values& row_vals = g; return row_vals.v_[uidx]; } - friend constexpr const vertex_value_type& - tag_invoke(::std::graph::tag_invoke::vertex_value_fn_t, const graph_type& g, const vertex_type& u) { + friend constexpr const vertex_value_type& vertex_value(const graph_type& g, const vertex_type& u) { static_assert(ranges::contiguous_range, "row_index_ must be a contiguous range to evaluate uidx"); auto uidx = g.index_of(u); const csr_row_values& row_vals = g; @@ -922,12 +920,8 @@ class compressed_graph : public compressed_graph_base = decltype(vertex_value(g,u)) // -namespace tag_invoke { - TAG_INVOKE_DEF(vertex_value); // vertex_value(g,u) -> ? -} +namespace _Vertex_value { +# if defined(__clang__) || defined(__EDG__) // TRANSITION, VSO-1681199 + void vertex_value() = delete; // Block unqualified name lookup +# else // ^^^ no workaround / workaround vvv + void vertex_value(); +# endif // ^^^ workaround ^^^ -/** - * @brief The user-defined value for a vertex, if it exists. - * - * Complexity: O(1) - * - * Default implementation: n/a. This must be specialized for the vertex type of each graph type, if available. - * - * @tparam G The graph type. - * @param g A graph instance. - * @param u A vertex instance. - * @return The value associated with vertex u. -*/ -template -auto vertex_value(G&& g, vertex_reference_t u) -> decltype(tag_invoke::vertex_value(g, u)) { - return tag_invoke::vertex_value(g, u); + template + concept _Has_ref_member = requires(_G&& __g, vertex_reference_t<_G> u) { + { _Fake_copy_init(u.vertex_value(__g)) }; + }; + template + 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 + }; + + 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>().vertex_value( + declval>())))}; + } else if constexpr (_Has_ref_ADL<_G, _UnCV>) { + return {_St_ref::_Non_member, noexcept(_Fake_copy_init(vertex_value( + declval<_G>(), declval>())))}; // intentional ADL + } 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.vertex_value(__g); + } else if constexpr (_Strat_ref == _St_ref::_Non_member) { + return vertex_value(__g, u); // intentional ADL + } else { + static_assert(_Always_false<_G>, "vertex_value(g,u) must be defined for the graph"); + } + } + }; +} // namespace _Vertex_value + +inline namespace _Cpos { + inline constexpr _Vertex_value::_Cpo vertex_value; } + template using vertex_value_t = decltype(vertex_value(declval(), declval>())); @@ -1266,8 +1320,8 @@ namespace _Graph_value { * @return The number of outgoing edges of vertex u. */ template - //requires(_Choice_ref<_G&>._Strategy != _St_ref::_None) - [[nodiscard]] constexpr auto operator()(_G&& __g) const noexcept(_Choice_ref<_G&>._No_throw) { + 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) { @@ -1275,8 +1329,7 @@ namespace _Graph_value { } else if constexpr (_Strat_ref == _St_ref::_Non_member) { return graph_value(__g); // intentional ADL } else { - static_assert(_Always_false<_G>, - "graph_value(g,u) is not defined and the default implementation cannot be evaluated"); + static_assert(_Always_false<_G>, "graph_value(g) must be defined for the graph"); } } }; diff --git a/tests/csv_routes.hpp b/tests/csv_routes.hpp index 1efd94f..649bf61 100644 --- a/tests/csv_routes.hpp +++ b/tests/csv_routes.hpp @@ -172,7 +172,7 @@ template std::optional> find_city(G&& g, std::string_view city_name) { #if 1 auto it = std::ranges::find_if(std::graph::vertices(g), - [&g, &city_name](auto& u) { return std::graph::vertex_value(g, u) == city_name; }); + [&g, &city_name](auto& u) { return std::graph::vertex_value(g, u) == city_name; }); if (it != end(std::graph::vertices(g))) return std::optional>(it); return std::optional>(); @@ -191,7 +191,7 @@ template std::graph::vertex_id_t find_city_id(G&& g, std::string_view city_name) { #if 1 auto it = std::ranges::find_if(std::graph::vertices(g), - [&g, &city_name](auto& u) { return std::graph::vertex_value(g, u) == city_name; }); + [&g, &city_name](auto& u) { return std::graph::vertex_value(g, u) == city_name; }); #else auto vertex_to_name = [&g](std::graph::vertex_reference_t u) { return std::graph::vertex_value(g, u); }; auto it = std::ranges::lower_bound(std::graph::vertices(g), city_name, std::less(), vertex_to_name); From 3efe89e3b595e994acbd8feb80a9c806f6fbfeba Mon Sep 17 00:00:00 2001 From: Phil Ratzloff Date: Sun, 12 Nov 2023 13:10:23 -0500 Subject: [PATCH 03/11] Replace old tag_invoke-based vertex_value with new style CPO --- example/CppCon2022/rr_adaptor.hpp | 18 ++-- include/graph/container/compressed_graph.hpp | 6 +- include/graph/container/dynamic_graph.hpp | 10 +-- include/graph/container/utility_edgelist.hpp | 5 +- include/graph/detail/graph_cpo.hpp | 90 ++++++++++++++++---- 5 files changed, 83 insertions(+), 46 deletions(-) diff --git a/example/CppCon2022/rr_adaptor.hpp b/example/CppCon2022/rr_adaptor.hpp index ae7e7ca..9d53590 100644 --- a/example/CppCon2022/rr_adaptor.hpp +++ b/example/CppCon2022/rr_adaptor.hpp @@ -167,12 +167,11 @@ class rr_adaptor { } // edge_value(g,uv) - friend constexpr edge_value_type& tag_invoke(std::graph::tag_invoke::edge_value_fn_t, 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& - tag_invoke(std::graph::tag_invoke::edge_value_fn_t, 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); } @@ -390,21 +389,16 @@ class rr_adaptor2 { return get<0>(to_tuple(uv)); } - friend constexpr vertex_value_type& - vertex_value(graph_type& g, vertex_type& u) { - return get<1>(to_tuple(u)); - } - friend constexpr const vertex_value_type& - vertex_value(const graph_type& g, const vertex_type& u) { + friend constexpr vertex_value_type& vertex_value(graph_type& g, vertex_type& u) { return get<1>(to_tuple(u)); } + friend constexpr const vertex_value_type& vertex_value(const graph_type& g, const vertex_type& u) { return get<1>(to_tuple(u)); } // edge_value(g,uv) - friend constexpr edge_value_type& tag_invoke(std::graph::tag_invoke::edge_value_fn_t, graph_type& g, edge_type& uv) { + friend constexpr edge_value_type& edge_value(graph_type& g, edge_type& uv) { // return get<1>(to_tuple(uv)); } - friend constexpr const edge_value_type& - tag_invoke(std::graph::tag_invoke::edge_value_fn_t, const graph_type& g, const edge_type& uv) { + friend constexpr const edge_value_type& edge_value(const graph_type& g, const edge_type& uv) { return get<1>(to_tuple(uv)); } diff --git a/include/graph/container/compressed_graph.hpp b/include/graph/container/compressed_graph.hpp index 68aee34..0e10022 100644 --- a/include/graph/container/compressed_graph.hpp +++ b/include/graph/container/compressed_graph.hpp @@ -318,14 +318,12 @@ class csr_col_values { private: // edge_value(g,uv) - friend constexpr edge_value_type& - tag_invoke(::std::graph::tag_invoke::edge_value_fn_t, graph_type& g, edge_type& uv) { + friend constexpr edge_value_type& edge_value(graph_type& g, edge_type& uv) { auto uv_idx = g.index_of(uv); csr_col_values& col_vals = g; return col_vals.v_[uv_idx]; } - friend constexpr const edge_value_type& - tag_invoke(::std::graph::tag_invoke::edge_value_fn_t, 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 uv_idx = g.index_of(uv); const csr_col_values& col_vals = g; return col_vals.v_[uv_idx]; diff --git a/include/graph/container/dynamic_graph.hpp b/include/graph/container/dynamic_graph.hpp index 2d9ce50..461fcc3 100644 --- a/include/graph/container/dynamic_graph.hpp +++ b/include/graph/container/dynamic_graph.hpp @@ -383,14 +383,8 @@ class dynamic_edge_value { value_type value_ = value_type(); private: // tag_invoke properties - friend constexpr value_type& - tag_invoke(::std::graph::tag_invoke::edge_value_fn_t, graph_type& g, edge_type& uv) noexcept { - return uv.value_; - } - friend constexpr const value_type& - tag_invoke(::std::graph::tag_invoke::edge_value_fn_t, const graph_type& g, const edge_type& uv) noexcept { - return uv.value_; - } + friend constexpr value_type& edge_value(graph_type& g, edge_type& uv) noexcept { return uv.value_; } + friend constexpr const value_type& edge_value(const graph_type& g, const edge_type& uv) noexcept { return uv.value_; } }; /** diff --git a/include/graph/container/utility_edgelist.hpp b/include/graph/container/utility_edgelist.hpp index 1d4d3d9..c5f250c 100644 --- a/include/graph/container/utility_edgelist.hpp +++ b/include/graph/container/utility_edgelist.hpp @@ -81,10 +81,7 @@ class utility_edgelist { return std::get<0>(e); } - friend constexpr EV& - tag_invoke(::std::graph::edgelist::tag_invoke::edge_value_fn_t, utility_edgelist& el, value_type& e) { - return std::get<2>(e); - } + friend constexpr EV& edge_value(utility_edgelist& el, value_type& e) { return std::get<2>(e); } storage_type storage_; VSourceId source_max_; diff --git a/include/graph/detail/graph_cpo.hpp b/include/graph/detail/graph_cpo.hpp index 7eddbd8..a00fc2a 100644 --- a/include/graph/detail/graph_cpo.hpp +++ b/include/graph/detail/graph_cpo.hpp @@ -1238,25 +1238,79 @@ using vertex_value_t = decltype(vertex_value(declval(), declval = decltype(edge_value(g,uv)) // -namespace tag_invoke { - TAG_INVOKE_DEF(edge_value); -} +namespace _Edge_value { +# if defined(__clang__) || defined(__EDG__) // TRANSITION, VSO-1681199 + void edge_value() = delete; // Block unqualified name lookup +# else // ^^^ no workaround / workaround vvv + void edge_value(); +# endif // ^^^ workaround ^^^ -/** - * @brief The user-defined value for a edge, if it exists. - * - * Complexity: O(1) - * - * Default implementation: n/a. This must be specialized for the edge type of each graph type, if available. - * - * @tparam G The graph type. - * @param g A graph instance. - * @param uv A edge instance. - * @return The value associated with edge uv. -*/ -template -auto edge_value(G&& g, edge_reference_t uv) -> decltype(tag_invoke::edge_value(g, uv)) { - return tag_invoke::edge_value(g, uv); + template + concept _Has_ref_member = requires(_G&& __g, edge_reference_t<_G> uv) { + { _Fake_copy_init(uv.edge_value(__g)) }; + }; + template + 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 + }; + + 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>().edge_value(declval>())))}; + } else if constexpr (_Has_ref_ADL<_G, _UnCV>) { + return { + _St_ref::_Non_member, + noexcept(_Fake_copy_init(edge_value(declval<_G>(), declval>())))}; // intentional ADL + } 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, uv)) + * + * @tparam G The graph type. + * @param g A graph instance. + * @param uv A vertex instance. + * @return The number of outgoing edges of vertex uv. + */ + 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) { + constexpr _St_ref _Strat_ref = _Choice_ref<_G&>._Strategy; + + if constexpr (_Strat_ref == _St_ref::_Member) { + return uv.edge_value(__g); + } else if constexpr (_Strat_ref == _St_ref::_Non_member) { + return edge_value(__g, uv); // intentional ADL + } else { + static_assert(_Always_false<_G>, "edge_value(g,uv) must be defined for the graph"); + } + } + }; +} // namespace _Edge_value + +inline namespace _Cpos { + inline constexpr _Edge_value::_Cpo edge_value; } // edge value types From f31acb110e564b2aea51277963f9219cd79f5e34 Mon Sep 17 00:00:00 2001 From: Phil Ratzloff Date: Sun, 12 Nov 2023 16:54:42 -0500 Subject: [PATCH 04/11] Replace old tag_invoke-based source_id with new style CPO --- include/graph/algorithm/mst.hpp | 2 +- include/graph/container/dynamic_graph.hpp | 2 +- include/graph/container/utility_edgelist.hpp | 5 +- include/graph/detail/graph_cpo.hpp | 177 +++++++++++++++---- 4 files changed, 149 insertions(+), 37 deletions(-) diff --git a/include/graph/algorithm/mst.hpp b/include/graph/algorithm/mst.hpp index e7c6622..8bee2e2 100644 --- a/include/graph/algorithm/mst.hpp +++ b/include/graph/algorithm/mst.hpp @@ -120,7 +120,7 @@ void kruskal(E&& e, // graph auto outer_compare = [&](auto&& i, auto&& j) { return compare(std::get<2>(i), std::get<2>(j)); }; std::sort(e.begin(), e.end(), outer_compare); - VId N = e.max_vid() + 1; + VId N = static_cast(e.max_vid() + 1); std::vector> subsets(N); for (VId uid = 0; uid < N; ++uid) { subsets[uid].first = uid; diff --git a/include/graph/container/dynamic_graph.hpp b/include/graph/container/dynamic_graph.hpp index 461fcc3..b7f98e7 100644 --- a/include/graph/container/dynamic_graph.hpp +++ b/include/graph/container/dynamic_graph.hpp @@ -296,7 +296,7 @@ class dynamic_edge_source { private: // source_id(g,uv), source(g) friend constexpr vertex_id_type - tag_invoke(::std::graph::tag_invoke::source_id_fn_t, const graph_type& g, const edge_type& uv) noexcept { + source_id(const graph_type& g, const edge_type& uv) noexcept { return uv.source_id_; } friend constexpr vertex_type& diff --git a/include/graph/container/utility_edgelist.hpp b/include/graph/container/utility_edgelist.hpp index c5f250c..c516231 100644 --- a/include/graph/container/utility_edgelist.hpp +++ b/include/graph/container/utility_edgelist.hpp @@ -76,10 +76,7 @@ class utility_edgelist { return el.storage_; } - friend constexpr VSourceId& - tag_invoke(::std::graph::edgelist::tag_invoke::source_id_fn_t, utility_edgelist& el, value_type& e) { - return std::get<0>(e); - } + friend constexpr VSourceId& source_id(utility_edgelist& el, value_type& e) { return std::get<0>(e); } friend constexpr EV& edge_value(utility_edgelist& el, value_type& e) { return std::get<2>(e); } diff --git a/include/graph/detail/graph_cpo.hpp b/include/graph/detail/graph_cpo.hpp index a00fc2a..bb0efb2 100644 --- a/include/graph/detail/graph_cpo.hpp +++ b/include/graph/detail/graph_cpo.hpp @@ -599,28 +599,78 @@ auto&& target(G&& g, edge_reference_t uv) { // // source_id(g,uv) -> vertex_id_t (optional; only when a source_id exists on an edge) // -namespace tag_invoke { - TAG_INVOKE_DEF(source_id); -} +namespace _EL_Source_id { +# if defined(__clang__) || defined(__EDG__) // TRANSITION, VSO-1681199 + void source_id() = delete; // Block unqualified name lookup +# else // ^^^ no workaround / workaround vvv + void source_id(); +# endif // ^^^ workaround ^^^ -/** - * @brief Get the source vertex id of an edge. - * - * Complexity: O(1) - * - * Default implementation: n/a. This must be specialized for each graph type, if available. - * - * Not all graphs support a source id on an edge. The existance of @c source_id(g,uv) function - * for a graph type G determines if it is considered a "sourced" edge or not. - * - * @tparam G The graph type. - * @param g A graph instance. - * @param uv An edge reference. - * @return The source vertex id. -*/ -template -auto source_id(G&& g, edge_reference_t uv) -> decltype(tag_invoke::source_id(g, uv)) { - return tag_invoke::source_id(g, uv); + template + concept _Has_ref_member = requires(_G&& __g, edge_reference_t<_G> uv) { + { _Fake_copy_init(uv.source_id(__g)) }; + }; + template + 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 + }; + + 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>().source_id(declval<_G>())))}; + } else if constexpr (_Has_ref_ADL<_G, _UnCV>) { + return { + _St_ref::_Non_member, + noexcept(_Fake_copy_init(source_id(declval<_G>(), declval>())))}; // intentional ADL + } 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, uv)) + * + * @tparam G The graph type. + * @param g A graph instance. + * @param uv A vertex instance. + * @return The number of outgoing edges of vertex uv. + */ + 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) { + constexpr _St_ref _Strat_ref = _Choice_ref<_G&>._Strategy; + + if constexpr (_Strat_ref == _St_ref::_Member) { + return uv.source_id(__g); + } else if constexpr (_Strat_ref == _St_ref::_Non_member) { + return source_id(__g, uv); // intentional ADL + } else { + static_assert(_Always_false<_G>, "source_id(g,uv) or g.source_id(uv) is not defined"); + } + } + }; +} // namespace _Source_id + +inline namespace _Cpos { + inline constexpr _EL_Source_id::_Cpo source_id; } // @@ -1239,11 +1289,11 @@ using vertex_value_t = decltype(vertex_value(declval(), declval = decltype(edge_value(g,uv)) // namespace _Edge_value { -# if defined(__clang__) || defined(__EDG__) // TRANSITION, VSO-1681199 - void edge_value() = delete; // Block unqualified name lookup -# else // ^^^ no workaround / workaround vvv +# if defined(__clang__) || defined(__EDG__) // TRANSITION, VSO-1681199 + void edge_value() = delete; // Block unqualified name lookup +# else // ^^^ no workaround / workaround vvv void edge_value(); -# endif // ^^^ workaround ^^^ +# endif // ^^^ workaround ^^^ template concept _Has_ref_member = requires(_G&& __g, edge_reference_t<_G> uv) { @@ -1416,13 +1466,78 @@ namespace edgelist { template using edge_reference_t = ranges::range_reference_t>; // edge reference type - namespace tag_invoke { - TAG_INVOKE_DEF(source_id); - } + namespace _Source_id { +# if defined(__clang__) || defined(__EDG__) // TRANSITION, VSO-1681199 + void source_id() = delete; // Block unqualified name lookup +# else // ^^^ no workaround / workaround vvv + void source_id(); +# endif // ^^^ workaround ^^^ - template - auto source_id(EL&& el, edge_reference_t uv) { - return tag_invoke::source_id(el, uv); + template + concept _Has_ref_member = requires(_G&& __g, edge_reference_t<_G> uv) { + { _Fake_copy_init(uv.source_id(__g)) }; + }; + template + 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 + }; + + 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>().source_id(declval<_G>())))}; + } else if constexpr (_Has_ref_ADL<_G, _UnCV>) { + return {_St_ref::_Non_member, noexcept(_Fake_copy_init(source_id( + declval<_G>(), declval>())))}; // intentional ADL + } 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, uv)) + * + * @tparam G The graph type. + * @param g A graph instance. + * @param uv A vertex instance. + * @return The number of outgoing edges of vertex uv. + */ + 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) { + constexpr _St_ref _Strat_ref = _Choice_ref<_G&>._Strategy; + + if constexpr (_Strat_ref == _St_ref::_Member) { + return uv.source_id(__g); + } else if constexpr (_Strat_ref == _St_ref::_Non_member) { + return source_id(__g, uv); // intentional ADL + } else { + static_assert(_Always_false<_G>, "source_id(g,uv) or g.source_id(uv) is not defined"); + } + } + }; + } // namespace _Source_id + + inline namespace _Cpos { + inline constexpr _Source_id::_Cpo source_id; } template From c90f7d53951b6788dd79d3b17530fddb9fda833a Mon Sep 17 00:00:00 2001 From: Phil Ratzloff Date: Sun, 12 Nov 2023 18:08:27 -0500 Subject: [PATCH 05/11] Replace old tag_invoke-based find_vertex_edge with new style CPO --- include/graph/container/dynamic_graph.hpp | 13 +- include/graph/detail/graph_cpo.hpp | 217 +++++++++++++++------- 2 files changed, 159 insertions(+), 71 deletions(-) diff --git a/include/graph/container/dynamic_graph.hpp b/include/graph/container/dynamic_graph.hpp index b7f98e7..4a33bf6 100644 --- a/include/graph/container/dynamic_graph.hpp +++ b/include/graph/container/dynamic_graph.hpp @@ -295,10 +295,7 @@ class dynamic_edge_source { private: // source_id(g,uv), source(g) - friend constexpr vertex_id_type - source_id(const graph_type& g, const edge_type& uv) noexcept { - return uv.source_id_; - } + friend constexpr vertex_id_type source_id(const graph_type& g, const edge_type& uv) noexcept { return uv.source_id_; } friend constexpr vertex_type& tag_invoke(::std::graph::tag_invoke::source_fn_t, graph_type& g, edge_type& uv) noexcept { return begin(vertices(g))[uv.source_id_]; @@ -694,13 +691,11 @@ class dynamic_vertex_base { } friend constexpr typename edges_type::iterator - tag_invoke(::std::graph::tag_invoke::find_vertex_edge_fn_t, graph_type& g, vertex_id_type uid, vertex_id_type vid) { + find_vertex_edge(graph_type& g, vertex_id_type uid, vertex_id_type vid) { return ranges::find(g[uid].edges_, [&g, &vid](const edge_type& uv) -> bool { return target_id(g, uv) == vid; }); } - friend constexpr typename edges_type::const_iterator tag_invoke(::std::graph::tag_invoke::find_vertex_edge_fn_t, - const graph_type& g, - vertex_id_type uid, - vertex_id_type vid) { + friend constexpr typename edges_type::const_iterator + find_vertex_edge(const graph_type& g, vertex_id_type uid, vertex_id_type vid) { return ranges::find(g[uid].edges_, [&g, &vid](const edge_type& uv) -> bool { return target_id(g, uv) == vid; }); } }; diff --git a/include/graph/detail/graph_cpo.hpp b/include/graph/detail/graph_cpo.hpp index bb0efb2..bab1c8a 100644 --- a/include/graph/detail/graph_cpo.hpp +++ b/include/graph/detail/graph_cpo.hpp @@ -600,11 +600,11 @@ auto&& target(G&& g, edge_reference_t uv) { // source_id(g,uv) -> vertex_id_t (optional; only when a source_id exists on an edge) // namespace _EL_Source_id { -# if defined(__clang__) || defined(__EDG__) // TRANSITION, VSO-1681199 - void source_id() = delete; // Block unqualified name lookup -# else // ^^^ no workaround / workaround vvv +# if defined(__clang__) || defined(__EDG__) // TRANSITION, VSO-1681199 + void source_id() = delete; // Block unqualified name lookup +# else // ^^^ no workaround / workaround vvv void source_id(); -# endif // ^^^ workaround ^^^ +# endif // ^^^ workaround ^^^ template concept _Has_ref_member = requires(_G&& __g, edge_reference_t<_G> uv) { @@ -667,7 +667,7 @@ namespace _EL_Source_id { } } }; -} // namespace _Source_id +} // namespace _EL_Source_id inline namespace _Cpos { inline constexpr _EL_Source_id::_Cpo source_id; @@ -773,65 +773,158 @@ using edge_id_t = decltype(edge_id(declval(), // find_vertex_edge(g,uid,vid) -> vertex_edge_iterator // default = find_vertex_edge(g,*find_vertex(g,uid),vid) // -namespace tag_invoke { - TAG_INVOKE_DEF(find_vertex_edge); +namespace _Find_vertex_edge { +# if defined(__clang__) || defined(__EDG__) // TRANSITION, VSO-1681199 + void find_vertex_edge() = delete; // Block unqualified name lookup +# else // ^^^ no workaround / workaround vvv + void find_vertex_edge(); +# endif // ^^^ workaround ^^^ - template - concept _has_find_vertex_edge_adl = requires(G&& g, vertex_id_t uid, vertex_id_t vid, vertex_reference_t u) { - { find_vertex_edge(g, u, vid) }; + template + concept _Has_ref_member = requires(_G&& __g, vertex_reference_t<_G> u, const vertex_id_t<_G>& vid) { + { _Fake_copy_init(u.find_vertex_edge(__g, vid)) }; }; - template - concept _has_find_vertex_id_edge_adl = - requires(G&& g, vertex_id_t uid, vertex_id_t vid, vertex_reference_t u) { - { find_vertex_edge(g, uid, vid) }; - }; -} // namespace tag_invoke -/** - * @brief Find an edge of a vertex. - * - * Complexity: O(E), where |E| is the number of outgoing edges of vertex u - * - * Default implementation: find_if(edges(g, u), [&g, &vid](auto&& uv) { return target_id(g, uv) == vid; }) - * - * @tparam G The graph type. - * @param g A graph instance. - * @param u A vertex instance. - * @param vid A target vertex id. - * @return An edge iterator of an outgoing edge of u with a target id of vid. end(edges(g,u)) will - * be returned if an edge doesn't exist. -*/ -template -auto find_vertex_edge(G&& g, vertex_reference_t u, vertex_id_t vid) { - if constexpr (tag_invoke::_has_find_vertex_edge_adl) - return tag_invoke::find_vertex_edge(g, u, vid); - else - return ranges::find_if(edges(g, u), [&g, &vid](auto&& uv) { return target_id(g, uv) == vid; }); -} + template + 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 + }; -/** - * @brief Find an edge in the graph. - * - * Complexity: O(E), where |E| is the number of outgoing edges of vertex u - * - * Default implementation: find_vertex_edge(g, *(begin(vertices(g)) + uid), vid) - * - * @tparam G The graph type. - * @param g A graph instance. - * @param uid The vertex source id of the edge. - * @param vid The vertex target id of the edge. - * @return An edge iterator of an outgoing edge of u with a target id of vid. end(edges(g,uid)) will - * be returned if an edge doesn't exist. -*/ -template -requires tag_invoke::_has_find_vertex_id_edge_adl || ranges::random_access_range> -auto find_vertex_edge(G&& g, vertex_id_t uid, vertex_id_t vid) { - if constexpr (tag_invoke::_has_find_vertex_id_edge_adl) - return tag_invoke::find_vertex_edge(g, uid, vid); - else if constexpr (ranges::random_access_range>) - find_vertex_edge(g, *(begin(vertices(g)) + uid), vid); + template + concept _Can_ref_eval = requires(_G&& __g, vertex_reference_t<_G> u) { + { _Fake_copy_init(edges(__g, u)) }; + }; + + template + 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 + }; + + template + concept _Can_id_eval = requires(_G&& __g, vertex_id_t<_G> uid) { + { _Fake_copy_init(edges(__g, uid)) }; + }; + + class _Cpo { + private: + enum class _St_ref { _None, _Member, _Non_member, _Auto_eval }; + enum class _St_id { _None, _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>().find_vertex_edge( + declval<_G>(), declval>())))}; + } else if constexpr (_Has_ref_ADL<_G, _UnCV>) { + return {_St_ref::_Non_member, + noexcept(_Fake_copy_init(find_vertex_edge(declval<_G>(), declval>(), + declval>())))}; // intentional ADL + } else if constexpr (_Can_ref_eval<_G, _UnCV>) { + using fnc_find_t = decltype([](edge_reference_t<_G>) -> bool { return true; }); + return {_St_ref::_Auto_eval, + noexcept(_Fake_copy_init(ranges::find_if(edges(declval<_G>(), declval>()), + declval())))}; // intentional ADL + } else { + return {_St_ref::_None}; + } + } + + template + static constexpr _Choice_t<_St_ref> _Choice_ref = _Choose_ref<_G>(); + + 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(find_vertex_edge(declval<_G>(), declval>(), + declval>())))}; // intentional ADL + } else if constexpr (_Can_id_eval<_G, _UnCV>) { + using fnc_find_t = decltype([](edge_reference_t<_G>) -> bool { return true; }); + return {_St_id::_Auto_eval, + noexcept(_Fake_copy_init(ranges::find_if(edges(declval<_G>(), declval>()), + declval())))}; // intentional ADL + } else { + return {_St_id::_None}; + } + } + + template + static constexpr _Choice_t<_St_id> _Choice_id = _Choose_id<_G>(); + + public: + /** + * @brief Find an edge given a source vertex reference and target vertex id. + * + * Complexity: O(e), where e is the number of outgoing edges of vertex u + * + * Default implementation: find_if(edges(g, u), [&g, &vid](auto&& uv) { return target_id(g, uv) == vid; }) + * + * @tparam G The graph type. + * @param g A graph instance. + * @param u Source vertex + * @param vid Target vertex id. + * @return An iterator to the edge if it exists, or end(edges(g,u)) if it doesn't exist. + */ + template + [[nodiscard]] constexpr auto operator()(_G&& __g, vertex_reference_t<_G> u, const vertex_id_t<_G>& vid) const + noexcept(_Choice_ref<_G&>._No_throw) { + constexpr _St_ref _Strat = _Choice_ref<_G&>._Strategy; + + if constexpr (_Strat == _St_ref::_Member) { + return u.find_vertex_edge(__g, vid); + } else if constexpr (_Strat == _St_ref::_Non_member) { + return find_vertex_edge(__g, u, vid); // intentional ADL + } else if constexpr (_Strat == _St_ref::_Auto_eval) { + return ranges::find_if(edges(__g, u), [&__g, &vid](auto&& uv) { return target_id(__g, uv) == vid; }); + } else { + static_assert(_Always_false<_G>, + "find_vertex_edge(g,uid) has not been defined and the default implemenation cannot be evaluated"); + } + } + + /** + * @brief Find an edge given a source and target vertex ids. + * + * Complexity: O(e), where e is the number of outgoing edges of vertex uid + * + * Default implementation: find_if(edges(g, uid), [&g, &vid](auto&& uv) { return target_id(g, uv) == vid; }) + * + * @tparam G The graph type. + * @param g A graph instance. + * @param u Source vertex + * @param vid Target vertex id. + * @return An iterator to the edge if it exists, or end(edges(g,u)) if it doesn't exist. + */ + template + [[nodiscard]] constexpr auto operator()(_G&& __g, vertex_id_t<_G> uid, const vertex_id_t<_G>& vid) const + noexcept(_Choice_id<_G&>._No_throw) { + constexpr _St_id _Strat = _Choice_id<_G&>._Strategy; + + if constexpr (_Strat == _St_id::_Non_member) { + return find_vertex_edge(__g, uid, uid); // intentional ADL + } else if constexpr (_Strat == _St_id::_Auto_eval) { + return ranges::find_if(edges(__g, uid), [&__g, &vid](auto&& uv) { return target_id(__g, uv) == vid; }); + } else { + static_assert(_Always_false<_G>, + "find_vertex_edge(g,uid) has not been defined and the default implemenation cannot be evaluated"); + } + } + }; +} // namespace _Find_vertex_edge + +inline namespace _Cpos { + inline constexpr _Find_vertex_edge::_Cpo find_vertex_edge; } + // // contains_edge(g,uid,vid) -> bool // default = uid < size(vertices(g)) && vid < size(vertices(g)), if adjacency_matrix @@ -1467,11 +1560,11 @@ namespace edgelist { using edge_reference_t = ranges::range_reference_t>; // edge reference type namespace _Source_id { -# if defined(__clang__) || defined(__EDG__) // TRANSITION, VSO-1681199 - void source_id() = delete; // Block unqualified name lookup -# else // ^^^ no workaround / workaround vvv +# if defined(__clang__) || defined(__EDG__) // TRANSITION, VSO-1681199 + void source_id() = delete; // Block unqualified name lookup +# else // ^^^ no workaround / workaround vvv void source_id(); -# endif // ^^^ workaround ^^^ +# endif // ^^^ workaround ^^^ template concept _Has_ref_member = requires(_G&& __g, edge_reference_t<_G> uv) { From 482cffe1ce948c3714475d2c9c02a1703d8fccee Mon Sep 17 00:00:00 2001 From: Phil Ratzloff Date: Mon, 13 Nov 2023 19:41:22 -0500 Subject: [PATCH 06/11] Replace old tag_invoke-based edge_value with new style CPO --- include/graph/detail/graph_cpo.hpp | 23 ++++++++++++----------- 1 file changed, 12 insertions(+), 11 deletions(-) diff --git a/include/graph/detail/graph_cpo.hpp b/include/graph/detail/graph_cpo.hpp index bab1c8a..77c65ee 100644 --- a/include/graph/detail/graph_cpo.hpp +++ b/include/graph/detail/graph_cpo.hpp @@ -774,11 +774,11 @@ using edge_id_t = decltype(edge_id(declval(), // default = find_vertex_edge(g,*find_vertex(g,uid),vid) // namespace _Find_vertex_edge { -# if defined(__clang__) || defined(__EDG__) // TRANSITION, VSO-1681199 - void find_vertex_edge() = delete; // Block unqualified name lookup -# else // ^^^ no workaround / workaround vvv +# if defined(__clang__) || defined(__EDG__) // TRANSITION, VSO-1681199 + void find_vertex_edge() = delete; // Block unqualified name lookup +# else // ^^^ no workaround / workaround vvv void find_vertex_edge(); -# endif // ^^^ workaround ^^^ +# endif // ^^^ workaround ^^^ template concept _Has_ref_member = requires(_G&& __g, vertex_reference_t<_G> u, const vertex_id_t<_G>& vid) { @@ -815,7 +815,7 @@ namespace _Find_vertex_edge { template [[nodiscard]] static consteval _Choice_t<_St_ref> _Choose_ref() noexcept { static_assert(is_lvalue_reference_v<_G>); - using _UnCV = remove_cvref_t<_G>; + using _UnCV = remove_cvref_t<_G>; if constexpr (_Has_ref_member<_G, _UnCV>) { return {_St_ref::_Member, noexcept(_Fake_copy_init(declval>().find_vertex_edge( @@ -840,7 +840,7 @@ namespace _Find_vertex_edge { template [[nodiscard]] static consteval _Choice_t<_St_id> _Choose_id() noexcept { static_assert(is_lvalue_reference_v<_G>); - using _UnCV = remove_cvref_t<_G>; + using _UnCV = remove_cvref_t<_G>; if constexpr (_Has_id_ADL<_G, _UnCV>) { return {_St_id::_Non_member, @@ -1382,18 +1382,18 @@ using vertex_value_t = decltype(vertex_value(declval(), declval = decltype(edge_value(g,uv)) // namespace _Edge_value { -# if defined(__clang__) || defined(__EDG__) // TRANSITION, VSO-1681199 - void edge_value() = delete; // Block unqualified name lookup -# else // ^^^ no workaround / workaround vvv +# if defined(__clang__) || defined(__EDG__) // TRANSITION, VSO-1681199 + void edge_value() = delete; // Block unqualified name lookup +# else // ^^^ no workaround / workaround vvv void edge_value(); -# endif // ^^^ workaround ^^^ +# endif // ^^^ workaround ^^^ template concept _Has_ref_member = requires(_G&& __g, edge_reference_t<_G> uv) { { _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 }; @@ -1456,6 +1456,7 @@ inline namespace _Cpos { inline constexpr _Edge_value::_Cpo edge_value; } + // edge value types template using edge_value_t = decltype(edge_value(declval(), declval>())); From dadbda0ca32f68ca55a95fd6278c3029fb13a799 Mon Sep 17 00:00:00 2001 From: Phil Ratzloff Date: Mon, 13 Nov 2023 21:11:58 -0500 Subject: [PATCH 07/11] Replace old tag_invoke-based edge_value with new style CPO edge_id(g,uv) now returns edge_descriptor,true,void,void> instead of pair, vertex_id_t> for consistancy with the rest of the libary. Move descriptor classes into new graph_descriptors.hpp --- include/graph/detail/graph_cpo.hpp | 134 +++++++++++++------ include/graph/graph.hpp | 1 + include/graph/graph_descriptors.hpp | 194 ++++++++++++++++++++++++++++ include/graph/graph_utility.hpp | 191 --------------------------- 4 files changed, 287 insertions(+), 233 deletions(-) create mode 100644 include/graph/graph_descriptors.hpp diff --git a/include/graph/detail/graph_cpo.hpp b/include/graph/detail/graph_cpo.hpp index 77c65ee..2eb3187 100644 --- a/include/graph/detail/graph_cpo.hpp +++ b/include/graph/detail/graph_cpo.hpp @@ -720,50 +720,100 @@ auto&& source(G&& g, edge_reference_t uv) { } // -// edge_id(g,uv) -> pair,vertex_id_t> -// default = pair(source_id(g,uv),target_id(g,uv)) +// edge_id(g,uv) -> edge_id_t +// default = edge_id_t(source_id(g,uv),target_id(g,uv)) // -// edge_id_t = decltype(edge_id(g,uv)) +// edge_id_t = edge_descriptor, true, void, void> // -namespace tag_invoke { - TAG_INVOKE_DEF(edge_id); +template +using edge_id_t = edge_descriptor, true, void, void>; // {source_id, target_id} - template - concept _has_edge_id_adl = requires(G&& g, ranges::range_reference_t uv) { - { edge_id(g, uv) }; +namespace _Edge_id { +# if defined(__clang__) || defined(__EDG__) // TRANSITION, VSO-1681199 + void edge_id() = delete; // Block unqualified name lookup +# else // ^^^ no workaround / workaround vvv + void edge_id(); +# endif // ^^^ workaround ^^^ + + template + concept _Has_ref_member = requires(_G&& __g, edge_reference_t<_G> uv) { + { uv.edge_id(__g) } -> convertible_to>; }; -} // namespace tag_invoke + template + 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 + }; -template -concept _can_eval_edge_id = requires(G&& g, ranges::range_reference_t uv) { - { target_id(g, uv) }; - { source_id(g, uv) }; -}; + template + concept _Can_id_eval = _Has_class_or_enum_type<_G> // + && requires(_G&& __g, edge_reference_t<_G> uv) { + { _Fake_copy_init(edge_id_t<_G>{source_id(__g, uv), target_id(__g, uv)}) }; + }; + class _Cpo { + private: + enum class _St_ref { _None, _Member, _Non_member, _Auto_eval }; -/** - * @brief Get the edge id of an edge. - * - * Complexity: O(1) - * - * Default implementation: pair(source_id(g, uv), target_id(g, uv)) - * - * @tparam G The graph type. - * @param g A graph instance. - * @param uv An edge reference. - * @return The edge id as a pair of vertex id's. -*/ -template -requires tag_invoke::_has_edge_id_adl> || _can_eval_edge_id> -auto edge_id(G&& g, edge_reference_t uv) { - if constexpr (tag_invoke::_has_edge_id_adl>) - return tag_invoke::edge_id(g, uv); - else if constexpr (_can_eval_edge_id>) - return pair(source_id(g, uv), target_id(g, uv)); -} + template + [[nodiscard]] static consteval _Choice_t<_St_ref> _Choose_ref() noexcept { + static_assert(is_lvalue_reference_v<_G>); + using _UnCV = remove_cvref_t<_G>; -template -using edge_id_t = decltype(edge_id(declval(), - declval>())); // e.g. pair,vertex_id_t> + if constexpr (_Has_ref_member<_G, _UnCV>) { + return {_St_ref::_Member, noexcept(_Fake_copy_init(declval>().edge_id(declval<_G>())))}; + } else if constexpr (_Has_ref_ADL<_G, _UnCV>) { + return {_St_ref::_Non_member, + noexcept(_Fake_copy_init(edge_id(declval<_G>(), declval>())))}; // intentional ADL + } else if constexpr (_Can_id_eval<_G, _UnCV>) { + return {_St_ref::_Auto_eval, + noexcept(_Fake_copy_init(edge_id_t<_G>{source_id(declval<_G>(), declval>()), + target_id(declval<_G>(), declval>())}))}; + } else { + return {_St_ref::_None}; + } + } + + template + static constexpr _Choice_t<_St_ref> _Choice_ref = _Choose_ref<_G>(); + + public: + /** + * @brief The id of an edge, made from its source_id and target_id. + * + * Complexity: O(1) + * + * Default implementation: + * edge_descriptor,true>{source_id(g,uv), target_id(g,uv)} + * given that source_id(g,uv) is defined. + * + * @tparam G The graph type. + * @param g A graph instance. + * @param uv An edge reference. + * @return An edge_descriptor with the source_id and target_id. + */ + 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) { + constexpr _St_ref _Strat_ref = _Choice_ref<_G&>._Strategy; + + if constexpr (_Strat_ref == _St_ref::_Member) { + return uv.edge_id(__g); + } else if constexpr (_Strat_ref == _St_ref::_Non_member) { + return edge_id(__g, uv); // intentional ADL + } else if constexpr (_Strat_ref == _St_ref::_Auto_eval) { + return edge_id_t<_G>{source_id(__g, uv), target_id(__g, uv)}; + } else { + static_assert(_Always_false<_G>, + "edge_id(g,uv) is not defined, or target_id(g,uv) and source_id(g,uv) are not defined"); + } + } + }; +} // namespace _Edge_id + +inline namespace _Cpos { + inline constexpr _Edge_id::_Cpo edge_id; +} // @@ -1382,18 +1432,18 @@ using vertex_value_t = decltype(vertex_value(declval(), declval = decltype(edge_value(g,uv)) // namespace _Edge_value { -# if defined(__clang__) || defined(__EDG__) // TRANSITION, VSO-1681199 - void edge_value() = delete; // Block unqualified name lookup -# else // ^^^ no workaround / workaround vvv +# if defined(__clang__) || defined(__EDG__) // TRANSITION, VSO-1681199 + void edge_value() = delete; // Block unqualified name lookup +# else // ^^^ no workaround / workaround vvv void edge_value(); -# endif // ^^^ workaround ^^^ +# endif // ^^^ workaround ^^^ template concept _Has_ref_member = requires(_G&& __g, edge_reference_t<_G> uv) { { _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 }; diff --git a/include/graph/graph.hpp b/include/graph/graph.hpp index 9cac1a7..c0d500d 100644 --- a/include/graph/graph.hpp +++ b/include/graph/graph.hpp @@ -22,6 +22,7 @@ #include #include +#include "graph_descriptors.hpp" #include "detail/graph_cpo.hpp" diff --git a/include/graph/graph_descriptors.hpp b/include/graph/graph_descriptors.hpp new file mode 100644 index 0000000..0258bdc --- /dev/null +++ b/include/graph/graph_descriptors.hpp @@ -0,0 +1,194 @@ +#pragma once + +namespace std::graph { +// +// vertex_descriptor +// for(auto&& [uid, u] : vertexlist(g)) +// for(auto&& [uid, u, value] : vertexlist(g, [](vertex_reference_t u) { return ...; } ) +// +template +struct vertex_descriptor { + VId id; + V vertex; + VV value; +}; +template +struct vertex_descriptor { + VId id; + V vertex; +}; +template +struct vertex_descriptor { + VId id; + VV value; +}; +template +struct vertex_descriptor { + VId id; +}; + +template +using copyable_vertex_t = vertex_descriptor; // {id, value} + +// +// edge_descriptor +// +// for(auto&& [target_id, uv] : incidence(g,u)) +// for(auto&& [target_id, uv, value] : incidence(g,u, [](edge_reference_t uv) { return ...; }) +// +// for(auto&& [source_id, target_id, uv] : incidence(g,u)) +// for(auto&& [source_id, target_id, uv, value] : incidence(g,u, [](edge_reference_t uv) { return ...; }) +// +template +struct edge_descriptor { + VId source_id; + VId target_id; + E edge; + EV value; +}; + +template +struct edge_descriptor { + VId source_id; + VId target_id; + E edge; +}; +template +struct edge_descriptor { + VId source_id; + VId target_id; +}; +template +struct edge_descriptor { + VId source_id; + VId target_id; + EV value; +}; + +template +struct edge_descriptor { + VId target_id; + E edge; + EV value; +}; +template +struct edge_descriptor { + VId target_id; + E edge; +}; + +template +struct edge_descriptor { + VId target_id; + EV value; +}; +template +struct edge_descriptor { + VId target_id; +}; + +// +// targeted_edge +// for(auto&& [vid,uv,value] : edges_view(g, u, [](vertex_edge_reference_t uv) { return ...; } ) +// for(auto&& [vid,uv] : edges_view(g, u) ) +// +//template +//using targeted_edge = edge_descriptor; // {target_id, edge, [, value]} + +// +// sourced_edge +// for(auto&& [uid,vid,uv,value] : sourced_edges_view(g, u, [](vertex_edge_reference_t uv) { return ...; } ) +// for(auto&& [uid,vid,uv] : sourced_edges_view(g, u) ) +// +//template +//using sourced_edge = edge_descriptor; // {source_id, target_id, edge, [, value]} + +// +// edgelist_edge +// for(auto&& [uid,vid,uv,value] : edges_view(g, [](vertex_edge_reference_t g) { return ...; } ) +// for(auto&& [uid,vid,uv] : edges_view(g) ) +// +template +using edgelist_edge = edge_descriptor; // {source_id, target_id [, edge] [, value]} + +// +// copyable_edge_t +// +template +using copyable_edge_t = edge_descriptor; // {source_id, target_id [, value]} + +// +// neighbor_descriptor (for adjacency) +// +template +struct neighbor_descriptor { + VId source_id; + VId target_id; + V target; + VV value; +}; + +template +struct neighbor_descriptor { + VId target_id; + V target; + VV value; +}; + +template +struct neighbor_descriptor { + VId target_id; + V target; +}; + +template +struct neighbor_descriptor { + VId target_id; + VV value; +}; + +template +struct neighbor_descriptor { + VId target_id; +}; + +template +struct neighbor_descriptor { + VId source_id; + VId target_id; + V target; +}; + +template +struct neighbor_descriptor { + VId source_id; + VId target_id; + VV value; +}; + +template +struct neighbor_descriptor { + VId source_id; + VId target_id; +}; + +// +// view concepts +// +template +concept copyable_vertex = convertible_to>; + +template +concept copyable_edge = convertible_to>; + +// +// is_sourced +// +template +inline constexpr bool is_sourced_v = false; +template +inline constexpr bool is_sourced_v> = true; +template +inline constexpr bool is_sourced_v> = true; + +} diff --git a/include/graph/graph_utility.hpp b/include/graph/graph_utility.hpp index cf0072f..d1d2a6e 100644 --- a/include/graph/graph_utility.hpp +++ b/include/graph/graph_utility.hpp @@ -7,197 +7,6 @@ enum three_colors : int8_t { black, white, grey }; // { finished, undiscovered, enum struct cancel_search : int8_t { continue_search, cancel_branch, cancel_all }; -// -// vertex_descriptor -// for(auto&& [uid, u] : vertexlist(g)) -// for(auto&& [uid, u, value] : vertexlist(g, [](vertex_reference_t u) { return ...; } ) -// -template -struct vertex_descriptor { - VId id; - V vertex; - VV value; -}; -template -struct vertex_descriptor { - VId id; - V vertex; -}; -template -struct vertex_descriptor { - VId id; - VV value; -}; -template -struct vertex_descriptor { - VId id; -}; - -template -using copyable_vertex_t = vertex_descriptor; // {id, value} - -// -// edge_descriptor -// -// for(auto&& [target_id, uv] : incidence(g,u)) -// for(auto&& [target_id, uv, value] : incidence(g,u, [](edge_reference_t uv) { return ...; }) -// -// for(auto&& [source_id, target_id, uv] : incidence(g,u)) -// for(auto&& [source_id, target_id, uv, value] : incidence(g,u, [](edge_reference_t uv) { return ...; }) -// -template -struct edge_descriptor { - VId source_id; - VId target_id; - E edge; - EV value; -}; - -template -struct edge_descriptor { - VId source_id; - VId target_id; - E edge; -}; -template -struct edge_descriptor { - VId source_id; - VId target_id; -}; -template -struct edge_descriptor { - VId source_id; - VId target_id; - EV value; -}; - -template -struct edge_descriptor { - VId target_id; - E edge; - EV value; -}; -template -struct edge_descriptor { - VId target_id; - E edge; -}; - -template -struct edge_descriptor { - VId target_id; - EV value; -}; -template -struct edge_descriptor { - VId target_id; -}; - -// -// targeted_edge -// for(auto&& [vid,uv,value] : edges_view(g, u, [](vertex_edge_reference_t uv) { return ...; } ) -// for(auto&& [vid,uv] : edges_view(g, u) ) -// -//template -//using targeted_edge = edge_descriptor; // {target_id, edge, [, value]} - -// -// sourced_edge -// for(auto&& [uid,vid,uv,value] : sourced_edges_view(g, u, [](vertex_edge_reference_t uv) { return ...; } ) -// for(auto&& [uid,vid,uv] : sourced_edges_view(g, u) ) -// -//template -//using sourced_edge = edge_descriptor; // {source_id, target_id, edge, [, value]} - -// -// edgelist_edge -// for(auto&& [uid,vid,uv,value] : edges_view(g, [](vertex_edge_reference_t g) { return ...; } ) -// for(auto&& [uid,vid,uv] : edges_view(g) ) -// -template -using edgelist_edge = edge_descriptor; // {source_id, target_id [, edge] [, value]} - -// -// copyable_edge -// -template -using copyable_edge_t = edge_descriptor; // {source_id, target_id [, value]} - -// -// neighbor_descriptor (for adjacency) -// -template -struct neighbor_descriptor { - VId source_id; - VId target_id; - V target; - VV value; -}; - -template -struct neighbor_descriptor { - VId target_id; - V target; - VV value; -}; - -template -struct neighbor_descriptor { - VId target_id; - V target; -}; - -template -struct neighbor_descriptor { - VId target_id; - VV value; -}; - -template -struct neighbor_descriptor { - VId target_id; -}; - -template -struct neighbor_descriptor { - VId source_id; - VId target_id; - V target; -}; - -template -struct neighbor_descriptor { - VId source_id; - VId target_id; - VV value; -}; - -template -struct neighbor_descriptor { - VId source_id; - VId target_id; -}; - -// -// view concepts -// -template -concept copyable_vertex = convertible_to>; - -template -concept copyable_edge = convertible_to>; - -// -// is_sourced -// -template -inline constexpr bool is_sourced_v = false; -template -inline constexpr bool is_sourced_v> = true; -template -inline constexpr bool is_sourced_v> = true; - - template class source_vertex { public: From 81af07662deab9437b8c5300189d5d18f298380e Mon Sep 17 00:00:00 2001 From: Phil Ratzloff Date: Mon, 13 Nov 2023 22:19:56 -0500 Subject: [PATCH 08/11] Replace old tag_invoke-based contains_edge(g,uid,vid) with new style CPO --- include/graph/detail/graph_cpo.hpp | 160 ++++++++++++++++------------- 1 file changed, 91 insertions(+), 69 deletions(-) diff --git a/include/graph/detail/graph_cpo.hpp b/include/graph/detail/graph_cpo.hpp index 2eb3187..0e402ae 100644 --- a/include/graph/detail/graph_cpo.hpp +++ b/include/graph/detail/graph_cpo.hpp @@ -729,11 +729,11 @@ template using edge_id_t = edge_descriptor, true, void, void>; // {source_id, target_id} namespace _Edge_id { -# if defined(__clang__) || defined(__EDG__) // TRANSITION, VSO-1681199 - void edge_id() = delete; // Block unqualified name lookup -# else // ^^^ no workaround / workaround vvv +# if defined(__clang__) || defined(__EDG__) // TRANSITION, VSO-1681199 + void edge_id() = delete; // Block unqualified name lookup +# else // ^^^ no workaround / workaround vvv void edge_id(); -# endif // ^^^ workaround ^^^ +# endif // ^^^ workaround ^^^ template concept _Has_ref_member = requires(_G&& __g, edge_reference_t<_G> uv) { @@ -980,42 +980,99 @@ inline namespace _Cpos { // default = uid < size(vertices(g)) && vid < size(vertices(g)), if adjacency_matrix // = find_vertex_edge(g,uid) != ranges::end(edges(g,uid)); // -namespace tag_invoke { - TAG_INVOKE_DEF(contains_edge); +namespace _Contains_edge { +# if defined(__clang__) || defined(__EDG__) // TRANSITION, VSO-1681199 + void contains_edge() = delete; // Block unqualified name lookup +# else // ^^^ no workaround / workaround vvv + void contains_edge(); +# endif // ^^^ workaround ^^^ - template - concept _has_contains_edge_adl = requires(G&& g, vertex_id_t uid, vertex_id_t vid) { - { contains_edge(g, uid, vid) }; + template + 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 + }; + + template + concept _Can_matrix_eval = _Has_class_or_enum_type<_G> && is_adjacency_matrix_v<_G> // + && ranges::sized_range>; + + template + concept _Can_id_eval = _Has_class_or_enum_type<_G> // + && requires(_G&& __g, vertex_reference_t<_G> u, vertex_id_t<_G> uid, vertex_id_t<_G> vid) { + { _Fake_copy_init(find_vertex(__g, uid)) }; + { _Fake_copy_init(edges(__g, u)) }; + { _Fake_copy_init(find_vertex_edge(__g, u, vid)) }; + }; + class _Cpo { + private: + enum class _St_ref { _None, _Non_member, _Matrix_eval, _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_ADL<_G, _UnCV>) { + return {_St_ref::_Non_member, + noexcept(_Fake_copy_init(contains_edge(declval<_G>(), declval>(), + declval>())))}; // intentional ADL + } else if constexpr (_Can_matrix_eval<_G, _UnCV>) { + return {_St_ref::_Matrix_eval, + noexcept(_Fake_copy_init(declval>() < ranges::size(vertices(declval<_G>()))))}; + } else if constexpr (_Can_id_eval<_G, _UnCV>) { + return {_St_ref::_Auto_eval, + noexcept(_Fake_copy_init(find_vertex_edge(declval<_G>(), declval>(), declval>()) != + declval>()))}; + } else { + return {_St_ref::_None}; + } + } + + template + static constexpr _Choice_t<_St_ref> _Choice_ref = _Choose_ref<_G>(); + + public: + /** + * @brief Does an edge exist in the graph? + * + * Complexity: O(1) + * + * Default implementation: + * uid < ranges::size(vertices(__g)) && vid < ranges::size(vertices(__g)), if is_adjacency_matrix_v<_G> + * find_vertex_edge(g, uid) != ranges::end(edges(g, uid)), otherwise + * + * @tparam G The graph type. + * @param g A graph instance. + * @param uv An edge reference. + * @return An edge_descriptor with the source_id and target_id. + */ + template + requires(_Choice_ref<_G&>._Strategy != _St_ref::_None) + [[nodiscard]] constexpr auto operator()(_G&& __g, vertex_id_t<_G>& uid, vertex_id_t<_G>& vid) const + noexcept(_Choice_ref<_G&>._No_throw) -> bool { + constexpr _St_ref _Strat_ref = _Choice_ref<_G&>._Strategy; + + if constexpr (_Strat_ref == _St_ref::_Non_member) { + return contains_edge(__g, uid, vid); // intentional ADL + } else if constexpr (_Strat_ref == _St_ref::_Matrix_eval) { + return uid < ranges::size(vertices(__g)) && vid < ranges::size(vertices(__g)); + } else if constexpr (_Strat_ref == _St_ref::_Auto_eval) { + auto ui = find_vertex(__g, uid); + return find_vertex_edge(__g, *ui, vid) != ranges::end(edges(__g, *ui)); + } else { + static_assert(_Always_false<_G>, + "contains_edge(g,uv) is not defined, or find_vertex_(g,uid) and source_id(g,uv) are not defined"); + } + } }; -} // namespace tag_invoke +} // namespace _Contains_edge -/** - * @brief Does an edge exist in the graph? - * - * Complexity: O(E), where |E| is the number of outgoing edges of vertex u - * - * Default implementation: find_vertex_edge(g, *ui) != end(edges(g, *ui)); - * - * @tparam G The graph type. - * @param g A graph instance. - * @param uid The vertex source id of the edge. - * @param vid The vertex target id of the edge. - * @return true if the edge exists, or false otherwise. -*/ -template -auto contains_edge(G&& g, vertex_id_t uid, vertex_id_t vid) { - if constexpr (tag_invoke::_has_contains_edge_adl) - return tag_invoke::contains_edge(g, uid, vid); - else if constexpr (is_adjacency_matrix_v) { - return uid < ranges::size(vertices(g)) && vid < ranges::size(vertices(g)); - } else { - auto ui = find_vertex(g, uid); - return find_vertex_edge(g, *ui) != ranges::end(edges(g, *ui)); - } +inline namespace _Cpos { + inline constexpr _Contains_edge::_Cpo contains_edge; } -# if 1 // // num_vertices(g,) -> integral default = size(vertices(g)) // num_vertices(g,pid) -> integral default = size(vertices(g,pid)) @@ -1160,41 +1217,6 @@ namespace _NumVertices { inline namespace _Cpos { inline constexpr _NumVertices::_Cpo num_vertices; } -# else -// -// num_vertices(g) -> integral -// default = size(vertices(g)) -// -namespace tag_invoke { - TAG_INVOKE_DEF(num_vertices); - - template - concept _has_num_vertices_adl = requires(G&& g, vertex_reference_t u) { - { num_vertices(g) }; - }; -} // namespace tag_invoke - -/** - * @brief The number of outgoing edges of a vertex. - * - * Complexity: O(1) - * - * Default implementation: size(vertices(g)) - * - * @tparam G The graph type. - * @param g A graph instance. - * @return The number of vertices in a graph. -*/ -template -requires tag_invoke::_has_num_vertices_adl -auto num_vertices(G&& g) { - if constexpr (tag_invoke::_has_num_vertices_adl) - return tag_invoke::num_vertices(g); - else { - return ranges::size(vertices(g)); - } -} -# endif // // degree(g,u ) -> integral default = size(edges(g,u)) if sized_range> From e69ccdf41f13e82ccc0a656171aa2668b40b862f Mon Sep 17 00:00:00 2001 From: Phil Ratzloff Date: Mon, 13 Nov 2023 22:36:26 -0500 Subject: [PATCH 09/11] Replace old tag_invoke-based target(g,uv) with new style CPO --- include/graph/container/compressed_graph.hpp | 4 +- include/graph/container/dynamic_graph.hpp | 6 +- include/graph/detail/graph_cpo.hpp | 124 +++++++++++++------ 3 files changed, 88 insertions(+), 46 deletions(-) diff --git a/include/graph/container/compressed_graph.hpp b/include/graph/container/compressed_graph.hpp index 0e10022..af33253 100644 --- a/include/graph/container/compressed_graph.hpp +++ b/include/graph/container/compressed_graph.hpp @@ -796,11 +796,11 @@ class compressed_graph_base friend constexpr vertex_id_type target_id(const graph_type& g, const edge_type& uv) noexcept { return uv.index; } friend constexpr vertex_type& - tag_invoke(::std::graph::tag_invoke::target_fn_t, graph_type& g, edge_type& uv) noexcept { + target(graph_type& g, edge_type& uv) noexcept { return g.row_index_[uv.index]; } friend constexpr const vertex_type& - tag_invoke(::std::graph::tag_invoke::target_fn_t, const graph_type& g, const edge_type& uv) noexcept { + target(const graph_type& g, const edge_type& uv) noexcept { return g.row_index_[uv.index]; } diff --git a/include/graph/container/dynamic_graph.hpp b/include/graph/container/dynamic_graph.hpp index 4a33bf6..06e44d1 100644 --- a/include/graph/container/dynamic_graph.hpp +++ b/include/graph/container/dynamic_graph.hpp @@ -235,12 +235,10 @@ class dynamic_edge_target { // target_id(g,uv), target(g,uv) friend constexpr vertex_id_type target_id(const graph_type& g, const edge_type& uv) noexcept { return uv.target_id_; } - friend constexpr vertex_type& - tag_invoke(::std::graph::tag_invoke::target_fn_t, graph_type& g, edge_type& uv) noexcept { + friend constexpr vertex_type& target(graph_type& g, edge_type& uv) noexcept { return begin(vertices(g))[uv.target_id_]; } - friend constexpr const vertex_type& - tag_invoke(::std::graph::tag_invoke::target_fn_t, const graph_type& g, const edge_type& uv) noexcept { + friend constexpr const vertex_type& target(const graph_type& g, const edge_type& uv) noexcept { return begin(vertices(g))[uv.target_id_]; } }; diff --git a/include/graph/detail/graph_cpo.hpp b/include/graph/detail/graph_cpo.hpp index 0e402ae..d794aa9 100644 --- a/include/graph/detail/graph_cpo.hpp +++ b/include/graph/detail/graph_cpo.hpp @@ -560,41 +560,84 @@ inline namespace _Cpos { // for random_access_range and integral - concept _has_target_adl = requires(G&& g, ranges::range_reference_t uv) { - { target(g, uv) }; + template + 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 + }; + template + concept _Can_ref_eval = ranges::random_access_range> // + && requires(_G&& __g, edge_reference_t<_G> uv) { + { _Fake_copy_init(target_id(__g, uv)) } -> integral; + }; + + class _Cpo { + private: + enum class _St_ref { _None, _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_ADL<_G, _UnCV>) { + return {_St_ref::_Non_member, + noexcept(_Fake_copy_init(target(declval<_G>(), declval>())))}; // intentional ADL + } else if constexpr (_Can_ref_eval<_G, _UnCV>) { + return { + _St_ref::_Auto_eval, + noexcept(_Fake_copy_init(begin(vertices(declval<_G>())) + + target_id(declval<_G>(), declval>())))}; // intentional ADL + } 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, uv)) + * + * @tparam G The graph type. + * @param g A graph instance. + * @param uv A vertex instance. + * @return The number of outgoing edges of vertex uv. + */ + 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) { + constexpr _St_ref _Strat_ref = _Choice_ref<_G&>._Strategy; + + if constexpr (_Strat_ref == _St_ref::_Non_member) { + return target(__g, uv); // intentional ADL + } else if constexpr (_Strat_ref == _St_ref::_Auto_eval) { + return *(begin(vertices(__g)) + target_id(__g, uv)); + } else { + static_assert(_Always_false<_G>, "target(g,uv) or g.target(uv) is not defined"); + } + } }; -} // namespace tag_invoke -template -concept _can_eval_target = - ranges::random_access_range> && requires(G&& g, ranges::range_reference_t uv) { - { target_id(g, uv) } -> integral; - }; +} // namespace _Target -/** - * @brief Get the target vertex of an edge. - * - * Complexity: O(1) - * - * Default implementation: *(begin(vertices(g)) + target_id(g, uv)) - * - * @tparam G The graph type. - * @param g A graph instance. - * @param uv An edge reference. - * @return The target vertex reference. -*/ -template -requires tag_invoke::_has_target_adl> || _can_eval_target> -auto&& target(G&& g, edge_reference_t uv) { - if constexpr (tag_invoke::_has_target_adl>) - return tag_invoke::target(g, uv); - else if constexpr (_can_eval_target>) - return *(begin(vertices(g)) + target_id(g, uv)); +inline namespace _Cpos { + inline constexpr _Target::_Cpo target; } + // // // source_id(g,uv) -> vertex_id_t (optional; only when a source_id exists on an edge) @@ -981,11 +1024,11 @@ inline namespace _Cpos { // = find_vertex_edge(g,uid) != ranges::end(edges(g,uid)); // namespace _Contains_edge { -# if defined(__clang__) || defined(__EDG__) // TRANSITION, VSO-1681199 - void contains_edge() = delete; // Block unqualified name lookup -# else // ^^^ no workaround / workaround vvv +# if defined(__clang__) || defined(__EDG__) // TRANSITION, VSO-1681199 + void contains_edge() = delete; // Block unqualified name lookup +# else // ^^^ no workaround / workaround vvv void contains_edge(); -# endif // ^^^ workaround ^^^ +# endif // ^^^ workaround ^^^ template concept _Has_ref_ADL = _Has_class_or_enum_type<_G> // @@ -1022,8 +1065,9 @@ namespace _Contains_edge { noexcept(_Fake_copy_init(declval>() < ranges::size(vertices(declval<_G>()))))}; } else if constexpr (_Can_id_eval<_G, _UnCV>) { return {_St_ref::_Auto_eval, - noexcept(_Fake_copy_init(find_vertex_edge(declval<_G>(), declval>(), declval>()) != - declval>()))}; + noexcept(_Fake_copy_init( + find_vertex_edge(declval<_G>(), declval>(), declval>()) != + declval>()))}; } else { return {_St_ref::_None}; } @@ -1078,11 +1122,11 @@ inline namespace _Cpos { // num_vertices(g,pid) -> integral default = size(vertices(g,pid)) // namespace _NumVertices { -# if defined(__clang__) || defined(__EDG__) // TRANSITION, VSO-1681199 - void num_vertices() = delete; // Block unqualified name lookup -# else // ^^^ no workaround / workaround vvv +# if defined(__clang__) || defined(__EDG__) // TRANSITION, VSO-1681199 + void num_vertices() = delete; // Block unqualified name lookup +# else // ^^^ no workaround / workaround vvv void num_vertices(); -# endif // ^^^ workaround ^^^ +# endif // ^^^ workaround ^^^ template concept _Has_ref_member = requires(_G&& __g) { From ee7323e476f2fa9eab928276215fcd517177b162 Mon Sep 17 00:00:00 2001 From: Phil Ratzloff Date: Tue, 14 Nov 2023 19:55:17 -0500 Subject: [PATCH 10/11] Replace old tag_invoke-based source(g,uv) with new style CPO --- include/graph/container/dynamic_graph.hpp | 5 +- include/graph/detail/graph_cpo.hpp | 121 +++++++++++++++------- 2 files changed, 86 insertions(+), 40 deletions(-) diff --git a/include/graph/container/dynamic_graph.hpp b/include/graph/container/dynamic_graph.hpp index 06e44d1..29d0f0d 100644 --- a/include/graph/container/dynamic_graph.hpp +++ b/include/graph/container/dynamic_graph.hpp @@ -294,12 +294,13 @@ class dynamic_edge_source { private: // source_id(g,uv), source(g) friend constexpr vertex_id_type source_id(const graph_type& g, const edge_type& uv) noexcept { return uv.source_id_; } + friend constexpr vertex_type& - tag_invoke(::std::graph::tag_invoke::source_fn_t, graph_type& g, edge_type& uv) noexcept { + source(graph_type& g, edge_type& uv) noexcept { return begin(vertices(g))[uv.source_id_]; } friend constexpr const vertex_type& - tag_invoke(::std::graph::tag_invoke::source_fn_t, const graph_type& g, const edge_type& uv) noexcept { + source_fn(const graph_type& g, const edge_type& uv) noexcept { return begin(vertices(g))[uv.source_id_]; } }; diff --git a/include/graph/detail/graph_cpo.hpp b/include/graph/detail/graph_cpo.hpp index d794aa9..157c7ab 100644 --- a/include/graph/detail/graph_cpo.hpp +++ b/include/graph/detail/graph_cpo.hpp @@ -561,11 +561,11 @@ inline namespace _Cpos { // uv can be from edges(g,u) or vertices(g,u) // namespace _Target { -# if defined(__clang__) || defined(__EDG__) // TRANSITION, VSO-1681199 - void target() = delete; // Block unqualified name lookup -# else // ^^^ no workaround / workaround vvv +# if defined(__clang__) || defined(__EDG__) // TRANSITION, VSO-1681199 + void target() = delete; // Block unqualified name lookup +# else // ^^^ no workaround / workaround vvv void target(); -# endif // ^^^ workaround ^^^ +# endif // ^^^ workaround ^^^ template concept _Has_ref_ADL = _Has_class_or_enum_type<_G> // @@ -575,6 +575,7 @@ namespace _Target { template concept _Can_ref_eval = ranges::random_access_range> // && requires(_G&& __g, edge_reference_t<_G> uv) { + { _Fake_copy_init(vertices(__g)) }; { _Fake_copy_init(target_id(__g, uv)) } -> integral; }; @@ -723,45 +724,89 @@ inline namespace _Cpos { // for random_access_range and integral - concept _has_source_adl = requires(G&& g, ranges::range_reference_t uv) { - { source(g, uv) }; - }; -} // namespace tag_invoke + template + 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 + }; + template + concept _Can_ref_eval = ranges::random_access_range> // + && requires(_G&& __g, edge_reference_t<_G> uv) { + { _Fake_copy_init(source_id(__g, uv)) } -> integral; + }; -template -concept _can_eval_source_id = ranges::random_access_range && requires(G&& g, ranges::range_reference_t uv) { - { source_id(g, uv) } -> integral; -}; + class _Cpo { + private: + enum class _St_ref { _None, _Non_member, _Auto_eval }; -/** - * @brief Get the source vertex of an edge. - * - * Complexity: O(1) - * - * Default implementation: *(begin(vertices(g)) + source_id(g, uv)), if @source_id(g,uv) is defined for G - * - * Not all graphs support a source on an edge. The existance of @c source_id(g,uv) function - * for a graph type G determines if it is considered a "sourced" edge or not. If it is, - * @c source(g,uv) will also exist. - * - * @tparam G The graph type. - * @param g A graph instance. - * @param uv An edge reference. - * @return The source vertex reference. -*/ -template -requires tag_invoke::_has_source_adl> || _can_eval_source_id> -auto&& source(G&& g, edge_reference_t uv) { - if constexpr (tag_invoke::_has_source_adl>) - return tag_invoke::source(g, uv); - else if constexpr (_can_eval_source_id>) - return *(begin(vertices(g)) + source_id(g, uv)); + 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_ADL<_G, _UnCV>) { + return {_St_ref::_Non_member, + noexcept(_Fake_copy_init(source(declval<_G>(), declval>())))}; // intentional ADL + } else if constexpr (_Can_ref_eval<_G, _UnCV>) { + return { + _St_ref::_Auto_eval, + noexcept(_Fake_copy_init(begin(vertices(declval<_G>())) + + source_id(declval<_G>(), declval>())))}; // intentional ADL + } else { + return {_St_ref::_None}; + } + } + + template + static constexpr _Choice_t<_St_ref> _Choice_ref = _Choose_ref<_G>(); + + public: + /** + * @brief Get the source vertex of an edge. + * + * Complexity: O(1) + * + * Default implementation: *(begin(vertices(g)) + source_id(g, uv)), + # if @source_id(g,uv) is defined for G and random_access_range> + * + * Not all graphs support a source on an edge. The existance of @c source_id(g,uv) function + * for a graph type G determines if it is considered a "sourced" edge or not. If it is, + * @c source(g,uv) will also exist. + * + * @tparam G The graph type. + * @param g A graph instance. + * @param uv An edge reference. + * @return The source vertex reference. + */ + 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) { + constexpr _St_ref _Strat_ref = _Choice_ref<_G&>._Strategy; + + if constexpr (_Strat_ref == _St_ref::_Non_member) { + return source(__g, uv); // intentional ADL + } else if constexpr (_Strat_ref == _St_ref::_Auto_eval) { + return *(begin(vertices(__g)) + source_id(__g, uv)); + } else { + static_assert(_Always_false<_G>, "source(g,uv) or g.source(uv) is not defined"); + } + } + }; +} // namespace _Source + +inline namespace _Cpos { + inline constexpr _Source::_Cpo source; } + // // edge_id(g,uv) -> edge_id_t // default = edge_id_t(source_id(g,uv),target_id(g,uv)) From e891a2bb2d058d414aa2a985f09f481f513f07bd Mon Sep 17 00:00:00 2001 From: Phil Ratzloff Date: Tue, 14 Nov 2023 20:32:24 -0500 Subject: [PATCH 11/11] Replace old tag_invoke-based vertex_id(g,ui) with new style CPO --- example/CppCon2022/rr_adaptor.hpp | 6 +- include/graph/container/compressed_graph.hpp | 11 +- include/graph/container/dynamic_graph.hpp | 10 +- include/graph/detail/graph_cpo.hpp | 128 +++++++++++++------ 4 files changed, 98 insertions(+), 57 deletions(-) diff --git a/example/CppCon2022/rr_adaptor.hpp b/example/CppCon2022/rr_adaptor.hpp index 9d53590..a3d9ed0 100644 --- a/example/CppCon2022/rr_adaptor.hpp +++ b/example/CppCon2022/rr_adaptor.hpp @@ -130,8 +130,7 @@ class rr_adaptor { return g.vertices_; } - friend vertex_id_type - tag_invoke(std::graph::tag_invoke::vertex_id_fn_t, const graph_type& g, std::ranges::iterator_t ui) { + friend vertex_id_type vertex_id(const graph_type& g, std::ranges::iterator_t ui) { return static_cast(ui - std::ranges::begin(g.vertices_)); // overriden to assure correct type returned } @@ -362,8 +361,7 @@ class rr_adaptor2 { return g.vertices_; } - friend vertex_id_type - tag_invoke(std::graph::tag_invoke::vertex_id_fn_t, const graph_type& g, std::ranges::iterator_t ui) { + friend vertex_id_type vertex_id(const graph_type& g, std::ranges::iterator_t ui) { return static_cast(ui - std::ranges::begin(g.vertices_)); // overriden to assure correct type returned } diff --git a/include/graph/container/compressed_graph.hpp b/include/graph/container/compressed_graph.hpp index af33253..540aff6 100644 --- a/include/graph/container/compressed_graph.hpp +++ b/include/graph/container/compressed_graph.hpp @@ -753,8 +753,7 @@ class compressed_graph_base return const_vertices_type(g.row_index_.begin(), g.row_index_.end() - 1); // don't include terminating row } - friend vertex_id_type - tag_invoke(::std::graph::tag_invoke::vertex_id_fn_t, const compressed_graph_base& g, const_iterator ui) { + friend vertex_id_type vertex_id(const compressed_graph_base& g, const_iterator ui) { return static_cast(ui - g.row_index_.begin()); } @@ -795,12 +794,8 @@ class compressed_graph_base // target_id(g,uv), target(g,uv) friend constexpr vertex_id_type target_id(const graph_type& g, const edge_type& uv) noexcept { return uv.index; } - friend constexpr vertex_type& - target(graph_type& g, edge_type& uv) noexcept { - return g.row_index_[uv.index]; - } - friend constexpr const vertex_type& - target(const graph_type& g, const edge_type& uv) noexcept { + friend constexpr vertex_type& target(graph_type& g, edge_type& uv) noexcept { return g.row_index_[uv.index]; } + friend constexpr const vertex_type& target(const graph_type& g, const edge_type& uv) noexcept { return g.row_index_[uv.index]; } diff --git a/include/graph/container/dynamic_graph.hpp b/include/graph/container/dynamic_graph.hpp index 29d0f0d..381fbce 100644 --- a/include/graph/container/dynamic_graph.hpp +++ b/include/graph/container/dynamic_graph.hpp @@ -295,12 +295,10 @@ class dynamic_edge_source { // source_id(g,uv), source(g) friend constexpr vertex_id_type source_id(const graph_type& g, const edge_type& uv) noexcept { return uv.source_id_; } - friend constexpr vertex_type& - source(graph_type& g, edge_type& uv) noexcept { + friend constexpr vertex_type& source(graph_type& g, edge_type& uv) noexcept { return begin(vertices(g))[uv.source_id_]; } - friend constexpr const vertex_type& - source_fn(const graph_type& g, const edge_type& uv) noexcept { + friend constexpr const vertex_type& source_fn(const graph_type& g, const edge_type& uv) noexcept { return begin(vertices(g))[uv.source_id_]; } }; @@ -1243,9 +1241,7 @@ class dynamic_graph_base { return g.vertices_; } - friend vertex_id_type tag_invoke(::std::graph::tag_invoke::vertex_id_fn_t, - const dynamic_graph_base& g, - typename vertices_type::const_iterator ui) { + 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 157c7ab..0634b66 100644 --- a/include/graph/detail/graph_cpo.hpp +++ b/include/graph/detail/graph_cpo.hpp @@ -170,47 +170,99 @@ using vertex_reference_t = ranges::range_reference_t>; // vertex_id(g,ui) -> vertex_id_t // default = ui - begin(vertices(g)), if random_access_iterator // -namespace tag_invoke { - TAG_INVOKE_DEF(vertex_id); +namespace _Vertex_id { +# if defined(__clang__) || defined(__EDG__) // TRANSITION, VSO-1681199 + void vertex_id() = delete; // Block unqualified name lookup +# else // ^^^ no workaround / workaround vvv + void vertex_id(); +# endif // ^^^ workaround ^^^ - template - concept _has_vertex_id_adl = requires(G&& g, vertex_iterator_t ui) { - { vertex_id(g, ui) }; + template + concept _Has_ref_member = requires(_G&& __g, vertex_iterator_t<_G> ui) { + { _Fake_copy_init(ui->vertex_id(__g)) }; }; -} // namespace tag_invoke + template + 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 + }; + template + concept _Can_ref_eval = ranges::random_access_range>; -/** - * @brief Get's the id of a vertex. - * - * Complexity: O(1) - * - * Default implementation: ui - begin(g) - * - * This is a customization point function that may be overriden for a graph type. - * The main reason to do so is to change the return type to be something different - * than range_difference_t>. For 64-bit systems, that's typically - * int64_t. The return type is used to define the type vertex_id_t which is used - * for vertex id in other functions. - * - * Why does this function take a vertex iterator instead of a vertex reference? - * The vertex id is often calculated rather than stored. Given an iterator, the id is easily - * calculated by id = (ui - begin(vertices(g))). If a vertex reference v is passed instead - * it is also easily calculated for vertices stored in contiguous memory like std::vector. - * However, if it's a random access container like a deque, then the reference won't work - * and an iterator is the only option. - * - * @tparam G The graph type. - * @param g A graph instance. - * @param ui A vertex iterator for a vertext in graph G. - * @return The vertex id of a vertex. -*/ -template -requires tag_invoke::_has_vertex_id_adl || random_access_iterator> -auto vertex_id(G&& g, vertex_iterator_t ui) { - if constexpr (tag_invoke::_has_vertex_id_adl) - return tag_invoke::vertex_id(g, ui); - else if constexpr (random_access_iterator>) - return ui - ranges::begin(vertices(g)); + 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>()->vertex_id(declval<_G>())))}; + } else if constexpr (_Has_ref_ADL<_G, _UnCV>) { + return { + _St_ref::_Non_member, + noexcept(_Fake_copy_init(vertex_id(declval<_G>(), declval>())))}; // intentional ADL + } else if constexpr (_Can_ref_eval<_G, _UnCV>) { + return {_St_ref::_Auto_eval, + noexcept(_Fake_copy_init(declval>() - + ranges::begin(vertices(declval<_G>()))))}; // intentional ADL + } else { + return {_St_ref::_None}; + } + } + + template + static constexpr _Choice_t<_St_ref> _Choice_ref = _Choose_ref<_G>(); + + public: + /** + * @brief Get's the id of a vertex. + * + * Complexity: O(1) + * + * Default implementation: ui - begin(g) + * + * This is a customization point function that may be overriden for a graph type. + * The main reason to do so is to change the return type to be something different + * than range_difference_t>. For 64-bit systems, that's typically + * int64_t. The return type is used to define the type vertex_id_t which is used + * for vertex id in other functions. + * + * Why does this function take a vertex iterator instead of a vertex reference? + * The vertex id is often calculated rather than stored. Given an iterator, the id is easily + * calculated by id = (ui - begin(vertices(g))). If a vertex reference v is passed instead + * it is also easily calculated for vertices stored in contiguous memory like std::vector. + * However, if it's a random access container like a deque, then the reference won't work + * and an iterator is the only option. + * + * @tparam G The graph type. + * @param g A graph instance. + * @param ui A vertex iterator for a vertext in graph G. + * @return The vertex id of a vertex. + */ + template + requires(_Choice_ref<_G&>._Strategy != _St_ref::_None) + [[nodiscard]] constexpr auto operator()(_G&& __g, vertex_iterator_t<_G> ui) const + noexcept(_Choice_ref<_G&>._No_throw) { + constexpr _St_ref _Strat_ref = _Choice_ref<_G&>._Strategy; + + if constexpr (_Strat_ref == _St_ref::_Member) { + return ui->vertex_id(__g); + } else if constexpr (_Strat_ref == _St_ref::_Non_member) { + return vertex_id(__g, ui); // intentional ADL + } else if constexpr (_Strat_ref == _St_ref::_Auto_eval) { + return ui - ranges::begin(vertices(__g)); + } else { + static_assert(_Always_false<_G>, "vertices(g) is not defined or is not random-access"); + } + } + }; +} // namespace _Vertex_id + +inline namespace _Cpos { + inline constexpr _Vertex_id::_Cpo vertex_id; } /**