Skip to content

Commit 08a83d5

Browse files
Allow variant deserialization to make types with private constructors
If a type has a private no-args constructor, but has friended boost::serialization::access, it can usually be deserialised as normal. However, it _can't_ be deserialized out of a variant, because the variant tries to invoke the default constructor. Fix this by constructing it through boost::serialization::access, which is designed to model "classes that can't be created with no arguments except through deserialization"
1 parent 8a8c628 commit 08a83d5

4 files changed

Lines changed: 64 additions & 18 deletions

File tree

include/boost/serialization/std_variant.hpp

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -122,11 +122,17 @@ struct variant_impl
122122
// with an implementation that de-serialized to the address of the
123123
// aligned storage included in the variant.
124124
using type = mp::front<Seq>;
125-
type value;
126-
ar >> BOOST_SERIALIZATION_NVP(value);
127-
v = std::move(value);
125+
struct ValueData {
126+
alignas(type) unsigned char data[sizeof(type)];
127+
type * ptr() { return reinterpret_cast<type *>(data); }
128+
ValueData() { access::construct(ptr()); }
129+
~ValueData() { ptr()->~type(); }
130+
} value;
131+
132+
ar >> BOOST_SERIALIZATION_NVP(*value.ptr());
133+
v = std::move(*value.ptr());
128134
type * new_address = & std::get<type>(v);
129-
ar.reset_object_address(new_address, & value);
135+
ar.reset_object_address(new_address, value.ptr());
130136
return;
131137
}
132138
//typedef typename mpl::pop_front<S>::type type;

include/boost/serialization/variant.hpp

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -159,11 +159,17 @@ struct variant_impl {
159159
// with an implementation that de-serialized to the address of the
160160
// aligned storage included in the variant.
161161
typedef typename mpl::front<S>::type head_type;
162-
head_type value;
163-
ar >> BOOST_SERIALIZATION_NVP(value);
164-
v = std::move(value);;
162+
struct ValueData {
163+
alignas(head_type) unsigned char data[sizeof(head_type)];
164+
head_type * ptr() { return reinterpret_cast<head_type *>(data); }
165+
ValueData() { access::construct(ptr()); }
166+
~ValueData() { ptr()->~head_type(); }
167+
} value;
168+
169+
ar >> BOOST_SERIALIZATION_NVP(*value.ptr());
170+
v = std::move(*value.ptr());
165171
head_type * new_address = & get<head_type>(v);
166-
ar.reset_object_address(new_address, & value);
172+
ar.reset_object_address(new_address, value.ptr());
167173
return;
168174
}
169175
typedef typename mpl::pop_front<S>::type type;

include/boost/serialization/variant2.hpp

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -92,11 +92,17 @@ struct variant_impl
9292
// with an implementation that de-serialized to the address of the
9393
// aligned storage included in the variant.
9494
using type = mp11::mp_front<Seq>;
95-
type value;
96-
ar >> BOOST_SERIALIZATION_NVP(value);
97-
v = std::move(value);
95+
struct ValueData {
96+
alignas(type) unsigned char data[sizeof(type)];
97+
type * ptr() { return reinterpret_cast<type *>(data); }
98+
ValueData() { access::construct(ptr()); }
99+
~ValueData() { ptr()->~type(); }
100+
} value;
101+
102+
ar >> BOOST_SERIALIZATION_NVP(*value.ptr());
103+
v = std::move(*value.ptr());
98104
type * new_address = & variant2::get<type>(v);
99-
ar.reset_object_address(new_address, & value);
105+
ar.reset_object_address(new_address, value.ptr());
100106
return;
101107
}
102108
//typedef typename mpl::pop_front<S>::type type;

test/test_variant.cpp

Lines changed: 34 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,32 @@ namespace boost {
5757
#include "A.hpp"
5858
#include "A.ipp"
5959

60+
class V {
61+
private:
62+
friend class boost::serialization::access;
63+
int m_i;
64+
V() :
65+
m_i(0)
66+
{}
67+
template<class Archive>
68+
void serialize(Archive& ar, unsigned /*version*/)
69+
{
70+
ar & BOOST_SERIALIZATION_NVP(m_i);
71+
}
72+
public:
73+
V(int i) :
74+
m_i(i)
75+
{}
76+
int get_i() const
77+
{
78+
return m_i;
79+
}
80+
bool operator==(const V& other) const
81+
{
82+
return m_i == other.m_i;
83+
}
84+
};
85+
6086
class are_equal
6187
: public boost::static_visitor<bool>
6288
{
@@ -156,6 +182,8 @@ void test(Variant & v)
156182
test_type(v);
157183
v = std::string("we can't stop here, this is Bat Country");
158184
test_type(v);
185+
v = V(67);
186+
test_type(v);
159187
}
160188

161189
#include <boost/serialization/variant.hpp>
@@ -166,10 +194,10 @@ void test(Variant & v)
166194

167195
int test_boost_variant(){
168196
std::cerr << "Testing boost_variant\n";
169-
boost::variant<bool, int, float, double, A, std::string> v;
197+
boost::variant<bool, int, float, double, A, std::string, V> v;
170198
test(v);
171199
const A a;
172-
boost::variant<bool, int, float, double, const A *, std::string> v1 = & a;
200+
boost::variant<bool, int, float, double, const A *, std::string, V> v1 = & a;
173201
test_type(v1);
174202
return EXIT_SUCCESS;
175203
}
@@ -180,10 +208,10 @@ int test_boost_variant(){
180208

181209
int test_boost_variant2(){
182210
std::cerr << "Testing boost_variant2\n";
183-
boost::variant2::variant<bool, int, float, double, A, std::string> v;
211+
boost::variant2::variant<bool, int, float, double, A, std::string, V> v;
184212
test(v);
185213
const A a;
186-
boost::variant2::variant<bool, int, float, double, const A *, std::string> v1 = & a;
214+
boost::variant2::variant<bool, int, float, double, const A *, std::string, V> v1 = & a;
187215
test_type(v1);
188216
return EXIT_SUCCESS;
189217
}
@@ -194,10 +222,10 @@ int test_boost_variant2(){
194222
#include <variant>
195223
int test_std_variant(){
196224
std::cerr << "Testing Std Variant\n";
197-
std::variant<bool, int, float, double, A, std::string> v;
225+
std::variant<bool, int, float, double, A, std::string, V> v;
198226
test(v);
199227
const A a;
200-
std::variant<bool, int, float, double, const A *, std::string> v1 = & a;
228+
std::variant<bool, int, float, double, const A *, std::string, V> v1 = & a;
201229
test_type(v1);
202230
return EXIT_SUCCESS;
203231
}

0 commit comments

Comments
 (0)