1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385
|
#ifndef _MovableEnvelope_h_
#define _MovableEnvelope_h_
#include "../util/Logger.h"
#include <boost/spirit/include/support_argument.hpp>
#include <memory>
#include <type_traits>
namespace parse::detail {
/** \p MovableEnvelope enables the boost::spirit parser to handle a
\p T with move semantics.
boost::spirit is designed only work with value semantics. The
boost::phoenix actors only handle value semantics. Types with move
only semantics like unique_ptr will not work as expected.
boost::spirit is designed to work with compile time polymorphism. With
run-time polymorphism using raw pointers to track heap objects, each
time the parser backtracks it leaks memory.
\p MovableEnvelope makes a \p unique_ptr<T> with move only semantics
appear to have copy semantics, by moving it each time that it is copied.
This allows boost::phoenix actors to handle it correctly if the movement is
one way from creation at the parse location to consumption in a larger
parsed component.
\p MovableEnvelope is a work around. It can be removed if
boost::spirit supports move semantics, or the parser is changed to use
compile time polymorphism.
*/
template <typename T>
class MovableEnvelope {
public:
using enveloped_type = T;
/** \name Rule of Five constructors and operators.
MovableEnvelope satisfies the rule of five with the following
constructors and operator=().
*/ //@{
// Default constructor
MovableEnvelope() {}
// Copy constructor
// This leaves \p other in an emptied state
MovableEnvelope(const MovableEnvelope& other) :
MovableEnvelope(std::move(other.obj))
{}
// Move constructor
MovableEnvelope(MovableEnvelope&& other) :
MovableEnvelope(std::move(other.obj))
{}
// Move operator
MovableEnvelope& operator= (MovableEnvelope&& other) {
obj = std::move(other.obj);
original_obj = other.original_obj;
// Intentionally leave other.original_obj != other.obj.get()
return *this;
}
// Copy operator
// This leaves \p other in an emptied state
MovableEnvelope& operator= (const MovableEnvelope& other) {
obj = std::move(other.obj);
original_obj = other.original_obj;
// Intentionally leave other.original_obj != other.obj.get()
return *this;
}
//@}
virtual ~MovableEnvelope() = default;
/** \name Converting constructors and operators.
MovableEnvelope allows conversion between compatible types with the following
constructors and operators.
*/ //@{
// nullptr constructor
MovableEnvelope(std::nullptr_t) {}
template <typename U> requires (std::is_convertible_v<std::unique_ptr<U>, std::unique_ptr<T>>)
explicit MovableEnvelope(std::unique_ptr<U>&& obj_) :
obj(std::move(obj_)),
original_obj(obj.get())
{}
// This takes ownership of obj_
template <typename U> requires (std::is_convertible_v<std::unique_ptr<U>, std::unique_ptr<T>>)
explicit MovableEnvelope(U* obj_) :
obj(obj_),
original_obj(obj.get())
{}
// Converting copy constructor
// This leaves \p other in an emptied state
template <typename U> requires (std::is_convertible_v<std::unique_ptr<U>, std::unique_ptr<T>>)
MovableEnvelope(const MovableEnvelope<U>& other) :
MovableEnvelope(std::move(other.obj))
{}
// Converting move constructor
template <typename U> requires (std::is_convertible_v<std::unique_ptr<U>, std::unique_ptr<T>>)
MovableEnvelope(MovableEnvelope<U>&& other) :
MovableEnvelope(std::move(other.obj))
{}
template <typename U> requires (std::is_convertible_v<std::unique_ptr<U>, std::unique_ptr<T>>)
MovableEnvelope& operator= (MovableEnvelope<U>&& other) {
obj = std::move(other.obj);
original_obj = other.original_obj;
// Intentionally leave other.original_obj != other.obj.get()
return *this;
}
// Copy operator
// This leaves \p other in an emptied state
template <typename U> requires (std::is_convertible_v<std::unique_ptr<U>, std::unique_ptr<T>>)
MovableEnvelope& operator= (const MovableEnvelope<U>& other) {
obj = std::move(other.obj);
original_obj = other.original_obj;
// Intentionally leave other.original_obj != other.obj.get()
return *this;
}
//@}
// Return false if the original \p obj has been moved away from this
// MovableEnvelope.
bool IsEmptiedEnvelope() const
{ return (original_obj != obj.get());}
/** OpenEnvelope returns the enclosed \p obj and throws an expectation
exception if the wrapped pointer was already moved out of this \p
MovableEnvelope.
\p OpenEnvelope is a one-shot. Calling OpenEnvelope a second time
will throw, since the obj has already been removed.
\p pass should refer to qi::_pass_type to facilitate the
expectation exception.
*/
std::unique_ptr<T> OpenEnvelope(bool& pass) const {
if (IsEmptiedEnvelope()) {
ErrorLogger() <<
"The parser attempted to extract the unique_ptr from a MovableEnvelope more than once. "
"Until boost::spirit supports move semantics MovableEnvelope requires that unique_ptr be used only once. "
"Check that a parser is not back tracking over an actor containing an opened MovableEnvelope. "
"Check that set, map or vector parses are not repeatedly extracting the same unique_ptr<T>.";
pass = false;
}
return std::move(obj);
}
private:
template <typename U>
friend class MovableEnvelope;
mutable std::unique_ptr<T> obj;
mutable T* original_obj = nullptr;
};
/** \p construct_movable is a functor that constructs a MovableEnvelope<T> */
struct construct_movable {
template <typename T>
using result_type = MovableEnvelope<T>;
template <typename T>
result_type<T> operator() (T* obj) const
{ return MovableEnvelope<T>(obj); }
template <typename T>
result_type<T> operator() (std::unique_ptr<T>&& obj) const
{ return MovableEnvelope<T>(std::move(obj)); }
template <typename T>
result_type<T> operator() (std::unique_ptr<T>& obj) const
{ return MovableEnvelope<T>(std::move(obj)); }
template <typename T>
result_type<T> operator() (MovableEnvelope<T>&& obj) const
{ return MovableEnvelope<T>(std::move(obj)); }
template <typename T>
result_type<T> operator() (const MovableEnvelope<T>& obj) const
{ return MovableEnvelope<T>(obj); }
};
/** Free functions converting containers of MovableEnvelope to unique_ptrs. */
template <typename T>
std::vector<std::unique_ptr<T>> OpenEnvelopes(const std::vector<MovableEnvelope<T>>& envelopes,
bool& pass)
{
std::vector<std::unique_ptr<T>> retval;
retval.reserve(envelopes.size());
for (auto&& envelope : envelopes)
retval.emplace_back(envelope.OpenEnvelope(pass));
return retval;
}
template <typename T>
std::vector<std::pair<std::string, std::unique_ptr<T>>> OpenEnvelopes(
const std::vector<std::pair<std::string, MovableEnvelope<T>>>& in, bool& pass)
{
std::vector<std::pair<std::string, std::unique_ptr<T>>> retval;
retval.reserve(in.size());
for (auto&& name_and_value : in)
retval.emplace_back(name_and_value.first, name_and_value.second.OpenEnvelope(pass));
return retval;
}
template <typename K, typename V>
std::map<K, std::unique_ptr<V>> OpenEnvelopes(const std::map<K, MovableEnvelope<V>>& in,
bool& pass)
{
std::map<K, std::unique_ptr<V>> retval;
for (auto&& name_and_value : in)
retval.emplace(name_and_value.first, name_and_value.second.OpenEnvelope(pass));
return retval;
}
/**
Note: This simpler to use version of deconstruct_movable works with clang
version 4.0.1 and gcc version 7.2.1 with boost 1.63. It does not work
with clang 3.6.0 or gcc 4.8.4 or MSVC2015, with boost 1.58 on the
continuous integration servers. It is because of improved support for
decltype in the newer compilers.
When the project moves the required versions past these versions this
simpler, more uniform code could be adopted.
/ ** \p deconstruct_movable is a functor that extracts the unique_ptr from a
MovableEnvelope<T>. This is a one time operation that empties the
MovableEnvelope<T>. It is typically done while calling the constructor
from outside of boost::spirit that expects a unique_ptr<T> */
class deconstruct_movable_simple_version_that_works_gcc7p2p1_and_clang4p0p1 {
public:
template <typename T>
std::unique_ptr<T> operator() (const MovableEnvelope<T>& obj, bool& pass) const
{ return obj.OpenEnvelope(pass); }
template <typename T>
std::vector<std::unique_ptr<T>> operator() (const std::vector<MovableEnvelope<T>>& objs, bool& pass) const
{ return OpenEnvelopes(objs, pass); }
template <typename T>
std::vector<std::pair<std::string, std::unique_ptr<T>>> operator() (
const std::vector<std::pair<std::string, MovableEnvelope<T>>>& objs, bool& pass)
{ return OpenEnvelopes(objs, pass); }
};
class deconstruct_movable {
public:
template <typename> struct result;
template <typename F, typename MovableEnvelope_T, typename Bool>
struct result<F(MovableEnvelope_T, Bool)> {
using type = std::unique_ptr<typename std::decay_t<MovableEnvelope_T>::enveloped_type>;
};
template <typename FMETB>
using result_t = typename result<FMETB>::type;
template <typename T>
result_t<const deconstruct_movable(const MovableEnvelope<T>&, bool&)>
operator()(const MovableEnvelope<T>& obj, bool& pass) const
{ return obj.OpenEnvelope(pass); }
template <typename T>
result_t<const deconstruct_movable(const MovableEnvelope<T>&, bool&)>
operator()(const MovableEnvelope<T>& obj, const bool& pass) const
{
// Note: this ignores the constness of pass because older compilers
// pass the incorrect constness.
return obj.OpenEnvelope(pass);
}
// Unwrap ::optional<MovablelEnvelope<T>> to return
// unique_ptr(nullptr) for none
template <typename T>
result_t<const deconstruct_movable(const MovableEnvelope<T>&, bool&)>
operator()(const boost::optional<MovableEnvelope<T>>& obj, bool& pass) const
{
if (!obj)
return nullptr;
return obj->OpenEnvelope(pass);
}
template <typename T>
result_t<const deconstruct_movable(const MovableEnvelope<T>&, bool&)>
operator()(const boost::optional<MovableEnvelope<T>>& obj, const bool& pass) const
{
// Note: this ignores the constness of pass because older compilers
// pass the incorrect constness.
if (!obj)
return nullptr;
return obj->OpenEnvelope(pass);
}
};
class deconstruct_movable_vector {
public:
template <typename> struct result;
template <typename F, typename MovableEnvelope_T, typename Bool>
struct result<F(MovableEnvelope_T, Bool)> {
using type = std::vector<std::unique_ptr<typename std::decay_t<MovableEnvelope_T>::value_type::enveloped_type>>;
};
template <typename FMETB>
using result_t = typename result<FMETB>::type;
template <typename T>
result_t<const deconstruct_movable(const std::vector<MovableEnvelope<T>>&, bool&)>
operator()(const std::vector<MovableEnvelope<T>>& objs, bool& pass) const
{ return OpenEnvelopes(objs, pass); }
template <typename T>
result_t<const deconstruct_movable(const std::vector<MovableEnvelope<T>>&, bool&)>
operator()(const std::vector<MovableEnvelope<T>>& objs, const bool& pass) const
{
// Note: this ignores the constness of pass because older compilers
// pass the incorrect constness.
return OpenEnvelopes(objs, pass);
}
// Unwrap ::optional<vector<MovablelEnvelope<T>>> and return
// an empty vector for none
template <typename T>
result_t<const deconstruct_movable(const std::vector<MovableEnvelope<T>>&, bool&)>
operator()(const boost::optional<std::vector<MovableEnvelope<T>>>& objs, bool& pass) const
{
if (!objs)
return {};
return OpenEnvelopes(*std::move(objs), pass);
}
template <typename T>
result_t<const deconstruct_movable(const std::vector<MovableEnvelope<T>>&, bool&)>
operator()(const boost::optional<std::vector<MovableEnvelope<T>>>& objs, const bool& pass) const
{
// Note: this ignores the constness of pass because older compilers
// pass the incorrect constness.
if (!objs)
return {};
return OpenEnvelopes(*std::move(objs), pass);
}
};
class deconstruct_movable_vector_pair {
public:
template <typename> struct result;
template <typename F, typename MovableEnvelope_T, typename Bool>
struct result<F(MovableEnvelope_T, Bool)> {
using type = std::vector<
std::pair<std::string,
std::unique_ptr<typename std::decay_t<MovableEnvelope_T>::value_type::second_type::enveloped_type>>>;
};
template <typename FMETB>
using result_t = typename result<FMETB>::type;
template <typename T>
result_t<const deconstruct_movable(
const std::vector<std::pair<std::string, MovableEnvelope<T>>>&, bool&)>
operator()(const std::vector<std::pair<std::string, MovableEnvelope<T>>>& objs, bool& pass) const
{ return OpenEnvelopes(objs, pass); }
template <typename T>
result_t<const deconstruct_movable(const std::vector<std::pair<std::string, MovableEnvelope<T>>>&, bool&)>
operator()(const std::vector<std::pair<std::string, MovableEnvelope<T>>>& objs, const bool& pass) const
{
// Note: this ignores the constness of pass because older compilers
// pass the incorrect constness.
return OpenEnvelopes(objs, pass);
}
};
}
#endif
|