Skip to content

Commit 36bb238

Browse files
committed
gl alg common docs + return_type -> result_type
1 parent f7f266c commit 36bb238

11 files changed

Lines changed: 185 additions & 25 deletions

File tree

include/gl/algorithm/core.hpp

Lines changed: 110 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,9 @@
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/algorithm/core.hpp
6+
/// @brief Core data structures and types used to control and track graph algorithm execution.
7+
58
#pragma once
69

710
#include "gl/attributes/force_inline.hpp"
@@ -15,80 +18,179 @@ namespace gl::algorithm {
1518

1619
// --- general types ---
1720

21+
/// @ingroup GL GL-Algorithm
22+
/// @brief A tag type used to explicitly indicate the absence of a callback function.
23+
///
24+
/// > [!NOTE] Performance vs. Empty Lambdas
25+
/// >
26+
/// > Passing `empty_callback{}` is **not** the same as passing an empty lambda (e.g., `[]{}`).
27+
/// > Using this explicit tag type allows internal algorithms to use compile-time checks to
28+
/// > completely eliminate the callback invocation branch at compile time. This guarantees
29+
/// > strict zero-cost abstractions, vastly improves performance in unoptimized/debug builds,
30+
/// > and speeds up compilation times.
1831
struct empty_callback {};
1932

33+
/// @ingroup GL GL-Algorithm GL-Types
34+
/// @brief Represents a generic tri-state decision for control flow.
35+
///
36+
/// Used by custom predicates to determine how to proceed with a given item, execution step, or operation.
37+
/// While heavily utilized in graph traversal algorithms, it is entirely decoupled from graph-specific
38+
/// logic and can be used in any generic context requiring explicit accept/reject/abort semantics.
2039
struct decision {
21-
enum class eval : std::uint8_t { accept, reject, abort };
40+
/// @brief The underlying evaluation states.
41+
enum class eval : std::uint8_t {
42+
accept, /// < Proceed with the current element or operation.
43+
reject, /// < Skip the current element but continue the overall process.
44+
abort /// < Immediately terminate the entire process or algorithm.
45+
};
2246
using enum eval;
2347

48+
/// @brief Constructs a decision from an explicit evaluation state.
49+
/// @param value The tri-state evaluation.
2450
constexpr decision(const eval value) : value(value) {}
2551

52+
/// @brief Constructs a decision from a boolean value.
53+
/// @param value `true` maps to `accept`, `false` maps to `reject`.
2654
constexpr decision(const bool value) : value(value ? eval::accept : eval::reject) {}
2755

56+
/// @brief Assigns a boolean value to the decision.
57+
/// @param value `true` maps to `accept`, `false` maps to `reject`.
58+
/// @return A reference to this decision.
2859
constexpr decision& operator=(const bool value) {
2960
this->value = value ? eval::accept : eval::reject;
3061
return *this;
3162
}
3263

64+
/// @brief Evaluates the decision as a boolean.
65+
/// @return `true` if the decision is `accept`, `false` otherwise.
3366
[[nodiscard]] constexpr operator bool() const {
3467
return this->value == eval::accept;
3568
}
3669

70+
/// @brief Compares the decision against a specific evaluation state.
71+
/// @param value The state to compare against.
72+
/// @return `true` if the states match.
3773
[[nodiscard]] constexpr bool operator==(const eval value) const {
3874
return this->value == value;
3975
}
4076

77+
/// @brief The stored evaluation state.
4178
eval value;
4279
};
4380

44-
enum class result_discriminator : bool { ret = true, noret = false };
81+
/// @ingroup GL GL-Algorithm
82+
/// @brief Tag used to statically dictate whether an algorithm should return a constructed result or execute purely for side effects.
83+
///
84+
/// > [!NOTE] Namespace Availability
85+
/// >
86+
/// > Because this enum uses the `using enum` declaration, its members (`ret` and `noret`) are
87+
/// > injected directly into the `gl::algorithm` namespace. Hence, you can use `gl::algorithm::ret`
88+
/// > and `gl::algorithm::noret` directly without the `result_discriminator::` scope.
89+
///
90+
/// ### See Also
91+
/// - @ref gl::algorithm::result_type "result_type"
92+
/// - @ref gl::algorithm::non_void_result_type "non_void_result_type"
93+
enum class result_discriminator : bool {
94+
ret = true, /// < The algorithm should build and return a result (e.g., a predecessor map).
95+
noret = false /// < The algorithm will purely invoke callbacks and return void.
96+
};
4597
using enum result_discriminator;
4698

47-
template <result_discriminator Result, typename ReturnType>
48-
using return_type = std::conditional_t<Result == algorithm::ret, ReturnType, void>;
49-
50-
template <result_discriminator Result, typename ReturnType>
51-
using non_void_return_type =
52-
std::conditional_t<Result == algorithm::ret, ReturnType, std::monostate>;
99+
/// @ingroup GL GL-Algorithm
100+
/// @brief Resolves to the specified `ResultType` if `Result` is `ret`, otherwise resolves to `void`.
101+
///
102+
/// ### See Also
103+
/// - @ref gl::algorithm::result_discriminator "result_discriminator"
104+
/// - @ref gl::algorithm::non_void_result_type "non_void_result_type"
105+
template <result_discriminator Result, typename ResultType>
106+
using result_type = std::conditional_t<Result == algorithm::ret, ResultType, void>;
107+
108+
/// @ingroup GL GL-Algorithm
109+
/// @brief Resolves to the specified `ResultType` if `Result` is `ret`, otherwise resolves to `std::monostate`.
110+
///
111+
/// Useful for returning dummy values from conditionally compiled algorithm branches.
112+
///
113+
/// ### See Also
114+
/// - @ref gl::algorithm::result_discriminator "result_discriminator"
115+
/// - @ref gl::algorithm::result_type "result_type"
116+
template <result_discriminator Result, typename ResultType>
117+
using non_void_result_type =
118+
std::conditional_t<Result == algorithm::ret, ResultType, std::monostate>;
53119

54120
// --- traversal types ---
55121

122+
/// @ingroup GL GL-Algorithm
123+
/// @brief Maps a vertex ID to its predecessor's ID in a traversal tree.
124+
/// @tparam GraphType The type of the graph being traversed.
56125
template <traits::c_graph GraphType>
57126
using predecessors_map = std::vector<typename GraphType::id_type>;
58127

128+
/// @ingroup GL GL-Algorithm
129+
/// @brief Represents an active node in a search frontier (e.g., a BFS queue or DFS stack).
130+
/// @tparam GraphType The type of the graph being searched.
59131
template <traits::c_graph GraphType>
60132
struct search_node {
61133
using id_type = typename GraphType::id_type;
62134

135+
/// @brief Constructs a search node acting as a root (predecessor is itself).
136+
/// @param vertex_id The ID of the vertex.
63137
search_node(id_type vertex_id) : vertex_id(vertex_id), pred_id(vertex_id) {}
64138

139+
/// @brief Constructs a search node with an explicit predecessor.
140+
/// @param vertex_id The ID of the vertex.
141+
/// @param pred_id The ID of the vertex's predecessor.
65142
search_node(id_type vertex_id, id_type pred_id) : vertex_id(vertex_id), pred_id(pred_id) {}
66143

144+
/// @brief Checks if this node is the root of a search tree.
145+
/// @return `true` if the node is valid and its predecessor is itself, `false` otherwise.
67146
[[nodiscard]] gl_attr_force_inline bool is_root() const noexcept {
68147
return this->vertex_id != invalid_id and this->vertex_id == this->pred_id;
69148
}
70149

150+
/// @brief The ID of the vertex currently being searched.
71151
id_type vertex_id;
152+
/// @brief The ID of the predecessor from which this vertex was reached.
72153
id_type pred_id;
73154
};
74155

75156
// --- constants ---
76157

158+
/// @ingroup GL GL-Algorithm
159+
/// @brief Constant representing the absence of a root vertex ID for a specific ID type.
160+
///
161+
/// ### See Also
162+
/// - @ref gl::invalid_id_v "gl::invalid_id_v"
163+
/// - @ref gl::algorithm::no_root_t "no_root_t"
164+
/// - @ref gl::algorithm::no_root "no_root"
77165
template <traits::c_id_type IdType>
78166
inline constexpr IdType no_root_v = invalid_id_v<IdType>;
79167

168+
/// @ingroup GL GL-Algorithm
169+
/// @brief Tag type providing an implicit conversion to the appropriate `no_root_v` for any numeric ID type.
170+
///
171+
/// ### See Also
172+
/// - @ref gl::algorithm::no_root_v "no_root_v"
173+
/// - @ref gl::algorithm::no_root "no_root"
80174
struct no_root_t {
175+
/// @brief Implicitly converts to the numeric `no_root_v` constant.
81176
template <traits::c_id_type IdType>
82177
[[nodiscard]] constexpr operator IdType() const noexcept {
83178
return no_root_v<IdType>;
84179
}
85180

181+
/// @brief Checks if a given ID matches the `no_root_v` constant.
86182
template <traits::c_id_type IdType>
87183
[[nodiscard]] friend constexpr bool operator==(const IdType& lhs, no_root_t) noexcept {
88184
return lhs == no_root_v<IdType>;
89185
}
90186
};
91187

188+
/// @ingroup GL GL-Algorithm
189+
/// @brief Global constant representing the absence of a root vertex.
190+
///
191+
/// ### See Also
192+
/// - @ref gl::algorithm::no_root_v "no_root_v"
193+
/// - @ref gl::algorithm::no_root_t "no_root_t"
92194
inline constexpr no_root_t no_root{};
93195

94196
} // namespace gl::algorithm

