Skip to content

Commit beaeece

Browse files
committed
serialize std::variant
1 parent 542a50e commit beaeece

File tree

5 files changed

+156
-0
lines changed

5 files changed

+156
-0
lines changed

src/sst/core/Makefile.am

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,7 @@ nobase_dist_sst_HEADERS = \
105105
serialization/impl/serialize_insertable.h \
106106
serialization/impl/serialize_tuple.h \
107107
serialization/impl/serialize_utility.h \
108+
serialization/impl/serialize_variant.h \
108109
serialization/impl/mapper.h \
109110
serialization/impl/packer.h \
110111
serialization/impl/sizer.h \
Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
// Copyright 2009-2025 NTESS. Under the terms
2+
// of Contract DE-NA0003525 with NTESS, the U.S.
3+
// Government retains certain rights in this software.
4+
//
5+
// Copyright (c) 2009-2025, NTESS
6+
// All rights reserved.
7+
//
8+
// This file is part of the SST software package. For license
9+
// information, see the LICENSE file in the top level directory of the
10+
// distribution.
11+
12+
#ifndef SST_CORE_SERIALIZATION_IMPL_SERIALIZE_VARIANT_H
13+
#define SST_CORE_SERIALIZATION_IMPL_SERIALIZE_VARIANT_H
14+
15+
#ifndef SST_INCLUDING_SERIALIZE_H
16+
#warning \
17+
"The header file sst/core/serialization/impl/serialize_variant.h should not be directly included as it is not part of the stable public API. The file is included in sst/core/serialization/serialize.h"
18+
#endif
19+
20+
#include "sst/core/serialization/serializer.h"
21+
22+
#include <utility>
23+
#include <variant>
24+
25+
namespace SST::Core::Serialization {
26+
27+
// Serialize std::variant
28+
template <typename... Types>
29+
class serialize_impl<std::variant<Types...>>
30+
{
31+
void operator()(std::variant<Types...>& obj, serializer& ser, ser_opt_t UNUSED(options))
32+
{
33+
size_t index = std::variant_npos;
34+
switch ( ser.mode() ) {
35+
case serializer::SIZER:
36+
index = obj.index();
37+
ser.size(index);
38+
break;
39+
40+
case serializer::PACK:
41+
index = obj.index();
42+
ser.pack(index);
43+
break;
44+
45+
case serializer::UNPACK:
46+
ser.unpack(index);
47+
48+
// Set the active variant to index. The variant must be default-constructible.
49+
// We cannot portably restore valueless_by_exception but we do nothing in that case.
50+
if ( index != std::variant_npos ) set_index<std::index_sequence_for<Types...>>[index](obj);
51+
break;
52+
53+
case serializer::MAP:
54+
{
55+
// TODO -- how to handle mapping of std::variant ?
56+
return;
57+
}
58+
}
59+
60+
// Serialize the active variant if it's not valueless_by_exception
61+
if ( index != std::variant_npos ) std::visit([&](auto& x) { SST_SER(x); }, obj);
62+
}
63+
64+
// Table of functions to set the active variant
65+
// Primary definition is std::nullptr_t to cause error if no specialization matches
66+
template <typename>
67+
static constexpr std::nullptr_t set_index {};
68+
69+
SST_FRIEND_SERIALIZE();
70+
};
71+
72+
// Table of functions to set the active variant
73+
// This is defined outside of serialize_impl to work around GCC Bug 71954
74+
template <typename... Types>
75+
template <size_t... INDEX>
76+
constexpr void (*serialize_impl<std::variant<Types...>>::set_index<std::index_sequence<INDEX...>>[])(
77+
std::variant<Types...>&) { [](std::variant<Types...>& obj) { obj.template emplace<INDEX>(); }... };
78+
79+
} // namespace SST::Core::Serialization
80+
81+
#endif // SST_CORE_SERIALIZATION_IMPL_SERIALIZE_VARIANT_H

src/sst/core/serialization/serialize.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -515,6 +515,7 @@ sst_ser_or_helper(Args... args)
515515
#include "sst/core/serialization/impl/serialize_insertable.h"
516516
#include "sst/core/serialization/impl/serialize_string.h"
517517
#include "sst/core/serialization/impl/serialize_tuple.h"
518+
#include "sst/core/serialization/impl/serialize_variant.h"
518519

