C++ Mutually Recursive Variant Type (Again) C++ Mutually Recursive Variant Type (Again) json json

C++ Mutually Recursive Variant Type (Again)


You could just use recursive_variant_ placeholder with make_recursive_variant.

Here's the gist:

using Value   = boost::make_recursive_variant<    Null,     String,     Integer,     Float,     Boolean,    std::unordered_map<Key, boost::recursive_variant_>, // Object    std::vector<boost::recursive_variant_>              // Array>::type;using Object = std::unordered_map<Key, Value>;using Array = boost::variant<Value>;

Live Demo

Live On Coliru

As you can see there's unimplemented bits in the code (never write functions missing return statements!). Also note the simplifications in control flow for get and the private visitor implementation.

#include <boost/variant.hpp>#include <boost/variant/recursive_wrapper.hpp>#include <boost/variant/variant.hpp>#include <string>#include <unordered_map>#include <vector>class JSONDocument {  public:    struct Null { constexpr bool operator==(Null) const { return true; } };    using String  = std::string;    using Integer = long;    using Float   = double;    using Boolean = bool;    using Key     = std::string;    using Path    = std::string;    using Value   = boost::make_recursive_variant<        Null,         String,         Integer,         Float,         Boolean,        std::unordered_map<Key, boost::recursive_variant_>, // Object        std::vector<boost::recursive_variant_>              // Array    >::type;    using Object = std::unordered_map<Key, Value>;    using Array = boost::variant<Value>;  private:    Value root;    struct value_traversal_visitor {        Path path;        using result_type = Value;        result_type operator()(Value const &x) const {            if (path.empty()) {                return x;            }            return boost::apply_visitor(*this, x);        }        result_type operator()(Null)           const { throw std::invalid_argument("null not addressable"); }        result_type operator()(String const &) const { throw std::invalid_argument("string not addressable"); }        // special handling for Array and Object types TODO        template <typename T> result_type operator()(T &&) const { return Null{}; }    };  public:    Value get(Path path) { return value_traversal_visitor{path}(root); }};int main() {}

CAVEATS

  • Note that you should NOT use void* for Null because all manner of unwanted implicit conversions
  • Note that you should probably not use unordered_map because

    • some JSON implementations allow duplicate property names
    • some JSON applications depend on the ordering of the properties

See also https://github.com/sehe/spirit-v2-json/blob/master/json.hpp#L37


Not a solution per se, but Here's a way to achieve variant recursivity using std::variant. I thought this might be of interest, since the stl doesn't provide any api for recursive, nor forward-declared types. Compiles using gcc 7.2 -std=c++17

#include <variant>#include <vector>#include <iostream>#include <algorithm>using namespace std;struct Nil {};struct vector1;using var_t1 = variant<Nil, int, vector1>;using var_t2 = variant<Nil, double, float, int, var_t1>;struct vector1 {     vector<var_t2> v_; };struct print_var_t2;struct print_var_t1 {    void operator()(const vector1& v);    void operator()(int) { cout << "int\n"; }    void operator()(const Nil&) { cout  << "nil\n"; }};struct print_var_t2 {    void operator()(const Nil&) { cout << "Nil\n"; }     void operator()(int)  { cout << "int\n"; }    void operator()(double) { cout << "double\n"; }    void operator()(float)  { cout << "float\n"; }    void operator()(const var_t1& v);};void print_var_t1::operator()(const vector1& v) {    for_each(v.v_.begin(), v.v_.end(), [](const var_t2& x)    {        visit(print_var_t2{}, x);    });}void print_var_t2::operator()(const var_t1& v) {    visit(print_var_t1{}, v);    }int main(){    vector1 v1;    v1.v_.push_back(.1);    v1.v_.push_back(2.f);    v1.v_.push_back(3);    v1.v_.push_back(var_t2{3});    var_t1 var1 = v1;    std::visit(print_var_t1{}, var1);    return 0;}