22// This file is part of the CPP-GL project (https://github.com/SpectraL519/cpp-gl).
33// Licensed under the MIT License. See the LICENSE file in the project root for full license information.
44
5+ // / @file gl/edge_descriptor.hpp
6+ // / @brief Defines the edge_descriptor class and related aliases, providing a lightweight wrapper for edges in graphs.
7+
58#pragma once
69
710#include " gl/attributes/force_inline.hpp"
1619
1720namespace gl {
1821
22+ // / @ingroup GL GL-Core
23+ // / @brief A lightweight wrapper representing a graph edge with its endpoints and optional properties.
24+ // /
25+ // / **Module:** Part of the @ref GL-Core "Core Graph Components" group.
26+ // /
27+ // / The `edge_descriptor` class provides a type-safe and efficient way to represent
28+ // / edges in both directed and undirected graph structures. It encapsulates the unique
29+ // / identifier of the edge, its source and target vertices, and optional property data.
30+ // /
31+ // / > [!WARNING] This class is not intended to be instantiated directly.
32+ // / >
33+ // / > Instead, `edge_descriptor` objects should be retrieved from the @ref gl::graph class instance that owns the given edge.
34+ // /
35+ // / ### Example Usage
36+ // / ```cpp
37+ // / std::cout << gl::io::verbose << gl::io::with_edge_properties;
38+ // / for (auto v : graph.vertices()) {
39+ // / for (auto e : graph.out_edges(v)) { // (1)!
40+ // / if (e.is_loop()) // (2)!
41+ // / e->weight = 0.0;
42+ // / else
43+ // / e->weight += 1.5; // (3)!
44+ // /
45+ // / std::cout << e << "\n"; // (4)!
46+ // / }
47+ // / }
48+ // / ```
49+ // /
50+ // / 1\. Retrieve edges using valid `graph` traversal methods like `out_edges()`.
51+ // /
52+ // / 2\. Utilize built-in edge utility methods to evaluate the edge type.
53+ // /
54+ // / 3\. Access and modify custom property fields via the arrow operator `->`.
55+ // /
56+ // / 4\. Print the detailed, formatted edge data using the applied stream manipulators.
57+ // / *Example output:* `[id: 12 | source: 3, target: 7 | weight: 1.5]`
58+ // /
59+ // / ### Template Parameters
60+ // / | Parameter | Description | Default | Constraint |
61+ // / | :------------- | :--- | :--- | :--- |
62+ // / | DirectionalTag | Tag specifying if the edge is directed or undirected. | @ref gl::directed_t "directed_t" | [**c_graph_directional_tag**](gl_traits.md#gl-traits-c-graph-directional-tag) |
63+ // / | Properties | The type of property data attached to the edge. | @ref gl::empty_properties "empty_properties" | [**c_properties**](gl_traits.md#gl-traits-c-properties) |
64+ // / | IdType | The underlying integer type used for the IDs. | @ref gl::default_id_type "default_id_type" | [**c_id_type**](gl_traits.md#gl-traits-c-id-type) |
65+ // /
66+ // / ### See Also
67+ // / * @ref gl::vertex_descriptor : For the corresponding vertex wrapper class.
68+ // / * @ref gl::graph : For the owning graph class that manages edge descriptors.
1969template <
2070 traits::c_graph_directional_tag DirectionalTag = directed_t ,
2171 traits::c_properties Properties = empty_properties,
2272 traits::c_id_type IdType = default_id_type>
2373class edge_descriptor final {
2474public:
75+ // / @brief The fully instantiated type of this edge descriptor.
2576 using type = edge_descriptor<DirectionalTag, Properties, IdType>;
77+ // / @brief The identifier type used for edges and vertices.
2678 using id_type = IdType;
79+ // / @brief The tag denoting whether the edge is directed or undirected.
2780 using directional_tag = DirectionalTag;
81+ // / @brief The type of properties associated with the edge.
2882 using properties_type = Properties;
2983
3084 friend directional_tag;
3185
86+ // / @brief Default constructor, initializes to an invalid edge.
3287 edge_descriptor () {
3388 *this = edge_descriptor::invalid ();
3489 }
3590
91+ // / @brief Constructs an edge descriptor with the given IDs (for empty properties).
92+ // / @param id The unique identifier for the edge.
93+ // / @param source The unique identifier for the source vertex.
94+ // / @param target The unique identifier for the target vertex.
3695 explicit edge_descriptor (const id_type id, const id_type source, const id_type target)
3796 requires(traits::c_empty_properties<properties_type>)
3897 : _id(id), _vertices(source, target) {}
3998
99+ // / @brief Constructs an edge descriptor with the given IDs and properties.
100+ // / @param id The unique identifier for the edge.
101+ // / @param source The unique identifier for the source vertex.
102+ // / @param target The unique identifier for the target vertex.
103+ // / @param properties A reference to the property data to associate with this edge.
40104 explicit edge_descriptor (
41105 const id_type id, const id_type source, const id_type target, properties_type& properties
42106 )
43107 requires(traits::c_non_empty_properties<properties_type>)
44108 : _id(id), _vertices(source, target), _properties(properties) {}
45109
110+ // / @brief Returns an invalid edge descriptor (for empty properties).
111+ // / @return An `edge_descriptor` holding `invalid_id` for edge and vertex IDs.
46112 [[nodiscard]] gl_attr_force_inline static edge_descriptor invalid () noexcept
47113 requires(traits::c_empty_properties<properties_type>)
48114 {
49115 return edge_descriptor (invalid_id, invalid_id, invalid_id);
50116 }
51117
118+ // / @brief Returns an invalid edge descriptor (for non-empty properties).
119+ // / @return An `edge_descriptor` holding `invalid_id` endpoints and empty properties.
52120 [[nodiscard]] gl_attr_force_inline static edge_descriptor invalid () noexcept
53121 requires(traits::c_non_empty_properties<properties_type>)
54122 {
55123 static properties_type invalid_properties{};
56124 return edge_descriptor (invalid_id, invalid_id, invalid_id, invalid_properties);
57125 }
58126
127+ // / @brief Copy constructor.
59128 edge_descriptor (const edge_descriptor&) = default ;
129+ // / @brief Copy assignment operator.
60130 edge_descriptor& operator =(const edge_descriptor&) = default ;
61131
132+ // / @brief Move constructor.
62133 edge_descriptor (edge_descriptor&&) noexcept = default ;
134+ // / @brief Move assignment operator.
63135 edge_descriptor& operator =(edge_descriptor&&) noexcept = default ;
64136
137+ // / @brief Destructor.
65138 ~edge_descriptor () = default ;
66139
140+ // / @brief Equality comparison operator for directed edges.
141+ // / @param other The edge descriptor to compare against.
142+ // / @return `true` if IDs and exact endpoint pairs match, `false` otherwise.
67143 [[nodiscard]] bool operator ==(const edge_descriptor& other) const noexcept
68144 requires (traits::c_directed_edge<type>)
69145 {
70146 return this ->_id == other._id and (this ->_vertices == other._vertices );
71147 }
72148
149+ // / @brief Equality comparison operator for undirected edges.
150+ // / @param other The edge descriptor to compare against.
151+ // / @return `true` if IDs and endpoint pairs match (order independent), `false` otherwise.
73152 [[nodiscard]] bool operator ==(const edge_descriptor& other) const noexcept
74153 requires (traits::c_undirected_edge<type>)
75154 {
@@ -78,32 +157,46 @@ class edge_descriptor final {
78157 or (this ->_vertices == other.incident_vertices_r ()));
79158 }
80159
160+ // / @brief Boolean conversion operator.
161+ // / @return `true` if the edge is valid.
81162 [[nodiscard]] gl_attr_force_inline operator bool () const noexcept {
82163 return this ->is_valid ();
83164 }
84165
166+ // / @brief Checks if the edge represents a directed connection.
167+ // / @return `true` if `DirectionalTag` is `directed_t`.
85168 [[nodiscard]] constexpr bool is_directed () const noexcept {
86169 return traits::c_directed_edge<type>;
87170 }
88171
172+ // / @brief Checks if the edge represents an undirected connection.
173+ // / @return `true` if `DirectionalTag` is `undirected_t`.
89174 [[nodiscard]] constexpr bool is_undirected () const noexcept {
90175 return traits::c_undirected_edge<type>;
91176 }
92177
178+ // / @brief Checks if the edge descriptor is valid.
179+ // / @return `true` if the edge ID and both endpoints are not equal to `invalid_id`.
93180 [[nodiscard]] bool is_valid () const noexcept {
94181 return this ->_id != invalid_id and this ->_vertices .first != invalid_id
95182 and this ->_vertices .second != invalid_id;
96183 }
97184
185+ // / @brief Returns the edge ID.
186+ // / @return The underlying integer ID of the edge.
98187 [[nodiscard]] gl_attr_force_inline id_type id () const noexcept {
99188 return this ->_id ;
100189 }
101190
191+ // / @brief Retrieves the endpoints of the edge as a pair.
192+ // / @return A `homogeneous_pair` representing `(source, target)`.
102193 [[nodiscard]] gl_attr_force_inline homogeneous_pair<id_type> incident_vertices (
103194 ) const noexcept {
104195 return this ->_vertices ;
105196 }
106197
198+ // / @brief Retrieves the reversed endpoints of the edge as a pair.
199+ // / @return A `homogeneous_pair` representing `(target, source)`.
107200 [[nodiscard]] gl_attr_force_inline homogeneous_pair<id_type> incident_vertices_r (
108201 ) const noexcept {
109202 return std::make_pair (this ->_vertices .second , this ->_vertices .first );
@@ -112,16 +205,24 @@ class edge_descriptor final {
112205 // clang-format off
113206 // gl_attr_force_inline misplacement
114207
208+ // / @brief Returns the source vertex ID.
209+ // / @return The underlying integer ID of the source vertex.
115210 [[nodiscard]] gl_attr_force_inline const id_type source () const noexcept {
116211 return this ->_vertices .first ;
117212 }
118213
214+ // / @brief Returns the target vertex ID.
215+ // / @return The underlying integer ID of the target vertex.
119216 [[nodiscard]] gl_attr_force_inline const id_type target () const noexcept {
120217 return this ->_vertices .second ;
121218 }
122219
123220 // clang-format on
124221
222+ // / @brief Gets the other endpoint of the edge given one of its incident vertices.
223+ // / @param vertex_id The ID of one incident vertex.
224+ // / @return The ID of the opposite vertex.
225+ // / @throws std::invalid_argument If the provided `vertex_id` is not incident to this edge.
125226 [[nodiscard]] const id_type other (const id_type vertex_id) const {
126227 if (vertex_id == this ->_vertices .first )
127228 return this ->_vertices .second ;
@@ -132,47 +233,69 @@ class edge_descriptor final {
132233 throw std::invalid_argument (std::format (" Got invalid vertex id: {}" , vertex_id));
133234 }
134235
236+ // / @brief Checks if a specific vertex is incident to this edge.
237+ // / @param vertex_id The vertex ID to query.
238+ // / @return `true` if the vertex is either the source or the target, `false` otherwise.
135239 [[nodiscard]] gl_attr_force_inline bool is_incident_with (const id_type vertex_id
136240 ) const noexcept {
137241 return vertex_id == this ->_vertices .first or vertex_id == this ->_vertices .second ;
138242 }
139243
140- // true if the given vertex is the `source` of the edge
244+ // / @brief Checks if the given vertex acts as the source for this edge.
245+ // / @param vertex_id The vertex ID to query.
246+ // / @return `true` if the vertex is the source (or any endpoint for undirected graphs).
141247 [[nodiscard]] gl_attr_force_inline bool is_incident_from (const id_type vertex_id
142248 ) const noexcept {
143249 return directional_tag::is_incident_from (*this , vertex_id);
144250 }
145251
146- // true if the given vertex is the `target` vertex of the edge
252+ // / @brief Checks if the given vertex acts as the target for this edge.
253+ // / @param vertex_id The vertex ID to query.
254+ // / @return `true` if the vertex is the target (or any endpoint for undirected graphs).
147255 [[nodiscard]] gl_attr_force_inline bool is_incident_to (const id_type vertex_id) const noexcept {
148256 return directional_tag::is_incident_to (*this , vertex_id);
149257 }
150258
259+ // / @brief Checks if the edge is a self-loop.
260+ // / @return `true` if the source and target are the same vertex, `false` otherwise.
151261 [[nodiscard]] gl_attr_force_inline bool is_loop () const noexcept {
152262 return this ->_vertices .first == this ->_vertices .second ;
153263 }
154264
265+ // / @brief Returns a reference to the edge properties.
266+ // / @return A reference to the associated `properties_type`.
267+ // / @throws std::logic_error If the edge descriptor is invalid.
155268 [[nodiscard]] gl_attr_force_inline properties_type& properties () const
156269 requires(traits::c_non_empty_properties<properties_type>)
157270 {
158271 this ->_validate ();
159272 return this ->_properties .get ();
160273 }
161274
275+ // / @brief Arrow operator for accessing properties.
276+ // / @return A pointer to the associated `properties_type`.
277+ // / @throws std::logic_error If the edge descriptor is invalid.
162278 [[nodiscard]] gl_attr_force_inline properties_type* operator ->() const
163279 requires (traits::c_non_empty_properties<properties_type>)
164280 {
165281 this ->_validate ();
166282 return &this ->_properties .get ();
167283 }
168284
285+ // / @brief Dereference operator for accessing properties.
286+ // / @return A reference to the associated `properties_type`.
287+ // / @throws std::logic_error If the edge descriptor is invalid.
169288 [[nodiscard]] gl_attr_force_inline properties_type& operator *() const
170289 requires (traits::c_non_empty_properties<properties_type>)
171290 {
172291 this ->_validate ();
173292 return this ->_properties .get ();
174293 }
175294
295+ // / @brief Output stream operator for edge descriptors.
296+ // / @param os The output stream.
297+ // / @param edge The edge descriptor to write.
298+ // / @return A reference to the output stream.
176299 friend std::ostream& operator <<(std::ostream& os, const edge_descriptor& edge) {
177300 using enum io::detail::option_bit;
178301
@@ -256,11 +379,31 @@ class edge_descriptor final {
256379 std::reference_wrapper<properties_type>> _properties;
257380};
258381
382+ // / @ingroup GL GL-Core
383+ // / @brief Type alias for a directed edge descriptor.
384+ // /
385+ // / Pre-binds the `DirectionalTag` of `edge_descriptor` to `directed_t`.
386+ // /
387+ // / ### Template Parameters
388+ // / | Parameter | Description | Default | Constraint |
389+ // / | :--------- | :--- | :--- | :--- |
390+ // / | Properties | The type of property data attached to the edge. | @ref gl::empty_properties "empty_properties" | [**c_properties**](gl_traits.md#gl-traits-c-properties) |
391+ // / | IdType | The underlying integer type used for the IDs. | @ref gl::default_id_type "default_id_type" | [**c_id_type**](gl_traits.md#gl-traits-c-id-type) |
259392template <
260393 traits::c_properties Properties = empty_properties,
261394 traits::c_id_type IdType = default_id_type>
262395using directed_edge = edge_descriptor<directed_t , Properties, IdType>;
263396
397+ // / @ingroup GL GL-Core
398+ // / @brief Type alias for an undirected edge descriptor.
399+ // /
400+ // / Pre-binds the `DirectionalTag` of `edge_descriptor` to `undirected_t`.
401+ // /
402+ // / ### Template Parameters
403+ // / | Parameter | Description | Default | Constraint |
404+ // / | :--------- | :--- | :--- | :--- |
405+ // / | Properties | The type of property data attached to the edge. | @ref gl::empty_properties "empty_properties" | [**c_properties**](gl_traits.md#gl-traits-c-properties) |
406+ // / | IdType | The underlying integer type used for the IDs. | @ref gl::default_id_type "default_id_type" | [**c_id_type**](gl_traits.md#gl-traits-c-id-type) |
264407template <
265408 traits::c_properties Properties = empty_properties,
266409 traits::c_id_type IdType = default_id_type>
0 commit comments