|
Boost : |
Subject: [boost] [variant] awkward recursion
From: Dave Abrahams (dave_at_[hidden])
Date: 2012-10-29 15:29:16
I've been playing around with variant a bit and decided to try to build
a data structure for storing json. I came up with several different
approaches but none are super-satisfying. I'd like some feedback. (If
you need a refresher on what's in json, the front page of
http://json.org tells all).
In the abstract, json looks like:
struct null {};
typedef std::map<std::string, value> object;
typedef std::vector<value> array;
typedef variant<
null, std::string, double, object, array, bool
> value;
Naturally, this code isn't legal C++, so I set about making it be.
The first thing I tried was to use recursive_wrapper<>, as the
documentation implies it is the most flexible way to do this sort of
thing. Unfortunately, there's no clean way to reformulate this type
in a way that allows recursive_wrapper work, because the type that needs
to be wrapped in recursive_wrapper includes "value" in its name, and I
can't forward-declare it.
The only way to forward-declare a type X, as far as I know, is:
struct X; // or class X;
So to try to get recursive_wrapper to work, I tried a couple of
formulations that started with that, but it turns out that when you do
this, you end up not needing recursive_wrapper at all
struct value;
typedef std::map<std::string, value> object;
typedef std::vector<value> array;
typedef variant<
null, std::string, double, object, array, bool
> value_base;
struct value : value_base
{
value() {};
template <class T>
value(T const& x) : base(x) {} // no move; I'm in C++03-land
value& operator=(value const& x) { base::operator=(x); return *this; }
}
};
I don't know if I've missed some things that should be in that "value"
class. I know that a recursive variant, in principle, requires a
pointer indirection somewhere, so I was wondering if, had I wrapped
object and array in recursive_wrapper as below, I would have been adding
an unnecessary inefficiency.
typedef variant<
null, std::string, double,
reference_wrapper<object>,
reference_wrapper<array>,
bool
> value_base;
I also wondered whether that would give me different results from
typedef std::map<std::string, value> object;
typedef std::vector<value> array;
typedef variant<
null, std::string, double,
std::map<std::string, reference_wrapper<value> >,
std::vector<reference_wrapper<value> >,
bool
> value_base;
and which one was better. Does anyone know? I think that should be in
the docs. Using recursive_variant_ works out quite nicely, except that
it doesn't allow me to separately declare "object" and "array" and
re-use them in the definition of "value":
typedef boost::make_recursive_variant<
null
, std::string
, double
, std::map<std::string, boost::recursive_variant_>
, std::vector<boost::recursive_variant_>
, bool
>::type value;
I also wonder if I'm getting an unnecessary inefficiency in that case.
Lastly, what flexibility am I sacrificing by using this approach?
-- Dave Abrahams BoostPro Computing Software Development Training http://www.boostpro.com Clang/LLVM/EDG Compilers C++ Boost
Boost list run by bdawes at acm.org, gregod at cs.rpi.edu, cpdaniel at pacbell.net, john at johnmaddock.co.uk