Skip to content

Commit 90dfcfc

Browse files
committed
wip: lightweight vertex descriptor (with vertex adding)
1 parent e220490 commit 90dfcfc

5 files changed

Lines changed: 117 additions & 42 deletions

File tree

include/gl/graph.hpp

Lines changed: 79 additions & 36 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,31 @@ 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)
98+
| std::views::transform([](auto [id, properties]) {
99+
return vertex_descriptor{id, *properties};
100+
});
101+
}
102+
103+
[[nodiscard]] gl_attr_force_inline auto vertices() const
104+
requires(not type_traits::is_default_properties_type_v<vertex_properties_type>)
105+
{
106+
return this->vertex_ids()
107+
| std::views::transform([](const types::id_type id) { return vertex_descriptor{id}; });
74108
}
75109

76110
[[nodiscard]] gl_attr_force_inline std::ranges::iota_view<types::id_type, types::id_type>
77111
vertex_ids() const {
78-
return std::views::iota(constants::initial_id, this->n_vertices());
112+
return std::views::iota(constants::initial_id, this->_n_vertices);
79113
}
80114

81115
// clang-format off
@@ -85,52 +119,58 @@ class graph final {
85119
const types::id_type vertex_id
86120
) const {
87121
this->_verify_vertex_id(vertex_id);
88-
return *this->_vertices[vertex_id];
122+
if constexpr (type_traits::is_default_properties_type_v<vertex_properties_type>)
123+
return vertex_descriptor{vertex_id};
124+
else
125+
return vertex_descriptor{vertex_id, *this->_vertex_properties[vertex_id]};
89126
}
90127

91128
// clang-format on
92129

93130
[[nodiscard]] gl_attr_force_inline bool has_vertex(const types::id_type vertex_id) const {
94-
return vertex_id < this->n_vertices();
131+
return vertex_id < this->_n_vertices;
95132
}
96133

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() {
134+
vertex_type add_vertex() {
102135
this->_impl.add_vertex();
103-
this->_vertices.push_back(detail::make_vertex<vertex_type>(this->n_vertices()));
104-
return *this->_vertices.back();
136+
const auto new_vertex_id = this->_n_vertices++;
137+
138+
if constexpr (type_traits::is_default_properties_type_v<vertex_properties_type>)
139+
return vertex_descriptor{new_vertex_id, *this->_vertex_properties.emplace_back()};
140+
else
141+
return vertex_descriptor{new_vertex_id};
105142
}
106143

107-
const vertex_type& add_vertex(const vertex_properties_type& properties)
144+
vertex_type add_vertex(vertex_properties_type properties)
108145
requires(not type_traits::is_default_properties_type_v<vertex_properties_type>)
109146
{
110147
this->_impl.add_vertex();
111-
this->_vertices.push_back(detail::make_vertex<vertex_type>(this->n_vertices(), properties));
112-
return *this->_vertices.back();
148+
return vertex_descriptor{
149+
this->_n_vertices++,
150+
*this->_vertex_properties.emplace_back(
151+
std::make_unique<vertex_properties_type>(std::move(properties))
152+
)
153+
};
113154
}
114155

115156
void add_vertices(const types::size_type n) {
116157
this->_impl.add_vertices(n);
117-
this->_vertices.reserve(this->n_vertices() + n);
118-
119-
for (types::size_type _ = constants::begin_idx; _ < n; ++_)
120-
this->_vertices.push_back(detail::make_vertex<vertex_type>(this->n_vertices()));
158+
this->_n_vertices += n;
159+
if constexpr (type_traits::is_default_properties_type_v<vertex_properties_type>)
160+
this->_vertex_properties.resize(this->_n_vertices);
121161
}
122162

123163
template <type_traits::c_sized_range_of<vertex_properties_type> VertexPropertiesRange>
124-
void add_vertices_with(const VertexPropertiesRange& properties_range) {
164+
void add_vertices_with(const VertexPropertiesRange& properties_range)
165+
requires(not type_traits::is_default_properties_type_v<vertex_properties_type>)
166+
{
125167
const auto n = std::ranges::size(properties_range);
126168

127169
this->_impl.add_vertices(n);
128-
this->_vertices.reserve(this->n_vertices() + n);
170+
this->_n_vertices += n;
129171

130-
for (const auto& properties : properties_range)
131-
this->_vertices.push_back(
132-
detail::make_vertex<vertex_type>(this->n_vertices(), properties)
133-
);
172+
if constexpr (type_traits::is_default_properties_type_v<vertex_properties_type>)
173+
this->_vertex_properties.append_range(properties_range);
134174
}
135175

136176
gl_attr_force_inline void remove_vertex(const types::size_type vertex_id) {
@@ -644,7 +684,10 @@ class graph final {
644684
}
645685
}
646686

647-
vetex_list_type _vertices{};
687+
types::size_type _n_vertices = 0uz;
688+
[[no_unique_address]] vertex_properties_map_type _vertex_properties;
689+
// TODO: edge properties map
690+
648691
implementation_type _impl{};
649692
};
650693

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)