Skip to content

Commit 837e51d

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

4 files changed

Lines changed: 79 additions & 30 deletions

File tree

include/gl/graph.hpp

Lines changed: 44 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) {
54+
this->_vertices.reserve(n_vertices);
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) {
4962
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));
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,10 +85,11 @@ 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));
74-
}
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+
// }
7593

7694
[[nodiscard]] gl_attr_force_inline std::ranges::iota_view<types::id_type, types::id_type>
7795
vertex_ids() const {
@@ -85,36 +103,33 @@ class graph final {
85103
const types::id_type vertex_id
86104
) const {
87105
this->_verify_vertex_id(vertex_id);
88-
return *this->_vertices[vertex_id];
106+
if constexpr (type_traits::is_default_properties_type_v<vertex_properties_type>)
107+
return vertex_descriptor{vertex_id};
108+
else
109+
return vertex_descriptor{vertex_id, *this->_vertex_properties[vertex_id]};
89110
}
90111

91112
// clang-format on
92113

93114
[[nodiscard]] gl_attr_force_inline bool has_vertex(const types::id_type vertex_id) const {
94-
return vertex_id < this->n_vertices();
115+
return vertex_id < this->_n_vertices;
95116
}
96117

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() {
118+
vertex_type add_vertex() {
102119
this->_impl.add_vertex();
103-
this->_vertices.push_back(detail::make_vertex<vertex_type>(this->n_vertices()));
104-
return *this->_vertices.back();
120+
return this->_vertices.emplace_back(this->n_vertices(), this->_vertex_properties.emplace_back());
105121
}
106122

107123
const vertex_type& add_vertex(const vertex_properties_type& properties)
108124
requires(not type_traits::is_default_properties_type_v<vertex_properties_type>)
109125
{
110126
this->_impl.add_vertex();
111-
this->_vertices.push_back(detail::make_vertex<vertex_type>(this->n_vertices(), properties));
112-
return *this->_vertices.back();
127+
return vertex_descriptor{this->_n_vertices++, *this->_vertex_properties.emplace_back(std::make_unique<vertex_properties_type>(properties))};
113128
}
114129

115130
void add_vertices(const types::size_type n) {
116131
this->_impl.add_vertices(n);
117-
this->_vertices.reserve(this->n_vertices() + n);
132+
this->_vertices.reserve(this->_n_vertices + n);
118133

119134
for (types::size_type _ = constants::begin_idx; _ < n; ++_)
120135
this->_vertices.push_back(detail::make_vertex<vertex_type>(this->n_vertices()));
@@ -644,7 +659,10 @@ class graph final {
644659
}
645660
}
646661

647-
vetex_list_type _vertices{};
662+
types::size_type _n_vertices = 0uz;
663+
[[no_unique_address]] vertex_properties_map_type _vertex_properties;
664+
// TODO: edge properties map
665+
648666
implementation_type _impl{};
649667
};
650668

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: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ 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<type_traits::is_default_properties_type_v<properties_type>, types::empty_properties, properties_type&>;
2324

2425
template <type_traits::c_instantiation_of<graph_traits> GraphTraits>
2526
friend class graph;
@@ -28,11 +29,15 @@ class vertex_descriptor final {
2829
vertex_descriptor(const vertex_descriptor&) = delete;
2930
vertex_descriptor& operator=(const vertex_descriptor&) = delete;
3031

31-
explicit vertex_descriptor(const types::id_type id) : _id(id) {}
32+
// TODO: private
33+
explicit vertex_descriptor(const types::id_type id)
34+
requires(type_traits::is_default_properties_type_v<properties_type>)
35+
: _id(id), _properties() {}
3236

33-
explicit vertex_descriptor(const types::id_type id, const properties_type& properties)
37+
// TODO: private
38+
explicit vertex_descriptor(const types::id_type id, properties_type& properties)
3439
requires(not type_traits::is_default_properties_type_v<properties_type>)
35-
: _id(id), properties(properties) {}
40+
: _id(id), _properties(properties) {}
3641

3742
vertex_descriptor(vertex_descriptor&&) = default;
3843
vertex_descriptor& operator=(vertex_descriptor&&) = default;
@@ -51,7 +56,9 @@ class vertex_descriptor final {
5156
return this->_id;
5257
}
5358

54-
[[no_unique_address]] mutable properties_type properties{};
59+
[[no_unique_address]] gl_attr_force_inline properties_ref_type properties() mutable {
60+
return this->_properties;
61+
}
5562

5663
friend inline std::ostream& operator<<(std::ostream& os, const vertex_descriptor& vertex) {
5764
vertex._write(os);
@@ -89,6 +96,7 @@ class vertex_descriptor final {
8996
}
9097

9198
types::id_type _id;
99+
[[no_unique_address]] properties_ref_type _properties;
92100
};
93101

94102
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)