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; } /**