Skip to content

Commit 860d977

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 860d977

5 files changed

Lines changed: 78 additions & 22 deletions

File tree

include/boost/serialization/access.hpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -129,6 +129,10 @@ class access {
129129
// class doesn't have a class-specific placement new defined.
130130
::new(t)T;
131131
}
132+
template<class T>
133+
static T *construct_r(void * t) {
134+
return ::new(t)T;
135+
}
132136
template<class T, class U>
133137
static T & cast_reference(U & u){
134138
return static_cast<T &>(u);

include/boost/serialization/std_variant.hpp

Lines changed: 14 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -122,11 +122,19 @@ 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_;
128+
void * mem() { return static_cast<void *>(data); }
129+
type * ptr() { return ptr_; }
130+
ValueData() : ptr_(access::construct_r<type>(mem())) {}
131+
~ValueData() { ptr()->~type(); }
132+
} value;
133+
134+
ar >> BOOST_SERIALIZATION_NVP(*value.ptr());
135+
v = std::move(*value.ptr());
128136
type * new_address = & std::get<type>(v);
129-
ar.reset_object_address(new_address, & value);
137+
ar.reset_object_address(new_address, value.ptr());
130138
return;
131139
}
132140
//typedef typename mpl::pop_front<S>::type type;
@@ -149,7 +157,7 @@ struct variant_impl<0, Seq>
149157

150158
template<class Archive, class... Types>
151159
void load(
152-
Archive & ar,
160+
Archive & ar,
153161
std::variant<Types...>& v,
154162
const unsigned int version
155163
){
@@ -188,7 +196,7 @@ void serialize(Archive &, std::monostate &, const unsigned int /*version*/)
188196

189197
namespace boost {
190198
namespace serialization {
191-
199+
192200
template<class... Types>
193201
struct tracking_level<
194202
std::variant<Types...>

include/boost/serialization/variant.hpp

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -159,11 +159,19 @@ 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_;
165+
void * mem() { return static_cast<void *>(data); }
166+
head_type * ptr() { return ptr_; }
167+
ValueData() : ptr_(access::construct_r<head_type>(mem())) {}
168+
~ValueData() { ptr()->~head_type(); }
169+
} value;
170+
171+
ar >> BOOST_SERIALIZATION_NVP(*value.ptr());
172+
v = std::move(*value.ptr());
165173
head_type * new_address = & get<head_type>(v);
166-
ar.reset_object_address(new_address, & value);
174+
ar.reset_object_address(new_address, value.ptr());
167175
return;
168176
}
169177
typedef typename mpl::pop_front<S>::type type;

include/boost/serialization/variant2.hpp

Lines changed: 14 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -92,11 +92,19 @@ 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_;
98+
void * mem() { return static_cast<void *>(data); }
99+
type * ptr() { return ptr_; }
100+
ValueData() : ptr_(access::construct_r<type>(mem())) {}
101+
~ValueData() { ptr()->~type(); }
102+
} value;
103+
104+
ar >> BOOST_SERIALIZATION_NVP(*value.ptr());
105+
v = std::move(*value.ptr());
98106
type * new_address = & variant2::get<type>(v);
99-
ar.reset_object_address(new_address, & value);
107+
ar.reset_object_address(new_address, value.ptr());
100108
return;
101109
}
102110
//typedef typename mpl::pop_front<S>::type type;
@@ -119,7 +127,7 @@ struct variant_impl<Seq>
119127

120128
template<class Archive, class... Types>
121129
void load(
122-
Archive & ar,
130+
Archive & ar,
123131
variant2::variant<Types...> & v,
124132
const unsigned int version
125133
){
@@ -158,7 +166,7 @@ void serialize(Archive &ar, variant2::monostate &, const unsigned int /*version*
158166

159167
namespace boost {
160168
namespace serialization {
161-
169+
162170
template<class... Types>
163171
struct tracking_level<
164172
variant2::variant<Types...>

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)