Skip to content

Commit 611fd19

Browse files
committed
Use in-place serialization of container elements
1 parent e3e5c6f commit 611fd19

File tree

3 files changed

+154
-179
lines changed

3 files changed

+154
-179
lines changed

src/sst/core/serialization/impl/serialize_insertable.h

Lines changed: 153 additions & 125 deletions
Original file line numberDiff line numberDiff line change
@@ -20,18 +20,88 @@
2020
#include "sst/core/serialization/impl/serialize_utility.h"
2121
#include "sst/core/serialization/serializer.h"
2222

23+
#include <cstddef>
24+
#include <deque>
2325
#include <forward_list>
26+
#include <list>
2427
#include <map>
2528
#include <set>
29+
#include <type_traits>
2630
#include <unordered_map>
2731
#include <unordered_set>
32+
#include <utility>
2833
#include <vector>
2934

3035
namespace SST::Core::Serialization {
3136

32-
// A type with begin(), end() methods with value_type elements which can call insert_element()
33-
// to insert them in begin() to end() order
34-
//
37+
// If the type is a pair with a const first, map it to pair with non-const first
38+
template <typename T>
39+
struct remove_const_key
40+
{
41+
using type = T;
42+
};
43+
44+
template <typename KEY, typename VALUE>
45+
struct remove_const_key<std::pair<const KEY, VALUE>>
46+
{
47+
using type = std::pair<KEY, VALUE>;
48+
};
49+
50+
// Whether a size() method exists for a type
51+
template <typename, typename = void>
52+
constexpr bool has_size_v = false;
53+
54+
template <typename T>
55+
constexpr bool has_size_v<T, std::void_t<decltype(std::declval<T>().size())>> = true;
56+
57+
// Whether std::distance(begin(), end()) exists for a type
58+
template <typename, typename = void>
59+
constexpr bool has_distance_v = false;
60+
61+
template <typename T>
62+
constexpr bool
63+
has_distance_v<T, std::void_t<decltype(std::distance(std::declval<T>().begin(), std::declval<T>().end()))>> = true;
64+
65+
// Get the size of a container, using a size() method if it exists, otherwise distance(begin, end)
66+
template <typename T>
67+
std::enable_if_t<has_size_v<T> || has_distance_v<T>, size_t>
68+
get_size(const T& t)
69+
{
70+
if constexpr ( has_size_v<T> )
71+
return t.size();
72+
else
73+
return std::distance(t.begin(), t.end());
74+
}
75+
76+
// Whether it is a std::vector<bool>
77+
template <typename>
78+
constexpr bool is_vector_bool_v = false;
79+
80+
template <typename... Ts>
81+
constexpr bool is_vector_bool_v<std::vector<bool, Ts...>> = true;
82+
83+
// Whether it is a simple map (not a multimap and has integral, floating-point, enum, or convertible to string keys)
84+
template <typename, typename = void>
85+
constexpr bool is_simple_map_v = false;
86+
87+
template <template <typename...> class MAP, typename KEY, typename... REST>
88+
constexpr bool is_simple_map_v<
89+
MAP<KEY, REST...>,
90+
std::enable_if_t<
91+
(is_same_template_v<MAP, std::map> || is_same_template_v<MAP, std::unordered_map>) &&
92+
(std::is_arithmetic_v<KEY> || std::is_enum_v<KEY> || std::is_convertible_v<KEY, std::string>)>> = true;
93+
94+
// Whether it is a simple set (not a multiset and has integral, floating-point, enum, or convertible to string keys)
95+
template <typename, typename = void>
96+
constexpr bool is_simple_set_v = false;
97+
98+
template <template <typename...> class SET, typename KEY, typename... REST>
99+
constexpr bool is_simple_set_v<
100+
SET<KEY, REST...>,
101+
std::enable_if_t<
102+
(is_same_template_v<SET, std::set> || is_same_template_v<SET, std::unordered_set>) &&
103+
(std::is_arithmetic_v<KEY> || std::is_enum_v<KEY> || std::is_convertible_v<KEY, std::string>)>> = true;
104+
35105
// std::deque
36106
// std::forward_list
37107
// std::list
@@ -45,171 +115,129 @@ namespace SST::Core::Serialization {
45115
// std::unordered_set
46116
// std::vector, including std::vector<bool>
47117
//
48-
// and any user-defined or future STL classes which have begin() and end() methods, a
49-
// value_type element type, and an insert_element() overload which can append value_type
50-
51-
template <class T>
118+
// clang-format off
119+
template <template <typename...> class T, typename... Ts>
52120
class serialize_impl<
53-
T, std::void_t<
54-
// exclude std::string and std::string* to prevent specialization ambiguity
55-
std::enable_if_t<!std::is_same_v<std::remove_pointer_t<T>, std::string>>,
56-
57-
// whether begin() method exists
58-
decltype(std::declval<T>().begin()),
59-
60-
// whether end() method exists
61-
decltype(std::declval<T>().end()),
62-
63-
// whether a clear() method exists
64-
decltype(std::declval<T>().clear()),
65-
66-
// whether get_size() can determine size because it has a matching overload
67-
decltype(get_size(std::declval<T>())),
68-
69-
// whether insert_element() can insert value_type elements
70-
decltype(insert_element(std::declval<T>(), std::declval<typename T::value_type>()))>>
121+
T<Ts...>, std::enable_if_t<
122+
is_same_template_v< T, std::deque > ||
123+
is_same_template_v< T, std::forward_list > ||
124+
is_same_template_v< T, std::list > ||
125+
is_same_template_v< T, std::map > ||
126+
is_same_template_v< T, std::multimap > ||
127+
is_same_template_v< T, std::multiset > ||
128+
is_same_template_v< T, std::set > ||
129+
is_same_template_v< T, std::unordered_map > ||
130+
is_same_template_v< T, std::unordered_multimap > ||
131+
is_same_template_v< T, std::unordered_multiset > ||
132+
is_same_template_v< T, std::unordered_set > ||
133+
is_same_template_v< T, std::vector >
134+
> >
135+
// clang-format on
71136
{
72-
// If the type is a pair with a const first, map it to pair with non-const first
73-
template <typename U>
74-
struct remove_const_key
75-
{
76-
using type = U;
77-
};
137+
// Object type = container template with template arguments
138+
using OBJ = T<Ts...>;
78139

79-
template <typename KEY, typename VALUE>
80-
struct remove_const_key<std::pair<const KEY, VALUE>>
81-
{
82-
using type = std::pair<KEY, VALUE>;
83-
};
84-
85-
// Value type of element, with const removed from first of pair if it exists
86-
using value_type = typename remove_const_key<typename T::value_type>::type;
87-
88-
// Note: the following use struct templates because of a GCC bug which does
89-
// not allow static constexpr variable templates defined inside of a class.
90-
91-
// Whether it is a std::vector
92-
template <typename>
93-
struct is_vector : std::false_type
94-
{};
95-
96-
template <typename... Ts>
97-
struct is_vector<std::vector<Ts...>> : std::true_type
98-
{};
99-
100-
// Whether it is a std::vector<bool>
101-
template <typename>
102-
struct is_vector_bool : std::false_type
103-
{};
104-
105-
template <typename... Ts>
106-
struct is_vector_bool<std::vector<bool, Ts...>> : std::true_type
107-
{};
108-
109-
// Whether it is a std::forward_list
110-
template <typename>
111-
struct is_forward_list : std::false_type
112-
{};
113-
114-
template <typename... Ts>
115-
struct is_forward_list<std::forward_list<Ts...>> : std::true_type
116-
{};
117-
118-
// Whether it is a simple map (not a multimap and has integral, floating-point, enum, or convertible to string keys)
119-
template <typename, typename = void>
120-
struct is_simple_map : std::false_type
121-
{};
122-
123-
template <template <typename...> class MAP, typename KEY, typename... REST>
124-
struct is_simple_map<
125-
MAP<KEY, REST...>,
126-
std::enable_if_t<(is_same_template_v<MAP, std::map> || is_same_template_v<MAP, std::unordered_map>)&&(
127-
std::is_arithmetic_v<KEY> || std::is_enum_v<KEY> || std::is_convertible_v<KEY, std::string>)>> :
128-
std::true_type
129-
{};
130-
131-
// Whether it is a simple set (not a multiset and has integral, floating-point, enum, or convertible to string keys)
132-
template <typename, typename = void>
133-
struct is_simple_set : std::false_type
134-
{};
135-
136-
template <template <typename...> class SET, typename KEY, typename... REST>
137-
struct is_simple_set<
138-
SET<KEY, REST...>,
139-
std::enable_if_t<(is_same_template_v<SET, std::set> || is_same_template_v<SET, std::unordered_set>)&&(
140-
std::is_arithmetic_v<KEY> || std::is_enum_v<KEY> || std::is_convertible_v<KEY, std::string>)>> :
141-
std::true_type
142-
{};
140+
// Value type of element with const removed from first of pair if it exists
141+
using value_type = typename remove_const_key<typename OBJ::value_type>::type;
143142

144143
public:
145-
void operator()(T& t, serializer& ser)
144+
void operator()(OBJ& obj, serializer& ser)
146145
{
147146
switch ( const auto mode = ser.mode() ) {
148147
case serializer::SIZER:
149148
case serializer::PACK:
150149
{
151-
size_t size = get_size(t);
150+
size_t size = get_size(obj);
152151

153152
if ( mode == serializer::PACK )
154153
ser.pack(size);
155154
else
156155
ser.size(size);
157156

158-
if constexpr ( is_vector_bool<T>::value ) {
157+
if constexpr ( is_vector_bool_v<OBJ> ) {
159158
// For std::vector<bool>, iterate over bool values instead of references to elements
160-
for ( bool e : t )
161-
ser& e;
159+
for ( bool e : obj )
160+
SST_SER(e);
162161
}
163162
else {
164163
// Iterate over references to elements, casting away any const in keys
165-
for ( auto& e : t )
166-
ser&(value_type&)e;
164+
for ( auto& e : obj )
165+
SST_SER((value_type&) e);
167166
}
168167
break;
169168
}
170169

171170
case serializer::UNPACK:
172171
{
172+
// Get the total size of the container
173173
size_t size;
174174
ser.unpack(size);
175175

176-
t.clear(); // Clear the container
177-
if constexpr ( is_vector<T>::value ) t.reserve(size); // Reserve size of vector
178-
179-
// For std::forward_list, last is iterator of last element inserted
180-
decltype(t.begin()) last [[maybe_unused]];
181-
if constexpr ( is_forward_list<T>::value ) last = t.before_begin();
182-
183-
for ( size_t i = 0; i < size; ++i ) {
184-
value_type e {}; // For now, elements have to be default-initializable
185-
ser& e; // Unpack the element
186-
187-
// Insert the element, moving it for efficiency
188-
if constexpr ( is_forward_list<T>::value )
189-
last = insert_element(t, std::move(e), last);
190-
else
191-
insert_element(t, std::move(e));
176+
// Erase the container
177+
obj.clear();
178+
if constexpr ( is_same_template_v<T, std::vector> ) obj.reserve(size); // Reserve size of vector
179+
180+
if constexpr ( is_same_template_v<T, std::forward_list> ) {
181+
auto last = obj.before_begin(); // iterator of last element inserted
182+
for ( size_t i = 0; i < size; ++i ) {
183+
last = obj.emplace_after(last);
184+
auto& value = *last;
185+
SST_SER(value);
186+
}
187+
}
188+
else {
189+
for ( size_t i = 0; i < size; ++i ) {
190+
if constexpr ( is_same_template_v<T, std::map> || is_same_template_v<T, std::unordered_map> ) {
191+
typename OBJ::key_type key {};
192+
SST_SER(key);
193+
auto& value = obj[std::move(key)];
194+
SST_SER(value);
195+
}
196+
else if constexpr (
197+
is_same_template_v<T, std::multimap> || is_same_template_v<T, std::unordered_multimap> ) {
198+
typename OBJ::key_type key {};
199+
SST_SER(key);
200+
auto& value = obj.emplace(std::move(key), typename OBJ::mapped_type {})->second;
201+
SST_SER(value);
202+
}
203+
else if constexpr (
204+
is_same_template_v<T, std::set> || is_same_template_v<T, std::unordered_set> ||
205+
is_same_template_v<T, std::multiset> || is_same_template_v<T, std::unordered_multiset> ) {
206+
typename OBJ::key_type key {};
207+
SST_SER(key);
208+
obj.emplace(std::move(key));
209+
}
210+
else if constexpr ( is_vector_bool_v<OBJ> ) {
211+
bool value {};
212+
SST_SER(value);
213+
obj.push_back(value);
214+
}
215+
else { // std::vector, std::deque, std::list
216+
auto& value = obj.emplace_back();
217+
SST_SER(value);
218+
}
219+
}
192220
}
193-
// assert(size == get_size(t));
221+
// assert(size == get_size(obj));
194222
break;
195223
}
196224

197225
case serializer::MAP:
198226
{
199227
using SST::Core::to_string;
200228
const std::string& name = ser.getMapName();
201-
auto* obj_map = new ObjectMapContainer<T>(&t);
229+
auto* obj_map = new ObjectMapContainer<OBJ>(&obj);
202230
ser.mapper().map_hierarchy_start(name, obj_map);
203231

204-
if constexpr ( is_vector_bool<T>::value ) {
232+
if constexpr ( is_vector_bool_v<OBJ> ) {
205233
// std::vector<bool>
206234
size_t i = 0;
207-
for ( bool e : t )
235+
for ( bool e : obj )
208236
sst_map_object(ser, e, to_string(i++));
209237
}
210-
else if constexpr ( is_simple_map<T>::value ) {
238+
else if constexpr ( is_simple_map_v<OBJ> ) {
211239
// non-multi maps with a simple key
212-
for ( auto& [key, value] : t )
240+
for ( auto& [key, value] : obj )
213241
sst_map_object(ser, value, to_string(key));
214242
}
215243
// TODO: handle is_simple_set
@@ -218,7 +246,7 @@ class serialize_impl<
218246
// std::unordered_multimap, std::multiset, std::unordered_multiset, and
219247
// std::map, std::set, std::unordered_map std::unordered_set with non-simple keys
220248
size_t i = 0;
221-
for ( auto& e : t )
249+
for ( auto& e : obj )
222250
sst_map_object(ser, (value_type&)e, to_string(i++));
223251
}
224252
ser.mapper().map_hierarchy_end();

0 commit comments

Comments
 (0)