include/gl/algorithm/traits.hpp

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,9 @@
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/algorithm/traits.hpp
6+
/// @brief C++20 concepts for validating custom callbacks and predicates passed to graph algorithms.
7+
58
#pragma once
69

710
#include "gl/algorithm/core.hpp"
@@ -10,24 +13,41 @@
1013

1114
namespace gl::traits {
1215

16+
/// @ingroup GL GL-Traits
17+
/// @brief Concept checking if a given type is the @ref gl::algorithm::empty_callback "empty_callback" tag.
1318
template <typename F>
1419
concept c_empty_callback = std::same_as<F, algorithm::empty_callback>;
1520

21+
/// @ingroup GL GL-Traits
22+
/// @brief Concept checking if a type is callable with specific arguments and returns a specific type.
23+
/// @tparam F The callable type.
24+
/// @tparam ReturnType The expected return type.
25+
/// @tparam Args The parameter types the callable must accept.
1626
template <typename F, typename ReturnType, typename... Args>
1727
concept c_callback = std::is_invocable_r_v<ReturnType, F, Args...>;
1828

29+
/// @ingroup GL GL-Traits
30+
/// @brief Concept allowing either a valid callback or the explicit absence of one through the use of @ref gl::algorithm::empty_callback "empty_callback".
1931
template <typename F, typename ReturnType, typename... Args>
2032
concept c_optional_callback = c_empty_callback<F> or c_callback<F, ReturnType, Args...>;
2133

34+
/// @ingroup GL GL-Traits
35+
/// @brief Concept checking if a type is a boolean predicate callable with specific arguments.
2236
template <typename F, typename... Args>
2337
concept c_predicate = std::predicate<F, Args...>;
2438

39+
/// @ingroup GL GL-Traits
40+
/// @brief Concept allowing either a valid boolean predicate or the explicit absence of one through the use of @ref gl::algorithm::empty_callback "empty_callback".
2541
template <typename F, typename... Args>
2642
concept c_optional_predicate = c_empty_callback<F> or c_predicate<F, Args...>;
2743

44+
/// @ingroup GL GL-Traits
45+
/// @brief Concept checking if a type is a predicate returning a @ref gl::algorithm::decision "decision".
2846
template <typename F, typename... Args>
2947
concept c_decision_predicate = std::is_invocable_r_v<algorithm::decision, F, Args...>;
3048

49+
/// @ingroup GL GL-Traits
50+
/// @brief Concept allowing either a valid decision predicate or the explicit absence of one.
3151
template <typename F, typename... Args>
3252
concept c_optional_decision_predicate = c_empty_callback<F> or c_decision_predicate<F, Args...>;
3353

include/gl/algorithm/traversal/breadth_first_search.hpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ template <
1616
traits::c_graph G,
1717
traits::c_optional_callback<void, typename G::id_type> PreVisitCallback = empty_callback,
1818
traits::c_optional_callback<void, typename G::id_type> PostVisitCallback = empty_callback>
19-
return_type<Result, predecessors_map<G>> breadth_first_search(
19+
result_type<Result, predecessors_map<G>> breadth_first_search(
2020
const G& graph,
2121
const typename G::id_type root_vertex_id = no_root,
2222
PreVisitCallback pre_visit = {},

include/gl/algorithm/traversal/depth_first_search.hpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ template <
1616
traits::c_graph G,
1717
traits::c_optional_callback<void, typename G::id_type> PreVisitCallback = empty_callback,
1818
traits::c_optional_callback<void, typename G::id_type> PostVisitCallback = empty_callback>
19-
return_type<Result, predecessors_map<G>> depth_first_search(
19+
result_type<Result, predecessors_map<G>> depth_first_search(
2020
const G& graph,
2121
const typename G::id_type root_vertex_id = no_root,
2222
PreVisitCallback pre_visit = {},
@@ -64,7 +64,7 @@ template <
6464
traits::c_graph G,
6565
traits::c_optional_callback<void, typename G::id_type> PreVisitCallback = empty_callback,
6666
traits::c_optional_callback<void, typename G::id_type> PostVisitCallback = empty_callback>
67-
return_type<Result, predecessors_map<G>> recursive_depth_first_search(
67+
result_type<Result, predecessors_map<G>> recursive_depth_first_search(
6868
const G& graph,
6969
const typename G::id_type root_vertex_id = no_root,
7070
PreVisitCallback pre_visit = {},

include/gl/algorithm/util.hpp

Lines changed: 41 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,9 @@
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/algorithm/util.hpp
6+
/// @brief Internal utilities and default behaviors used by the graph traversal algorithms.
7+
58
#pragma once
69

710
#include "gl/algorithm/core.hpp"
@@ -11,37 +14,66 @@
1114

1215
namespace gl::algorithm {
1316

17+
/// @ingroup GL GL-Algorithm
18+
/// @brief Initializes a predecessor map based on the static result discriminator.
19+
/// @tparam Result The compilation tag determining if the map should actually be built.
20+
/// @tparam G The type of the graph.
21+
/// @param graph The graph instance to size the map against.
22+
/// @return A fully sized and initialized `predecessors_map` if `Result == ret`, otherwise a dummy `std::monostate`.
1423
template <result_discriminator Result, traits::c_graph G>
15-
[[nodiscard]] gl_attr_force_inline non_void_return_type<Result, predecessors_map<G>>
24+
[[nodiscard]] gl_attr_force_inline non_void_result_type<Result, predecessors_map<G>>
1625
init_predecessors_map(const G& graph) {
17-
using return_t = non_void_return_type<Result, predecessors_map<G>>;
26+
using return_t = non_void_result_type<Result, predecessors_map<G>>;
1827
if constexpr (Result == ret)
1928
return return_t(graph.n_vertices(), invalid_id);
2029
else
2130
return return_t();
2231
}
2332

33+
/// @ingroup GL GL-Algorithm
34+
/// @brief Checks if a specific vertex was reached during a traversal.
35+
/// @tparam IdType The integral type of the vertex ID.
36+
/// @param pred_map The predecessor map populated by the traversal.
37+
/// @param vertex_id The vertex ID to query.
38+
/// @return `true` if the vertex has a valid assigned predecessor, `false` otherwise.
2439
template <traits::c_id_type IdType>
2540
[[nodiscard]] gl_attr_force_inline bool is_reachable(
2641
const traits::c_random_access_range_of<IdType> auto& pred_map, IdType vertex_id
2742
) noexcept {
2843
return pred_map[to_idx(vertex_id)] != invalid_id;
2944
}
3045

46+
/// @ingroup GL GL-Algorithm
47+
/// @brief Initializes a search frontier (queue or stack) with the starting root vertex.
48+
/// @tparam G The type of the graph.
49+
/// @tparam InitRangeType The underlying container type for the frontier.
50+
/// @param root_vertex_id The ID of the starting vertex.
51+
/// @return A container initialized with a single @ref search_node for the root vertex.
3152
template <
3253
traits::c_graph G,
3354
traits::c_forward_range_of<search_node<G>> InitRangeType = std::vector<search_node<G>>>
3455
[[nodiscard]] gl_attr_force_inline InitRangeType init_range(typename G::id_type root_vertex_id) {
3556
return InitRangeType{search_node<G>{root_vertex_id}};
3657
}
3758

59+
/// @ingroup GL GL-Algorithm
60+
/// @brief Generates a default lambda predicate that checks if a vertex has not yet been visited.
61+
/// @param visited A reference to the boolean array tracking visited vertices.
62+
/// @return A callable predicate evaluating to `true` if the vertex is unvisited.
3863
[[nodiscard]] gl_attr_force_inline auto default_visit_vertex_predicate(std::vector<bool>& visited) {
3964
return [&](traits::c_id_type auto vertex_id) -> bool { return not visited[to_idx(vertex_id)]; };
4065
}
4166

67+
/// @ingroup GL GL-Algorithm
68+
/// @brief Generates a default lambda callback that marks a vertex as visited and updates the predecessor map.
69+
/// @tparam G The type of the graph.
70+
/// @tparam Result The static discriminator indicating if the predecessor map should be updated.
71+
/// @param visited A reference to the boolean array tracking visited vertices.
72+
/// @param pred_map A reference to the active predecessor map.
73+
/// @return A callable callback that executes state updates upon visiting a vertex.
4274
template <traits::c_graph G, result_discriminator Result>
4375
[[nodiscard]] gl_attr_force_inline auto default_visit_callback(
44-
std::vector<bool>& visited, non_void_return_type<Result, predecessors_map<G>>& pred_map
76+
std::vector<bool>& visited, non_void_result_type<Result, predecessors_map<G>>& pred_map
4577
) {
4678
using id_type = typename G::id_type;
4779
return [&](id_type vertex_id, id_type pred_id) {
@@ -53,6 +85,12 @@ template <traits::c_graph G, result_discriminator Result>
5385
};
5486
}
5587

88+
/// @ingroup GL GL-Algorithm
89+
/// @brief Generates a default lambda predicate that checks if an adjacent vertex should be enqueued into the search frontier.
90+
/// @tparam G The type of the graph.
91+
/// @tparam AsResult If `true`, the generated predicate returns a @ref decision instead of a raw boolean.
92+
/// @param visited A reference to the boolean array tracking visited vertices.
93+
/// @return A callable predicate that returns `true` (or `decision::accept`) if the adjacent vertex has not been visited.
5694
template <traits::c_graph G, bool AsResult = false>
5795
[[nodiscard]] gl_attr_force_inline auto default_enqueue_vertex_predicate(std::vector<bool>& visited
5896
) {

include/hgl/algorithm/core.hpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,8 +27,8 @@ using gl::algorithm::decision;
2727
using gl::algorithm::result_discriminator;
2828
using enum result_discriminator;
2929

30-
using gl::algorithm::non_void_return_type;
31-
using gl::algorithm::return_type;
30+
using gl::algorithm::non_void_result_type;
31+
using gl::algorithm::result_type;
3232

3333
using gl::algorithm::no_root;
3434
using gl::algorithm::no_root_t;

include/hgl/algorithm/traversal/backward_search.hpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ template <
1818
traits::c_forward_range_of<typename H::id_type> RootRange = std::vector<typename H::id_type>,
1919
traits::c_optional_callback<void, const search_node<H>&> PreVisitCallback = empty_callback,
2020
traits::c_optional_callback<void, const search_node<H>&> PostVisitCallback = empty_callback>
21-
return_type<Result, search_tree<H>> backward_bfs(
21+
result_type<Result, search_tree<H>> backward_bfs(
2222
const H& hypergraph,
2323
const RootRange& root_vertices,
2424
const PreVisitCallback& pre_visit = {},
@@ -59,7 +59,7 @@ template <
5959
traits::c_forward_range_of<typename H::id_type> RootRange = std::vector<typename H::id_type>,
6060
traits::c_optional_callback<void, const search_node<H>&> PreVisitCallback = empty_callback,
6161
traits::c_optional_callback<void, const search_node<H>&> PostVisitCallback = empty_callback>
62-
return_type<Result, search_tree<H>> backward_dfs(
62+
result_type<Result, search_tree<H>> backward_dfs(
6363
const H& hypergraph,
6464
const RootRange& root_vertices,
6565
const PreVisitCallback& pre_visit = {},

0 commit comments

Comments
 (0)