519520
// Reenble warnings for including the above file independent of this
520521
// file.

src/sst/core/testElements/coreTest_Serialization.cc

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,16 +23,19 @@
2323
#include "sst/core/serialization/impl/serialize_utility.h"
2424
#include "sst/core/warnmacros.h"
2525

26+
#include <array>
2627
#include <deque>
2728
#include <forward_list>
2829
#include <list>
2930
#include <map>
3031
#include <set>
3132
#include <string>
3233
#include <tuple>
34+
#include <type_traits>
3335
#include <unordered_map>
3436
#include <unordered_set>
3537
#include <utility>
38+
#include <variant>
3639
#include <vector>
3740

3841
namespace SST::CoreTestSerialization {
@@ -223,6 +226,39 @@ checkContainerSerializeDeserialize(T*& data)
223226
return true;
224227
};
225228

229+
// std::variant
230+
auto checkVariant = [](auto& data, auto& result) {
231+
using T = std::decay_t<decltype(data)>;
232+
using R = std::decay_t<decltype(result)>;
233+
234+
if constexpr ( !std::is_same_v<T, R> ) {
235+
// ignore the cases where T != R at compile-time, since they are excluded by index() runtime equality test
236+
return false;
237+
}
238+
else if constexpr ( std::is_same_v<T, std::string> || std::is_arithmetic_v<T> || std::is_enum_v<T> ) {
239+
return data == result;
240+
}
241+
else if constexpr ( std::is_same_v<T, std::vector<int>> ) {
242+
if ( data.size() != result.size() ) return false;
243+
for ( size_t i = 0; i < data.size(); ++i )
244+
if ( data[i] != result[i] ) return false;
245+
return true;
246+
}
247+
else {
248+
static_assert(sizeof(T) == 0, "Unsupported type in checkVariant()");
249+
}
250+
};
251+
252+
template <typename... Types>
253+
bool
254+
checkVariantSerializeDeserialize(std::variant<Types...>& data)
255+
{
256+
std::variant<Types...> result;
257+
serializeDeserialize(data, result);
258+
if ( result.index() != data.index() ) return false;
259+
return std::visit(checkVariant, data, result);
260+
};
261+
226262
// Arrays
227263

228264
template <typename>
@@ -846,6 +882,40 @@ coreTestSerialization::coreTestSerialization(ComponentId_t id, Params& params) :
846882
if ( !passed ) out.output("ERROR: unordered_multiset<int32_t>* did not serialize/deserialize properly\n");
847883
delete umultiset_in;
848884
}
885+
else if ( test == "variant" ) {
886+
std::variant<std::vector<int>, double, std::string> var;
887+
for ( int ntry = 0; ntry < 5; ++ntry ) {
888+
bool passed = false;
889+
890+
// Generate random variant each try
891+
switch ( rng->generateNextUInt32() % std::variant_size_v<decltype(var)> ) {
892+
case 0:
893+
{
894+
var = std::vector<int>(rng->generateNextUInt32() % 1000);
895+
for ( auto& e : std::get<0>(var) )
896+
e = rng->generateNextInt32();
897+
passed = checkVariantSerializeDeserialize(var);
898+
break;
899+
}
900+
case 1:
901+
{
902+
var = double(rng->generateNextInt32());
903+
passed = checkVariantSerializeDeserialize(var);
904+
break;
905+
}
906+
case 2:
907+
{
908+
std::string str;
909+
size_t len = rng->generateNextUInt32() % 100;
910+
for ( size_t i = 0; i < len; ++i )
911+
str += "0123456789"[rng->generateNextUInt32() % 10];
912+
var = str;
913+
passed = checkVariantSerializeDeserialize(var);
914+
}
915+
}
916+
if ( !passed ) out.output("ERROR: std::variant<...> did not serialize/deserialize properly\n");
917+
}
918+
}
849919
else if ( test == "map_to_vector" ) {
850920

851921
// Containers to other containers

tests/testsuite_default_Serialization.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,9 @@ def test_Serialization_atomic(self):
6363
def test_Serialization_complexcontainer(self):
6464
self.serialization_test_template("complexcontainer")
6565

66+
def test_Serialization_variant(self):
67+
self.serialization_test_template("variant")
68+
6669
#####
6770
def serialization_test_template(self, testtype, default_reffile = True):
6871

0 commit comments

Comments
 (0)