Skip to content

Commit 1c8ec4f

Browse files
committed
wip: lightweight vertex descriptor
1 parent e220490 commit 1c8ec4f

5 files changed

Lines changed: 103 additions & 32 deletions

File tree

include/gl/graph.hpp

Lines changed: 65 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -23,12 +23,15 @@ class graph final {
2323
using implementation_type = typename implementation_tag::template type<traits_type>;
2424

2525
using vertex_type = typename traits_type::vertex_type;
26-
using vertex_ptr_type = typename traits_type::vertex_ptr_type;
27-
using vertex_properties_type = typename traits_type::vertex_properties_type;
26+
// using vetex_list_type = std::vector<vertex_type>;
27+
// using vertex_iterator_type =
28+
// types::dereferencing_iterator<typename vetex_list_type::const_iterator>;
2829

29-
using vetex_list_type = std::vector<vertex_ptr_type>;
30-
using vertex_iterator_type =
31-
types::dereferencing_iterator<typename vetex_list_type::const_iterator>;
30+
using vertex_properties_type = typename traits_type::vertex_properties_type;
31+
using vertex_properties_map_type = std::conditional_t<
32+
type_traits::is_default_properties_type_v<vertex_properties_type>,
33+
types::empty_properties_map,
34+
std::vector<std::unique_ptr<vertex_properties_type>>>;
3235

3336
// TODO: reverese iterators should be available for bidirectional ranges
3437

@@ -45,10 +48,24 @@ class graph final {
4548

4649
graph() = default;
4750

48-
graph(const types::size_type n_vertices) : _impl(n_vertices) {
51+
graph(const types::size_type n_vertices)
52+
requires(type_traits::is_default_properties_type_v<vertex_properties_type>)
53+
: _vertex_properties(), _impl(n_vertices) {
4954
this->_vertices.reserve(n_vertices);
50-
for (auto vertex_id = constants::initial_id; vertex_id < n_vertices; ++vertex_id)
51-
this->_vertices.push_back(detail::make_vertex<vertex_type>(vertex_id));
55+
for (auto id : std::views::iota(constants::initial_id, n_vertices))
56+
this->_vertices.emplace_back(vertex_id, this->_vertex_properties);
57+
}
58+
59+
graph(const types::size_type n_vertices)
60+
requires(not type_traits::is_default_properties_type_v<vertex_properties_type>)
61+
: _impl(n_vertices) {
62+
this->_vertices.reserve(n_vertices);
63+
this->_vertex_properties.reserve(n_vertices);
64+
for (auto [id, properties] : std::views::enumerate(this->_vertex_properties))
65+
this->_vertices.emplace_back(
66+
vertex_id,
67+
*this->_vertex_properties.emplace_back(std::make_unique<vertex_properties_type>())
68+
);
5269
}
5370

5471
graph(graph&&) = default;
@@ -59,7 +76,7 @@ class graph final {
5976
// --- general methods ---
6077

6178
[[nodiscard]] gl_attr_force_inline types::size_type n_vertices() const {
62-
return this->_vertices.size();
79+
return this->_n_vertices;
6380
}
6481

6582
[[nodiscard]] gl_attr_force_inline types::size_type n_unique_edges() const {
@@ -68,14 +85,29 @@ class graph final {
6885

6986
// --- vertex methods ---
7087

71-
[[nodiscard]] gl_attr_force_inline types::iterator_range<vertex_iterator_type> vertices(
72-
) const {
73-
return make_iterator_range(deref_cbegin(this->_vertices), deref_cend(this->_vertices));
88+
// TODO: return a view
89+
// [[nodiscard]] gl_attr_force_inline types::iterator_range<vertex_iterator_type> vertices(
90+
// ) const {
91+
// return make_iterator_range(deref_cbegin(this->_vertices), deref_cend(this->_vertices));
92+
// }
93+
94+
[[nodiscard]] gl_attr_force_inline auto vertices() const
95+
requires(type_traits::is_default_properties_type_v<vertex_properties_type>)
96+
{
97+
return std::views::enumerate(this->_vertex_properties) | std::views::transform([](auto [id, properties]) {
98+
return vertex_descriptor{id, *properties}; }));
99+
}
100+
101+
[[nodiscard]] gl_attr_force_inline auto vertices() const
102+
requires(not type_traits::is_default_properties_type_v<vertex_properties_type>)
103+
{
104+
return this->vertex_ids()
105+
| std::views::transform([](const types::id_type id) { return vertex_descriptor{id}; });
74106
}
75107

76108
[[nodiscard]] gl_attr_force_inline std::ranges::iota_view<types::id_type, types::id_type>
77109
vertex_ids() const {
78-
return std::views::iota(constants::initial_id, this->n_vertices());
110+
return std::views::iota(constants::initial_id, this->_n_vertices);
79111
}
80112

81113
// clang-format off
@@ -85,36 +117,40 @@ class graph final {
85117
const types::id_type vertex_id
86118
) const {
87119
this->_verify_vertex_id(vertex_id);
88-
return *this->_vertices[vertex_id];
120+
if constexpr (type_traits::is_default_properties_type_v<vertex_properties_type>)
121+
return vertex_descriptor{vertex_id};
122+
else
123+
return vertex_descriptor{vertex_id, *this->_vertex_properties[vertex_id]};
89124
}
90125

91126
// clang-format on
92127

93128
[[nodiscard]] gl_attr_force_inline bool has_vertex(const types::id_type vertex_id) const {
94-
return vertex_id < this->n_vertices();
129+
return vertex_id < this->_n_vertices;
95130
}
96131

97-
[[nodiscard]] gl_attr_force_inline bool has_vertex(const vertex_type& vertex) const {
98-
return this->has_vertex(vertex.id()) and &vertex == this->_vertices[vertex.id()].get();
99-
}
100-
101-
const vertex_type& add_vertex() {
132+
vertex_type add_vertex() {
102133
this->_impl.add_vertex();
103-
this->_vertices.push_back(detail::make_vertex<vertex_type>(this->n_vertices()));
104-
return *this->_vertices.back();
134+
return this->_vertices.emplace_back(
135+
this->n_vertices(), this->_vertex_properties.emplace_back()
136+
);
105137
}
106138

107139
const vertex_type& add_vertex(const vertex_properties_type& properties)
108140
requires(not type_traits::is_default_properties_type_v<vertex_properties_type>)
109141
{
110142
this->_impl.add_vertex();
111-
this->_vertices.push_back(detail::make_vertex<vertex_type>(this->n_vertices(), properties));
112-
return *this->_vertices.back();
143+
return vertex_descriptor{
144+
this->_n_vertices++,
145+
*this->_vertex_properties.emplace_back(
146+
std::make_unique<vertex_properties_type>(properties)
147+
)
148+
};
113149
}
114150

115151
void add_vertices(const types::size_type n) {
116152
this->_impl.add_vertices(n);
117-
this->_vertices.reserve(this->n_vertices() + n);
153+
this->_vertices.reserve(this->_n_vertices + n);
118154

119155
for (types::size_type _ = constants::begin_idx; _ < n; ++_)
120156
this->_vertices.push_back(detail::make_vertex<vertex_type>(this->n_vertices()));
@@ -644,7 +680,10 @@ class graph final {
644680
}
645681
}
646682

647-
vetex_list_type _vertices{};
683+
types::size_type _n_vertices = 0uz;
684+
[[no_unique_address]] vertex_properties_map_type _vertex_properties;
685+
// TODO: edge properties map
686+
648687
implementation_type _impl{};
649688
};
650689

include/gl/types/iterator_range.hpp

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -46,9 +46,7 @@ class iterator_range
4646
{
4747
public:
4848
using iterator = Iterator;
49-
#if __cplusplus >= 202302L
5049
using const_iterator = std::const_iterator<Iterator>;
51-
#endif
5250
using distance_type = std::ptrdiff_t;
5351
using value_type = std::remove_reference_t<typename iterator::value_type>;
5452

include/gl/types/properties.hpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ namespace types {
2525
// --- common properties ---
2626

2727
using empty_properties = std::monostate;
28+
using empty_properties_map = std::monostate;
2829

2930
class name_property
3031
#ifndef _GL_PROPERTY_TYPES_NOT_FINAL

include/gl/vertex_descriptor.hpp

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,10 @@ class vertex_descriptor final {
2020
public:
2121
using type = std::type_identity_t<vertex_descriptor<Properties>>;
2222
using properties_type = Properties;
23+
using properties_ref_type = std::conditional_t<
24+
type_traits::is_default_properties_type_v<properties_type>,
25+
types::empty_properties,
26+
properties_type&>;
2327

2428
template <type_traits::c_instantiation_of<graph_traits> GraphTraits>
2529
friend class graph;
@@ -28,11 +32,15 @@ class vertex_descriptor final {
2832
vertex_descriptor(const vertex_descriptor&) = delete;
2933
vertex_descriptor& operator=(const vertex_descriptor&) = delete;
3034

31-
explicit vertex_descriptor(const types::id_type id) : _id(id) {}
35+
// TODO: private
36+
explicit vertex_descriptor(const types::id_type id)
37+
requires(type_traits::is_default_properties_type_v<properties_type>)
38+
: _id(id), _properties() {}
3239

33-
explicit vertex_descriptor(const types::id_type id, const properties_type& properties)
40+
// TODO: private
41+
explicit vertex_descriptor(const types::id_type id, properties_type& properties)
3442
requires(not type_traits::is_default_properties_type_v<properties_type>)
35-
: _id(id), properties(properties) {}
43+
: _id(id), _properties(properties) {}
3644

3745
vertex_descriptor(vertex_descriptor&&) = default;
3846
vertex_descriptor& operator=(vertex_descriptor&&) = default;
@@ -51,7 +59,9 @@ class vertex_descriptor final {
5159
return this->_id;
5260
}
5361

54-
[[no_unique_address]] mutable properties_type properties{};
62+
[[no_unique_address]] gl_attr_force_inline properties_ref_type properties() mutable {
63+
return this->_properties;
64+
}
5565

5666
friend inline std::ostream& operator<<(std::ostream& os, const vertex_descriptor& vertex) {
5767
vertex._write(os);
@@ -89,6 +99,7 @@ class vertex_descriptor final {
8999
}
90100

91101
types::id_type _id;
102+
[[no_unique_address]] properties_ref_type _properties;
92103
};
93104

94105
template <type_traits::c_properties Properties = types::empty_properties>

task.md

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
# CPP-GL Graph Element Representation and Storage Refactor
2+
3+
## Task
4+
5+
- [YouTrack Task](https://spectral519.youtrack.cloud/issue/CPPGL-56/Replace-the-usage-of-pointers-for-vertex-and-edge-storage-to-simple-composite-types)
6+
- Description:
7+
```
8+
- The `graph` class should conditionally contain vertex and edge property maps. Each entry in these maps should be bound to the element with the given id.
9+
- The `vertex_descriptor` and `edge_descriptor` classes should contain a reference to the proper property map entry instead of containing the properties directly and define a mutable `properties()` getter method
10+
- The `graph` class should initialize new vertex/edge elements without the use of pointers and delegate the proper element handling/conversion to the implementation types
11+
- The vertex/edge getter methods of the `graph` class should return the elements by value
12+
- The `adjacency_list` model should store the edges without the use of pointers
13+
- Align the algorithms to match the new structure of the graph elements and their storage
14+
```
15+
16+
## Ideas
17+
18+
1. Lightweight descriptor object
19+
- Instead of storing the vertex set, store only the number of vertices and a properties map
20+
- Create the descriptor object only when necessary
21+
- After removing a vertex align the ids of descriptors stored by graph edges:
22+
- Requires a warning/caution doc stating that removing a vertex invalidates the ids of returned descriptors

0 commit comments

Comments
 (0)