Skip to content

Commit

Permalink
Replace old tag_invoke-based vertex_id(g,ui) with new style CPO
Browse files Browse the repository at this point in the history
  • Loading branch information
pratzl committed Nov 15, 2023
1 parent ee7323e commit e891a2b
Show file tree
Hide file tree
Showing 4 changed files with 98 additions and 57 deletions.
6 changes: 2 additions & 4 deletions example/CppCon2022/rr_adaptor.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -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<vertices_range> ui) {
friend vertex_id_type vertex_id(const graph_type& g, std::ranges::iterator_t<vertices_range> ui) {
return static_cast<vertex_id_type>(ui -
std::ranges::begin(g.vertices_)); // overriden to assure correct type returned
}
Expand Down Expand Up @@ -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<vertices_range> ui) {
friend vertex_id_type vertex_id(const graph_type& g, std::ranges::iterator_t<vertices_range> ui) {
return static_cast<vertex_id_type>(ui -
std::ranges::begin(g.vertices_)); // overriden to assure correct type returned
}
Expand Down
11 changes: 3 additions & 8 deletions include/graph/container/compressed_graph.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -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<vertex_id_type>(ui - g.row_index_.begin());
}

Expand Down Expand Up @@ -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];
}

Expand Down
10 changes: 3 additions & 7 deletions include/graph/container/dynamic_graph.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -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_];
}
};
Expand Down Expand Up @@ -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<vertex_id_type>(ui - g.vertices_.begin());
}

Expand Down
128 changes: 90 additions & 38 deletions include/graph/detail/graph_cpo.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -170,47 +170,99 @@ using vertex_reference_t = ranges::range_reference_t<vertex_range_t<G>>;
// vertex_id(g,ui) -> vertex_id_t<G>
// default = ui - begin(vertices(g)), if random_access_iterator<ui>
//
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 <class G>
concept _has_vertex_id_adl = requires(G&& g, vertex_iterator_t<G> ui) {
{ vertex_id(g, ui) };
template <class _G, class _UnCV>
concept _Has_ref_member = requires(_G&& __g, vertex_iterator_t<_G> ui) {
{ _Fake_copy_init(ui->vertex_id(__g)) };
};
} // namespace tag_invoke
template <class _G, class _UnCV>
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 <class _G, class _UnCV>
concept _Can_ref_eval = ranges::random_access_range<vertex_range_t<_G>>;

/**
* @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<vertex_range_t<G>>. For 64-bit systems, that's typically
* int64_t. The return type is used to define the type vertex_id_t<G> 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 <class G>
requires tag_invoke::_has_vertex_id_adl<G> || random_access_iterator<vertex_iterator_t<G>>
auto vertex_id(G&& g, vertex_iterator_t<G> ui) {
if constexpr (tag_invoke::_has_vertex_id_adl<G>)
return tag_invoke::vertex_id(g, ui);
else if constexpr (random_access_iterator<vertex_iterator_t<G>>)
return ui - ranges::begin(vertices(g));
class _Cpo {
private:
enum class _St_ref { _None, _Member, _Non_member, _Auto_eval };

template <class _G>
[[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_iterator_t<_G>>()->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<vertex_iterator_t<_G>>())))}; // intentional ADL
} else if constexpr (_Can_ref_eval<_G, _UnCV>) {
return {_St_ref::_Auto_eval,
noexcept(_Fake_copy_init(declval<vertex_iterator_t<_G>>() -
ranges::begin(vertices(declval<_G>()))))}; // intentional ADL
} else {
return {_St_ref::_None};
}
}

template <class _G>
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<vertex_range_t<G>>. For 64-bit systems, that's typically
* int64_t. The return type is used to define the type vertex_id_t<G> 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 <class _G>
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;
}

/**
Expand Down

0 comments on commit e891a2b

Please sign in to comment.