From 4bba09b4c9d5e07f9392e2500abdbea8db539a2c Mon Sep 17 00:00:00 2001 From: Phil Ratzloff Date: Sun, 17 Nov 2024 16:43:59 -0500 Subject: [PATCH] Add descriptor_traits --- include/graph/detail/descriptor.hpp | 373 ++++++++++++++++++++++++---- include/graph/detail/graph_cpo.hpp | 121 ++++++--- tests/descriptor_tests.cpp | 39 +-- 3 files changed, 429 insertions(+), 104 deletions(-) diff --git a/include/graph/detail/descriptor.hpp b/include/graph/detail/descriptor.hpp index 8740f04..2321e66 100644 --- a/include/graph/detail/descriptor.hpp +++ b/include/graph/detail/descriptor.hpp @@ -5,7 +5,126 @@ #ifndef GRAPH_DESCRIPTOR_HPP # define GRAPH_DESCRIPTOR_HPP -namespace graph::detail { +namespace graph { + +// Descriptor Change Notes +// +// Implications in the change for descriptors: +// 1. The vertex functions in the Graph Container Interface will replace vertex id and vertex reference with a vertex descriptor, +// and edge functions will replace edge reference with vertex descriptor. +// 2. The second implication is that the number of concepts, views and functions will shrink because of this. +// 3. It will become easier to write generic algorithms without specializations for vertices in random-access vs. bidirectional +// containers. +// 4. It becomes much harder to detect if uv.target_id(g) is defined, so member functions may not be auto-detected by CPOs. +// +// Practically, a descriptor is an integral index for contiguous and random-access containers and an iterator for other containers. +// +// Rename vertex_descriptor to vertex_info, etc. +// + +// Questions +// 1. How to provide the raw vertex & edge ranges? (non-descriptor ranges) +// a. special ranges? (e.g. raw_vertices_range(g), raw_edges_range(g,u)) +// + +// "Concepts" for descriptors +// 1. descriptor_iterator +// 2. descriptor_value: index or iterator +// +// 3. descriptor_view | descriptor_subrange_view +// a. size() +// b. begin(), end() +// c. id(descriptor) -> vertex_id_t +// d. find(id) -> +// +// 5. inner_iterator: iterator of the underlying container +// 6. inner_value_type: vertex or edge value +// 7. inner_id_type: index or key + +template +struct is_tuple_like : std::false_type {}; +template +struct is_tuple_like> : public std::true_type {}; +template +struct is_tuple_like> : public std::true_type {}; + +template +inline constexpr bool is_tuple_like_v = is_tuple_like::value; + + +template +struct tuple_tail { + using type = T; +}; +template +struct tuple_tail> { + using type = tuple; +}; +template +struct tuple_tail> { + using type = U; +}; +template +using tuple_tail_t = typename tuple_tail::type; + + +# if 0 +template +struct edge_descriptor_traits { + using inner_iterator = I; + using difference_type = iter_difference_t; + using value_type = conditional_t, difference_type, inner_iterator>; + + // container inner_id_type inner_target_type inner_value_type + // ========================= ================ ==================== ==================== + // vector difference_type int int + // vector> difference_type int pair + // deque difference_type int int + // deque> difference_type int pair + // map int int pair + // set int int int + // list int int int + // list> int int tuple + // list> int int pair + // + + using inner_value_type = iter_value_t; + + using inner_id_type = conditional_t< + random_access_iterator, // + difference_type, // + conditional_t>, tuple_element_t<0, iter_value_t>, iter_value_t>>; +}; +# endif //0 + +template +struct _descriptor_traits { + using inner_iterator = I; + using difference_type = iter_difference_t; + using value_type = conditional_t, difference_type, inner_iterator>; + + // container inner_id_type inner_value_type + // ========================= ================ ==================== + // vector> difference_type vector + // vector> difference_type map + // vector> difference_type set + // deque> difference_type deque + // deque> difference_type map + // map> int vector + // + + using inner_value_type = iter_value_t; + + // The type used to lookup an element in the container. + using inner_id_type = + conditional_t, // + difference_type, // + conditional_t, tuple_element_t<0, inner_value_type>, void>>; + + //using inner_target_id_type = + // conditional_t, tuple_element_t<0, inner_value_type>, inner_value_type>; +}; + /** * @brief An iterator that uses a descriptor (integral index or iterator) for a container. @@ -15,19 +134,23 @@ namespace graph::detail { * A descriptor enables an abstraction that allows the same code to be used for different types of containers * without loss of efficiency from the underlying container. For instance, if an integral index is used * for a contiguous container, the code will be as efficient as if the index were used directly. - * However, if it's used for a map (bidirectional container), the consuming code will need to do a log(n) - * lookup each time it wants to access the associated value. + * If it's used for a map (bidirectional container), the iterator will be dereferenced to get the associated + * value, avoiding a log(n) lookup if the id were to be used. * * @tparam I Iterator type of the underlying container. */ -template +template > class _descriptor_iterator { public: - using inner_iterator = I; - using this_type = _descriptor_iterator; + using this_type = _descriptor_iterator; + using traits = Traits; - using difference_type = iter_difference_t; - using value_type = conditional_t, difference_type, inner_iterator>; + using inner_iterator = I; + using inner_id_type = typename traits::inner_id_type; + using inner_value_type = typename traits::inner_value_type; + + using difference_type = typename traits::difference_type; + using value_type = typename traits::value_type; using pointer = std::add_pointer_t>; using reference = std::add_lvalue_reference_t>; using iterator_category = std::forward_iterator_tag; @@ -37,11 +160,21 @@ class _descriptor_iterator { explicit _descriptor_iterator(value_type descriptor) : descriptor_(descriptor) {} // copy & move constructors and assignment operators are default + template + requires convertible_to, inner_iterator> + explicit _descriptor_iterator(R& r, inner_iterator iter) { + if constexpr (integral) { + descriptor_ = static_cast(std::distance(std::ranges::begin(r), iter)); + } else { + descriptor_ = iter; + } + } + // // dereference // - reference operator*() const { return descriptor_; } - pointer operator->() const { return &descriptor_; } + reference operator*() const noexcept { return descriptor_; } + pointer operator->() const noexcept { return &descriptor_; } // // operators ++ @@ -65,17 +198,6 @@ class _descriptor_iterator { value_type descriptor_ = value_type(); // integral index or iterator, depending on container type }; -template -struct is_tuple_like : std::false_type {}; -template -struct is_tuple_like> : public std::true_type {}; -template -struct is_tuple_like> : public std::true_type {}; - -template -inline constexpr bool is_tuple_like_v = is_tuple_like::value; - - template struct descriptor_value { using type = T; @@ -136,19 +258,20 @@ template using _ident_t = _ident_type::type; -// (obsolete, for tests only. eventually need to remove) template -class descriptor_view_old { +class descriptor_view : public std::ranges::view_interface> { public: //using size_type = range_size_t; + using inner_iterator = iterator_t; // iterator of the underlying container + using iterator = _descriptor_iterator; // + using value_type = descriptor_value_t>; - using difference_type = range_difference_t; - using id_type = difference_type; // e.g. vertex_id_t - using iterator = _descriptor_iterator>; // - using descriptor_type = iter_value_t; // integral index or iterator, depending on container type + using difference_type = iter_difference_t; + using id_type = difference_type; // e.g. vertex_id_t + using descriptor_type = iter_value_t; // integral index or iterator, depending on container type - descriptor_view_old() = default; - explicit descriptor_view_old(C& c) : c_(c) {} + descriptor_view() = default; + constexpr explicit descriptor_view(C&& c) : c_(c) {} auto size() const requires sized_range @@ -156,29 +279,95 @@ class descriptor_view_old { return std::ranges::size(c_); } - auto begin() const { - if constexpr (std::ranges::random_access_range) { - return iterator{static_cast(0)}; + iterator begin() const { return std::ranges::begin(c_); } + iterator end() const { return std::ranges::end(c_); } + + /** + * @brief Get the vertex id for a descriptor. + * + * @param desc The descriptor. This must refer to a valid element in the container. + * @return vertex id. + */ + id_type id(descriptor_type& desc) const { + if constexpr (integral) { + return desc; + } else if constexpr (random_access_range) { + return static_cast(std::distance(c_.begin(), desc)); + } else if constexpr (is_tuple_like_v>) { + return std::get<0>(*desc); // e.g., pair::first used for map } else { - return iterator{std::ranges::begin(c_)}; + static_assert( + random_access_range, + "id cannot be determined for a forward range or a bidirectional range without a tuple-like value type"); + return id_type(); } } - auto end() const { - if constexpr (std::ranges::random_access_range) { - return iterator{static_cast(std::ranges::size(c_))}; + + /** + * @brief Find an element in the container, given an id. + * + * This assumes that the full range of id's in the container is [0, size(c_)). If a subrange is needed, use + * subrange_find. + * + * @param id The id to search for. + * @return Descriptor iterator to the element. If the element is not found, the iterator is equal to end(). + */ + iterator find(id_type id) const { + if constexpr (integral) { + return iterator(id); + } else if constexpr (random_access_range) { + return iterator(c_.begin() + id); + } else if constexpr (bidirectional_range) { + return iterator(c_.find(id)); // map or set } else { - return iterator{std::ranges::end(c_)}; + static_assert(random_access_range, + "find(id) cannot be evaluated for a forward range because there is no id/key in the container"); } + return end(); } - // Helper function to get the id of an descriptor. Actual implementation in vertex_id(g, ident) - id_type id(descriptor_type ident) const { - if constexpr (random_access_range) { - return ident; +private: + reference_wrapper c_; +}; + +template +class descriptor_subrange_view : public std::ranges::view_interface> { +public: + //using size_type = range_size_t; + using inner_iterator = iterator_t; // iterator of the underlying container + using iterator = _descriptor_iterator; // + + using value_type = descriptor_value_t>; + using difference_type = iter_difference_t; + using id_type = difference_type; // e.g. vertex_id_t + using descriptor_type = iter_value_t; // integral index or iterator, depending on container type + + descriptor_subrange_view() = default; + constexpr explicit descriptor_subrange_view(C&& c) + : c_(c), first_(c, std::ranges::begin(c)), last_(c, std::ranges::end(c)) {} + + auto size() const + requires sized_range + { + return std::ranges::size(c_); + } + + iterator begin() const { return first_; } + iterator end() const { return last_; } + + /** + * @brief Get the vertex id for a descriptor. + * + * @param desc The descriptor. This must refer to a valid element in the container. + * @return vertex id. + */ + id_type id(descriptor_type& desc) const { + if constexpr (integral) { + return desc; } else if constexpr (random_access_range) { - return static_cast(std::distance(c_.begin(), ident)); + return static_cast(std::distance(c_.begin(), desc)); } else if constexpr (is_tuple_like_v>) { - return ident->first; // map + return std::get<0>(*desc); // e.g., pair::first used for map } else { static_assert( random_access_range, @@ -187,9 +376,17 @@ class descriptor_view_old { } } - // Helper function to find an descriptor by vertex id. Actual implementation in find_vertex(g, id). + /** + * @brief Find an element in the container, given an id. + * + * This assumes that the full range of id's in the container is [0, size(c_)). If a subrange is needed, use + * subrange_find. + * + * @param id The id to search for. + * @return Descriptor iterator to the element. If the element is not found, the iterator is equal to end(). + */ iterator find(id_type id) const { - if constexpr (random_access_range) { + if constexpr (integral) { return iterator(id); } else if constexpr (random_access_range) { return iterator(c_.begin() + id); @@ -198,18 +395,94 @@ class descriptor_view_old { } else { static_assert(random_access_range, "find(id) cannot be evaluated for a forward range because there is no id/key in the container"); - return iterator(); } + return last_; + } + + /** + * @brief Find an element in a container, given an id, constrained to the range [first_, last_). + * + * The id must be in the range [first_, last_) of the container. If it isn't, the iterator will be equal to end() + * which is also last_. + * + * Note: The first/last constraint is really for edges in a CSR. Vertices in a CSR and edges in vertex> + * will include all elements in the container. Specialization for different conditions could reduce the number of + * constraints. + * + * @param id The id to search for. It must be in the range [first_, last_) of the container. + * @return Descriptor iterator to the element. If the element is not found, the iterator is equal to end(). + */ + iterator subrange_find(id_type id) const { + if constexpr (integral) { + if (id >= *first_ && id < *last_) { + return iterator(c_, c_.begin() + id); + } + } else if constexpr (random_access_range) { + if ((id >= *first_ - c_.begin()) && (id < *last_ - c_.begin())) { + return iterator(c_.begin() + id); + } + } else if constexpr (bidirectional_range) { + return iterator(c_.find(id)); // map or set + } else { + static_assert(random_access_range, + "find(id) cannot be evaluated for a forward range because there is no id/key in the container"); + } + return last_; } private: - C& c_; + reference_wrapper c_; + + // first_ and last_ may be a sub-range of the container (e.g. for a CSR). + iterator first_; + iterator last_; }; -template -using _descriptor_range = subrange<_descriptor_iterator>; -} // namespace graph::detail +# if 0 +template +using _descriptor_view = subrange<_descriptor_iterator>, _descriptor_iterator>>; + +template +auto to_descriptor_view(R&& r) { + using dview = _descriptor_view; + using diter = iterator_t; + using dvalue = range_value_t; + if constexpr (integral) { + return dview{diter(0), diter(static_cast(size(r)))}; + } else { + return dview{diter(std::ranges::begin(r)), diter(std::ranges::end(r))}; + } +} + +// The concrete value of a descriptor is the underlying type the descriptor points to. + +// value_type of the inner iterator of the descriptor_view +template +using _concrete_value_type = iter_value_t::inner_iterator>; + +/** + * @brief Returns a reference to the concrete value of a descriptor. + * + * @tparam DV Descriptor value_type. + * @tparam R Underlying range type of values referenced by the descriptor. + * + * @param r Underlying range of concrete values. + * @param dvalue Descriptor value. + * + * @return Reference to the concrete value. + */ +template +auto& _concrete_value(R&& r, DV&& dvalue) { + if constexpr (integral) { + return r[dvalue]; // could also be *(r.begin() + dvalue), or r.begin()[dvalue] + } else { + return *dvalue; + } +} +# endif + +} // namespace graph #endif // GRAPH_DESCRIPTOR_HPP diff --git a/include/graph/detail/graph_cpo.hpp b/include/graph/detail/graph_cpo.hpp index 01dc224..5a66bff 100644 --- a/include/graph/detail/graph_cpo.hpp +++ b/include/graph/detail/graph_cpo.hpp @@ -7,6 +7,9 @@ #ifndef GRAPH_CPO_HPP # define GRAPH_CPO_HPP +# define USE_VERTEX_DESCRIPTOR 0 +# define USE_EDGE_DESCRIPTOR 0 + namespace graph { // The non-standard naming of these structs is intentional to avoid conflicts with @@ -167,7 +170,7 @@ namespace _Vertices { { _Fake_copy_init(__g.vertices()) }; }; template - concept _Has_ref_ADL = _HasClassOrEnumType<_G> // + concept _Has_ref_ADL = _HasClassOrEnumType<_G> // && requires(_G&& __g) { { _Fake_copy_init(vertices(__g)) }; // intentional ADL }; @@ -219,7 +222,7 @@ namespace _Vertices { return __g.vertices(); } else if constexpr (_Strat_ref == _St_ref::_Non_member) { //static_assert(is_reference_v); - return vertices(__g); // intentional ADL + return vertices(__g); // intentional ADL } else if constexpr (_Strat_ref == _St_ref::_Auto_eval) { return std::forward<_G>(__g); // intentional ADL } else { @@ -278,7 +281,7 @@ namespace _Vertex_id { { _Fake_copy_init(ui->vertex_id(__g)) }; }; template - concept _Has_ref_ADL = _HasClassOrEnumType<_G> // + concept _Has_ref_ADL = _HasClassOrEnumType<_G> // && requires(_G&& __g, const vertex_iterator_t<_G> ui) { { _Fake_copy_init(vertex_id(__g, ui)) }; // intentional ADL }; @@ -455,7 +458,7 @@ namespace _Find_vertex { }; template - concept _Has_ADL = _HasClassOrEnumType<_G> // + concept _Has_ADL = _HasClassOrEnumType<_G> // && requires(_G&& __g, const vertex_id_t<_G>& uid) { { _Fake_copy_init(find_vertex(__g, uid)) }; // intentional ADL }; @@ -542,7 +545,7 @@ namespace _Edges { { _Fake_copy_init(u.edges(__g)) }; }; template - concept _Has_ref_ADL = _HasClassOrEnumType<_G> // + concept _Has_ref_ADL = _HasClassOrEnumType<_G> // && requires(_G&& __g, const vertex_reference_t<_G>& u) { { _Fake_copy_init(edges(__g, u)) }; // intentional ADL }; @@ -550,7 +553,7 @@ namespace _Edges { concept _Can_ref_eval = _HasClassOrEnumType<_G> && forward_range>; template - concept _Has_id_ADL = _HasClassOrEnumType<_G> // + concept _Has_id_ADL = _HasClassOrEnumType<_G> // && requires(_G&& __g, const vertex_id_t<_G>& uid) { { _Fake_copy_init(edges(__g, uid)) }; // intentional ADL }; @@ -625,7 +628,7 @@ namespace _Edges { } else if constexpr (_Strat_ref == _St_ref::_Non_member) { return edges(__g, u); // intentional ADL } else if constexpr (_Strat_ref == _St_ref::_Auto_eval) { - return u; // default impl + return u; // default impl } else { static_assert(_AlwaysFalse<_G>, "edges(g,u) is not defined and the default implementation cannot be evaluated"); } @@ -649,14 +652,25 @@ namespace _Edges { noexcept(_Choice_id<_G&>._No_throw) -> decltype(auto) { constexpr _St_id _Strat_id = _Choice_id<_G&>._Strategy; +# if USE_EDGE_DESCRIPTOR + if constexpr (_Strat_id == _St_id::_Non_member) { + return to_descriptor_view(edges(__g, uid)); // intentional ADL + } else if constexpr (_Strat_id == _St_id::_Auto_eval) { + return to_descriptor_view(*find_vertex(__g, uid)); // default impl + } else { + static_assert(_AlwaysFalse<_G>, + "edges(g,uid) is not defined and the default implementation cannot be evaluated"); + } +# else if constexpr (_Strat_id == _St_id::_Non_member) { - return edges(__g, uid); // intentional ADL + return edges(__g, uid); // intentional ADL } else if constexpr (_Strat_id == _St_id::_Auto_eval) { return *find_vertex(__g, uid); // default impl } else { static_assert(_AlwaysFalse<_G>, "edges(g,uid) is not defined and the default implementation cannot be evaluated"); } +# endif } }; } // namespace _Edges @@ -679,6 +693,22 @@ using vertex_edge_range_t = decltype(edges(declval(), declval using vertex_edge_iterator_t = iterator_t>; +# if USE_EDGE_DESCRIPTOR +/** + * @brief The edge descriptor type for graph G. + * The edge descriptor is an integral index value or an iterator to the edge value. + * @tparam G The graph type. +*/ +template +using edge_descriptor_t = range_value_t>; + +/** + * @brief The concrete edge type for graph G. + * @tparam G The graph type. +*/ +//template +//using edge_t = _concrete_value_type>; +# else /** * @brief The edge type for graph G. * @tparam G The graph type. @@ -692,7 +722,7 @@ using edge_t = range_value_t>; */ template using edge_reference_t = range_reference_t>; - +# endif // // num_edges(g,) -> integral default = n=0; for (const auto& u : vertices(g)) n += distance(edges(g,u)) @@ -710,7 +740,7 @@ namespace _NumEdges { { _Fake_copy_init(__g.num_edges()) }; }; template - concept _Has_ref_ADL = _HasClassOrEnumType<_G> // + concept _Has_ref_ADL = _HasClassOrEnumType<_G> // && requires(_G&& __g) { { _Fake_copy_init(num_edges(__g)) }; // intentional ADL }; @@ -800,6 +830,16 @@ namespace _Target_id { void target_id(); # endif // ^^^ workaround ^^^ +# if USE_EDGE_DESCRIPTOR + template + concept _Has_adjl_ref_member = requires(_G&& __g, edge_descriptor_t<_G> uv) { + { _Fake_copy_init(uv.target_id(__g)) }; + }; + template + concept _Has_adjl_ref_ADL = requires(_G&& __g, edge_descriptor_t<_G> uv) { + { _Fake_copy_init(target_id(__g, uv)) }; // intentional ADL + }; +# else template concept _Has_adjl_ref_member = requires(_G&& __g, edge_reference_t<_G> uv) { { _Fake_copy_init(uv.target_id(__g)) }; @@ -808,6 +848,7 @@ namespace _Target_id { concept _Has_adjl_ref_ADL = requires(_G&& __g, edge_reference_t<_G> uv) { { _Fake_copy_init(target_id(__g, uv)) }; // intentional ADL }; +#endif template concept _Is_basic_id_adj = integral<_al_edge_t<_G>>; // vertex> @@ -998,7 +1039,7 @@ namespace _Source_id { { _Fake_copy_init(uv.source_id(__g)) }; }; template - concept _Has_adjl_ref_ADL = _HasClassOrEnumType<_G> // + concept _Has_adjl_ref_ADL = _HasClassOrEnumType<_G> // && requires(_G&& __g, const edge_reference_t<_G>& uv) { { _Fake_copy_init(source_id(__g, uv)) }; // intentional ADL }; @@ -1008,7 +1049,7 @@ namespace _Source_id { { _Fake_copy_init(__e.source_id()) }; }; template - concept _Has_edgl_ref_ADL = _HasClassOrEnumType<_E> // + concept _Has_edgl_ref_ADL = _HasClassOrEnumType<_E> // && requires(_E&& __e) { { _Fake_copy_init(source_id(__e)) }; // intentional ADL }; @@ -1117,7 +1158,7 @@ namespace _Source_id { } else if constexpr (_Strat_ref == _St_edgl_ref::_Non_member) { return source_id(__e); // intentional ADL } else if constexpr (_Strat_ref == _St_edgl_ref::_Tuple_id) { - return get<1>(__e); // first element of tuple/pair + return get<1>(__e); // first element of tuple/pair } else if constexpr (_Strat_ref == _St_edgl_ref::_EDesc_id) { return __e.source_id; } else { @@ -1154,7 +1195,7 @@ namespace _Target { # endif // ^^^ workaround ^^^ template - concept _Has_ref_ADL = _HasClassOrEnumType<_G> // + concept _Has_ref_ADL = _HasClassOrEnumType<_G> // && requires(_G&& __g, const edge_reference_t<_G>& uv) { { _Fake_copy_init(target(__g, uv)) }; // intentional ADL }; @@ -1238,7 +1279,7 @@ namespace _Source { # endif // ^^^ workaround ^^^ template - concept _Has_ref_ADL = _HasClassOrEnumType<_G> // + concept _Has_ref_ADL = _HasClassOrEnumType<_G> // && requires(_G&& __g, const edge_reference_t<_G>& uv) { { _Fake_copy_init(source(__g, uv)) }; // intentional ADL }; @@ -1331,7 +1372,7 @@ namespace _Find_vertex_edge { }; template - concept _Has_ref_ADL = _HasClassOrEnumType<_G> // + concept _Has_ref_ADL = _HasClassOrEnumType<_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 }; @@ -1342,7 +1383,7 @@ namespace _Find_vertex_edge { }; template - concept _Has_id_ADL = _HasClassOrEnumType<_G> // + concept _Has_id_ADL = _HasClassOrEnumType<_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 }; @@ -1479,7 +1520,7 @@ namespace _Contains_edge { # endif // ^^^ workaround ^^^ template - concept _Has_ref_ADL = _HasClassOrEnumType<_G> // + concept _Has_ref_ADL = _HasClassOrEnumType<_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 }; @@ -1578,7 +1619,7 @@ namespace _Partition_id { { _Fake_copy_init(u.partition_id(__g)) }; }; template - concept _Has_ref_ADL = _HasClassOrEnumType<_G> // + concept _Has_ref_ADL = _HasClassOrEnumType<_G> // && requires(_G&& __g, const vertex_reference_t<_G>& u) { { _Fake_copy_init(partition_id(__g, u)) }; // intentional ADL }; @@ -1589,7 +1630,7 @@ namespace _Partition_id { }; template - concept _Has_id_ADL = _HasClassOrEnumType<_G> // + concept _Has_id_ADL = _HasClassOrEnumType<_G> // && requires(_G&& __g, const vertex_id_t<_G>& uid) { { _Fake_copy_init(partition_id(__g, uid)) }; // intentional ADL }; @@ -1611,7 +1652,7 @@ namespace _Partition_id { return {_St_id::_Non_member, noexcept(_Fake_copy_init(partition_id(declval<_G>(), declval>())))}; // intentional ADL } else if constexpr (_Can_id_eval<_G>) { - return {_St_id::_Auto_eval, noexcept(_Fake_copy_init(vertex_id_t<_G>(0)))}; // default impl + return {_St_id::_Auto_eval, noexcept(_Fake_copy_init(vertex_id_t<_G>(0)))}; // default impl } else { return {_St_id::_None}; } @@ -1663,7 +1704,7 @@ namespace _Partition_id { } else if constexpr (_Strat_ref == _St_ref::_Non_member) { return partition_id(__g, u); // intentional ADL } else if constexpr (_Strat_ref == _St_ref::_Auto_eval) { - return vertex_id_t<_G>{0}; // default impl + return vertex_id_t<_G>{0}; // default impl } else { static_assert(_AlwaysFalse<_G>, "partition_id(g,u) is not defined and the default implementation cannot be evaluated"); @@ -1691,7 +1732,7 @@ namespace _Partition_id { if constexpr (_Strat_id == _St_id::_Non_member) { return partition_id(__g, uid); // intentional ADL } else if constexpr (_Strat_id == _St_id::_Auto_eval) { - return vertex_id_t<_G>{0}; // default impl + return vertex_id_t<_G>{0}; // default impl } else { static_assert(_AlwaysFalse<_G>, "partition_id(g,uid) is not defined and the default implementation cannot be evaluated"); @@ -1724,7 +1765,7 @@ namespace _NumVertices { { _Fake_copy_init(__g.num_vertices(__g)) }; }; template - concept _Has_ref_ADL = _HasClassOrEnumType<_G> // + concept _Has_ref_ADL = _HasClassOrEnumType<_G> // && requires(_G&& __g) { { _Fake_copy_init(num_vertices(__g)) }; // intentional ADL }; @@ -1735,7 +1776,7 @@ namespace _NumVertices { }; template - concept _Has_id_ADL = _HasClassOrEnumType<_G> // + concept _Has_id_ADL = _HasClassOrEnumType<_G> // && requires(_G&& __g, partition_id_t<_G> pid) { { _Fake_copy_init(num_vertices(__g, pid)) }; // intentional ADL }; @@ -1807,7 +1848,7 @@ namespace _NumVertices { static_assert(_Strat_id == _St_id::_Auto_eval); if constexpr (_Strat_id == _St_id::_Non_member) { - return num_vertices(__g, pid); // intentional ADL + return num_vertices(__g, pid); // intentional ADL } else if constexpr (_Strat_id == _St_id::_Auto_eval) { return size(vertices(__g, pid)); // default impl } else { @@ -1835,7 +1876,7 @@ namespace _NumVertices { if constexpr (_Strat_id == _St_ref::_Member) { return __g.num_vertices(); } else if constexpr (_Strat_id == _St_ref::_Non_member) { - return num_vertices(__g); // intentional ADL + return num_vertices(__g); // intentional ADL } else if constexpr (_Strat_id == _St_ref::_Auto_eval) { return size(vertices(__g)); // default impl } else { @@ -1866,7 +1907,7 @@ namespace _Degree { { _Fake_copy_init(u.degree(__g)) }; }; template - concept _Has_ref_ADL = _HasClassOrEnumType<_G> // + concept _Has_ref_ADL = _HasClassOrEnumType<_G> // && requires(_G&& __g, const vertex_reference_t<_G>& u) { { _Fake_copy_init(degree(__g, u)) }; // intentional ADL }; @@ -1877,7 +1918,7 @@ namespace _Degree { }; template - concept _Has_id_ADL = _HasClassOrEnumType<_G> // + concept _Has_id_ADL = _HasClassOrEnumType<_G> // && requires(_G&& __g, const vertex_id_t<_G>& uid) { { _Fake_copy_init(degree(__g, uid)) }; // intentional ADL }; @@ -1950,7 +1991,7 @@ namespace _Degree { if constexpr (_Strat_ref == _St_ref::_Member) { return u.degree(__g); } else if constexpr (_Strat_ref == _St_ref::_Non_member) { - return degree(__g, u); // intentional ADL + return degree(__g, u); // intentional ADL } else if constexpr (_Strat_ref == _St_ref::_Auto_eval) { return size(edges(__g, u)); // default impl } else { @@ -1978,7 +2019,7 @@ namespace _Degree { constexpr _St_id _Strat_id = _Choice_id<_G&>._Strategy; if constexpr (_Strat_id == _St_id::_Non_member) { - return degree(__g, uid); // intentional ADL + return degree(__g, uid); // intentional ADL } else if constexpr (_Strat_id == _St_id::_Auto_eval) { return size(edges(__g, uid)); // default impl } else { @@ -2011,7 +2052,7 @@ namespace _Vertex_value { { _Fake_copy_init(u.vertex_value(__g)) }; }; template - concept _Has_ref_ADL = _HasClassOrEnumType<_G> // + concept _Has_ref_ADL = _HasClassOrEnumType<_G> // && requires(_G&& __g, vertex_reference_t<_G> u) { { _Fake_copy_init(vertex_value(__g, u)) }; // intentional ADL }; @@ -2091,7 +2132,7 @@ namespace _Edge_value { { _Fake_copy_init(uv.edge_value(__g)) }; }; template - concept _Has_adjl_ref_ADL = _HasClassOrEnumType<_G> // + concept _Has_adjl_ref_ADL = _HasClassOrEnumType<_G> // && requires(_G&& __g, edge_reference_t<_G> uv) { { _Fake_copy_init(edge_value(__g, uv)) }; // intentional ADL }; @@ -2106,7 +2147,7 @@ namespace _Edge_value { { _Fake_copy_init(__e.edge_value()) }; }; template - concept _Has_edgl_ref_ADL = _HasClassOrEnumType<_E> // + concept _Has_edgl_ref_ADL = _HasClassOrEnumType<_E> // && requires(_E&& __e) { { _Fake_copy_init(edge_value(__e)) }; // intentional ADL }; @@ -2195,7 +2236,7 @@ namespace _Edge_value { } else if constexpr (_Strat_ref == _St_adjl_ref::_Non_member) { return edge_value(__g, uv); // intentional ADL } else if constexpr (_Strat_ref == _St_adjl_ref::_Auto_eval) { - return uv; // intentional ADL + return uv; // intentional ADL } else { static_assert(_AlwaysFalse<_G>, "edge_value(g,uv) must be defined for the graph"); } @@ -2224,7 +2265,7 @@ namespace _Edge_value { } else if constexpr (_Strat_ref == _St_edgl_ref::_Non_member) { return edge_value(__e); // intentional ADL } else if constexpr (_Strat_ref == _St_edgl_ref::_Tuple_id) { - return get<2>(__e); // first element of tuple/pair + return get<2>(__e); // first element of tuple/pair } else if constexpr (_Strat_ref == _St_edgl_ref::_EDesc_id) { return __e.value; } else { @@ -2271,7 +2312,7 @@ namespace _Graph_value { { _Fake_copy_init(__g.graph_value()) }; }; template - concept _Has_ref_ADL = _HasClassOrEnumType<_G> // + concept _Has_ref_ADL = _HasClassOrEnumType<_G> // && requires(_G&& __g) { { _Fake_copy_init(graph_value(__g)) }; // intentional ADL }; @@ -2343,7 +2384,7 @@ namespace _Num_partitions { { _Fake_copy_init(__g.num_partitions()) }; }; template - concept _Has_ref_ADL = _HasClassOrEnumType<_G> // + concept _Has_ref_ADL = _HasClassOrEnumType<_G> // && requires(_G&& __g) { { _Fake_copy_init(num_partitions(__g)) }; // intentional ADL }; @@ -2397,7 +2438,7 @@ namespace _Num_partitions { } else if constexpr (_Strat_ref == _St_ref::_Non_member) { return num_partitions(__g); // intentional ADL } else if constexpr (_Strat_ref == _St_ref::_Auto_eval) { - return vertex_id_t<_G>(1); // default impl + return vertex_id_t<_G>(1); // default impl } else { static_assert(_AlwaysFalse<_G>, "num_partitions(g) is not defined and the default implementation cannot be evaluated"); @@ -2427,7 +2468,7 @@ namespace _HasEdge { { _Fake_copy_init(__g.has_edge()) }; }; template - concept _Has_ref_ADL = _HasClassOrEnumType<_G> // + concept _Has_ref_ADL = _HasClassOrEnumType<_G> // && requires(_G&& __g) { { _Fake_copy_init(has_edge(__g)) }; // intentional ADL }; diff --git a/tests/descriptor_tests.cpp b/tests/descriptor_tests.cpp index c8ca778..7091fd9 100644 --- a/tests/descriptor_tests.cpp +++ b/tests/descriptor_tests.cpp @@ -8,8 +8,9 @@ #include "graph/detail/descriptor.hpp" +#define ENABLE_DESCRIPTOR_TESTS 0 + using namespace graph; -using namespace graph::detail; using std::move; using std::conditional_t; @@ -53,7 +54,15 @@ I advance(I it, std::iter_difference_t n) { return it; } +TEST_CASE("Tuple tail") { + using Tuple = std::tuple; + using Tail = tuple_tail_t; + static_assert(is_same_v>); + + Tuple t{1, 2.0, '3'}; +} +#if ENABLE_DESCRIPTOR_TESTS TEMPLATE_TEST_CASE("Identifier iterator for contiguous container vector", "[descriptor]", (vector), @@ -607,7 +616,7 @@ TEMPLATE_TEST_CASE("continuous descriptor range vector", "[descriptor]", (v using Iterator = _descriptor_iterator>; using difference_type = typename iterator_traits::difference_type; Container v = {1, 2, 3, 4, 5}; - auto descriptors = descriptor_view_old(v); + auto descriptors = descriptor_subrange_view(v); difference_type i = 0; SECTION("descriptor std for") { @@ -634,7 +643,7 @@ TEMPLATE_TEST_CASE("bidirectional descriptor range list", "[descriptor]", ( using Iterator = _descriptor_iterator>; using difference_type = typename iterator_traits::difference_type; Container v = {1, 2, 3, 4, 5}; - auto descriptors = descriptor_view_old(v); + auto descriptors = descriptor_subrange_view(v); difference_type i = 0; SECTION("descriptor std for") { @@ -664,12 +673,12 @@ TEMPLATE_TEST_CASE("All simple values", (list), (const list)) { using Container = TestType; - using IdentifierView = descriptor_view_old; + using IdentifierView = descriptor_subrange_view; using Iterator = _descriptor_iterator>; using descriptor_type = typename IdentifierView::descriptor_type; // integral or iterator using difference_type = typename iterator_traits::difference_type; Container v = {1, 2, 3, 4, 5}; - auto descriptors = descriptor_view_old(v); + auto descriptors = descriptor_subrange_view(v); difference_type i = 0; SECTION("descriptor std for") { @@ -698,12 +707,12 @@ TEMPLATE_TEST_CASE("All simple values", TEMPLATE_TEST_CASE("All map-like containers", "[descriptor]", (map), (const map)) { using Container = TestType; - using IdentifierView = descriptor_view_old; + using IdentifierView = descriptor_subrange_view; using Iterator = _descriptor_iterator>; using descriptor_type = typename IdentifierView::descriptor_type; // integral or iterator using difference_type = typename iterator_traits::difference_type; Container v = {{0, 1}, {1, 2}, {2, 3}, {3, 4}, {4, 5}}; - auto descriptors = descriptor_view_old(v); + auto descriptors = descriptor_subrange_view(v); difference_type i = 0; SECTION("descriptor std for") { @@ -730,12 +739,12 @@ TEMPLATE_TEST_CASE("All map-like containers", "[descriptor]", (map), ( //TEMPLATE_TEST_CASE("All simple values", "[descriptor]", (forward_list), (const forward_list)) { // using Container = TestType; -// using IdentifierView = descriptor_view_old; +// using IdentifierView = descriptor_subrange_view; // using Iterator = _descriptor_iterator>; // using descriptor_type = typename IdentifierView::descriptor_type; // integral or iterator // using difference_type = typename iterator_traits::difference_type; // Container v = {5, 4, 3, 2, 1}; -// auto descriptors = descriptor_view_old(v); +// auto descriptors = descriptor_subrange_view(v); // difference_type i = 0; // // SECTION("descriptor std for") { @@ -761,11 +770,11 @@ TEMPLATE_TEST_CASE("All map-like containers", "[descriptor]", (map), ( //} -#if 0 +# if 0 TEST_CASE("example") { using G = vector; G g = {1, 2, 3, 4, 5}; - auto V = descriptor_view_old(g); + auto V = descriptor_subrange_view(g); for (auto&& uid : V) { auto id = vertex_id(g, uid); @@ -792,7 +801,7 @@ for (auto&& u : vertices(a)) { } } -// for(auto&& u : descriptor_view_old(a)) +// for(auto&& u : descriptor_subrange_view(a)) for (auto&& u : vertices(a)) { // u is a vertex descriptor/descriptor = index/iterator = graph_traits::vertex_info for (auto&& uv : edges(a,u)) { @@ -804,8 +813,10 @@ for (auto&& u : vertices(a)) { template auto vertices(const G& g) { - return descriptor_view_old(g); + return descriptor_subrange_view(g); } -#endif // 0 +# endif // 0 + +#endif // ENABLE_DESCRIPTOR_TESTS