diff --git a/include/graph/adj_list/adjacency_list_concepts.hpp b/include/graph/adj_list/adjacency_list_concepts.hpp index fd00b00..a049cf1 100644 --- a/include/graph/adj_list/adjacency_list_concepts.hpp +++ b/include/graph/adj_list/adjacency_list_concepts.hpp @@ -20,34 +20,33 @@ namespace graph::adj_list { // ============================================================================= /** - * @brief Concept for edge descriptors - * - * An edge is an edge descriptor that provides access to both source and target vertices. - * All edge descriptors in the graph library provide these operations. - * + * @brief Concept for edge types + * + * An edge is any type for which source_id(g, e) and target_id(g, e) are valid expressions. + * This includes adj_list edge_descriptors, edge_list descriptors, edge_data aggregates, + * tuple/pair representations, and any user-defined type with appropriate CPO support. + * * Requirements: - * - e must be an edge_descriptor * - source_id(g, e) must be valid (returns source vertex ID) - * - source(g, e) must be valid (returns source vertex descriptor) * - target_id(g, e) must be valid (returns target vertex ID) - * - target(g, e) must be valid (returns target vertex descriptor) - * + * * Note: Return types are not constrained to allow better compiler error messages. - * + * Algorithms that additionally need vertex descriptors (source(g,e) / target(g,e)) + * should add those requirements explicitly beyond this concept. + * * Examples: - * - Edge in adjacency list: edge_descriptor>::iterator, int> - * - Edge in edge list: edge_descriptor>::iterator> - * - Weighted edge: edge_descriptor with value access - * - * @tparam G Graph type - * @tparam E Edge type (must be edge_descriptor) + * - adj_list edge_descriptor + * - edge_list edge_descriptor + * - edge_data + * - std::tuple (source=get<0>, target=get<1>) + * + * @tparam G Graph (or edge-list container) type + * @tparam E Edge type */ template -concept edge = is_edge_descriptor_v> && requires(G& g, const E& e) { +concept edge = requires(G& g, const E& e) { source_id(g, e); - source(g, e); target_id(g, e); - target(g, e); }; // ============================================================================= diff --git a/tests/edge_list/test_edge_list_concepts.cpp b/tests/edge_list/test_edge_list_concepts.cpp index 9f81ddd..97a6ed6 100644 --- a/tests/edge_list/test_edge_list_concepts.cpp +++ b/tests/edge_list/test_edge_list_concepts.cpp @@ -2,6 +2,7 @@ #include #include #include +#include #include #include @@ -158,3 +159,30 @@ TEST_CASE("has_edge_value runtime behavior", "[edge_list][runtime]") { auto val = graph::edge_value(edges, e); REQUIRE(val == 3.14); } + +// ============================================================================= +// Interop with adj_list::edge concept (I.2) +// ============================================================================= + +TEST_CASE("edge_list types satisfy adj_list::edge concept", "[edge_list][concepts][interop]") { + // After dropping the is_edge_descriptor_v gate, any type whose elements + // support source_id(g,e) and target_id(g,e) satisfies edge. + + // tuple + using tuple_el = std::vector>; + STATIC_REQUIRE(adj_list::edge>); + + // pair + using pair_el = std::vector>; + STATIC_REQUIRE(adj_list::edge>); + + // edge_data with value + using ed_type = graph::edge_data; + using ed_el = std::vector; + STATIC_REQUIRE(adj_list::edge); + + // edge_list::edge_descriptor + using desc_type = edge_list::edge_descriptor; + using desc_el = std::vector; + STATIC_REQUIRE(adj_list::edge); +}