SST 16.0.0
Structural Simulation Toolkit
serialize_variant.h
1// Copyright 2009-2026 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-2026, 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 <array>
23#include <utility>
24#include <variant>
25
26namespace SST::Core::Serialization {
27
28// Serialize std::variant
29template <typename... Types>
30class serialize_impl<std::variant<Types...>>
31{
32 // ObjectMap class representing index of a std::variant
33 // If this ObjectMap is changed, the std::variant's active member is changed
34 // This is only used in mapping mode
35 class ObjectMapVariantIndex : public ObjectMapFundamental<size_t>
36 {
37 std::variant<Types...>& obj;
38 size_t index;
39 ObjectMap* const parent;
40
41 public:
42 ObjectMapVariantIndex(std::variant<Types...>& obj, ObjectMap* parent) :
44 obj(obj),
45 index(obj.index()),
46 parent(parent)
47 {}
48
49 void set_impl(const std::string& value) override
50 {
51 size_t old = index;
53 if ( index == old ) return;
54
55 // An index of std::variant_npos (or -1 converted to size_t, usually the same) indicates
56 // valueless_by_exception
57 if ( index == std::variant_npos || index == -size_t { 1 } ) {
58 parent->removeVariable("value");
59 return;
60 }
61
62 // If the new index is invalid, exit early with an error message
63 if ( index >= sizeof...(Types) ) {
64 printf("Index out of range\n");
65 return;
66 }
67
68 // Remove the value member
69 parent->removeVariable("value");
70
71 // Change the variant's active index
72 set_index<std::index_sequence_for<Types...>>::array.at(index)(obj);
73
74 // Add a value member of the variant's new alternative
75 std::visit([&](auto& x) { parent->addVariable("value", ObjectMapSerialization(x)); }, obj);
76 }
77 };
78
79 void operator()(std::variant<Types...>& obj, serializer& ser, ser_opt_t UNUSED(options))
80 {
81 size_t index = obj.index();
82
83 switch ( ser.mode() ) {
84 case serializer::MAP:
85 ser.mapper().map_hierarchy_start(ser.getMapName(), new ObjectMapContainer<std::variant<Types...>>(&obj));
86 ser.mapper().map_object("index", new ObjectMapVariantIndex(obj, ser.mapper().get_top()));
87 if ( index != std::variant_npos )
88 std::visit([&](auto& x) { ser.mapper().map_object("value", ObjectMapSerialization(x)); }, obj);
89 ser.mapper().map_hierarchy_end();
90 return;
91
92 case serializer::SIZER:
93 ser.size(index);
94 break;
95
96 case serializer::PACK:
97 ser.pack(index);
98 break;
99
100 case serializer::UNPACK:
101 ser.unpack(index);
102
103 // Set the active variant to index. The variant must be default-constructible.
104 // We cannot portably restore valueless_by_exception but we do nothing in that case.
105 if ( index != std::variant_npos ) set_index<std::index_sequence_for<Types...>>::array.at(index)(obj);
106 break;
107 }
108
109 // std::visit instantiates the generic lambda [&](auto& x) { SST_SER(x); } for each variant
110 // Types... in obj, and calls the one corresponding to obj's currently held variant
111
112 // Serialize the currently active variant if it's not valueless_by_exception
113 if ( index != std::variant_npos ) std::visit([&](auto& x) { SST_SER(x); }, obj);
114 }
115
116 // Table of functions to set the active variant
117 //
118 // This defines a partial specialization of the set_index<std::variant<Types...>> struct template which takes a
119 // single "type" template argument. This specialization matches std::index_sequence<INDEX...> representing a
120 // sequence of subscripts 0, 1, 2, ..., sizeof...(Types)-1 generated by std::index_sequence_for<Types...>
121 //
122 // This set_index partial specialization has a constexpr array of function pointers, each of which takes a
123 // std::variant<Types...>& obj argument and calls obj.emplace<INDEX>() which changes the active variant of obj to
124 // the Types... entry corresponding to INDEX. The emplace<INDEX>() method is called with no constructor arguments,
125 // which means that the new active variant will be default-constructed. This changing of the active variant happens
126 // before the new variant is deserialized in-place.
127 //
128 // Although std::visit can invoke a function with the currently active std::variant, it relies on knowing the
129 // currently active variant as returned by index(). To CHANGE the currently active variant requires an assignment
130 // or swap of the std::variant object or a call to the emplace() method.
131 //
132 // Changing the currently active variant to INDEX by calling emplace<INDEX>() requires that INDEX be a compile-time
133 // constant, and the effects of constructing the newly active variant are different for each of the Types... indexed
134 // by INDEX, and so a separate function table entry is needed for each INDEX so that the correct one can be
135 // dispatched at runtime.
136 //
137 // { obj.emplace<0>(), obj.emplace<1>(), obj.emplace<2>(), ..., obj.emplace<sizeof...(Types)-1>() }
138
139 template <typename>
140 struct set_index;
141
142 template <size_t INDEX>
143 static void set_index_func(std::variant<Types...>& obj)
144 {
145 obj.template emplace<INDEX>();
146 }
147
148 template <size_t... INDEX>
149 struct set_index<std::index_sequence<INDEX...>>
150 {
151 static constexpr std::array<void (*)(std::variant<Types...>& obj), sizeof...(INDEX)> array = {
152 set_index_func<INDEX>...
153 };
154 };
155
156 SST_FRIEND_SERIALIZE();
157};
158
159} // namespace SST::Core::Serialization
160
161#endif // SST_CORE_SERIALIZATION_IMPL_SERIALIZE_VARIANT_H
Class used to map containers.
Definition objectMap.h:1420
ObjectMap representing fundamental types, and classes treated as fundamental types.
Definition objectMap.h:1230
virtual void set_impl(const std::string &value) override
Set the value of the object represented as a string.
Definition objectMap.h:1244
Base class for objects created by the serializer mapping mode used to map the variables for objects.
Definition objectMap.h:188
virtual void removeVariable(const std::string &UNUSED(name))
Removes a variable from this ObjectMap.
Definition objectMap.h:412
virtual void addVariable(const std::string &UNUSED(name), ObjectMap *UNUSED(obj))
Adds a variable to this ObjectMap.
Definition objectMap.h:403
Base serialize class.
Definition serialize.h:132
This class is basically a wrapper for objects to declare the order in which their members should be s...
Definition serializer.h:43