C ++ Mutually recursive variant type (again)

I have a problem similar to that described here: C ++ Mutually recursive variant type

I am trying to create a JSON representation in C ++. Many libraries already offer great JSON views and parsers, which are very fast, but I don't invent this wheel. I need to create a C ++ JSON view that supports certain space optimizations in certain conditions. In short, if and only if the JSON array contains homogeneous data, rather than storing each element in the form of bloated variants of options, I need a compact repository of native types. I also need to support heterogeneous arrays and standard nested JSON objects.

The following is “if the wishes were horses, beggars rode” to a version of the code that is intended to clearly illustrate the intention, but is clearly broken, because types are used before any declaration exists. I want not to specify the same information several times in types (i.e. Array, Object and Value should not require duplication of type specifications). I also want to avoid unnecessary time spent on execution.

#include <string> #include <unordered_map> #include <vector> #include <boost/variant.hpp> #include <boost/variant/variant.hpp> #include <boost/variant/recursive_wrapper.hpp> class JSONDocument { public: using String = std::string; using Integer = long; using Float = double; using Boolean = bool; using Null = void *; using Key = std::string; using Path = std::string; using Value = boost::variant< Null, String, Integer, Float, Boolean, Object, Array >; using Object = std::unordered_map<Key,Value>; using Array = boost::variant< std::vector<Null>, std::vector<String>, std::vector<Integer>, std::vector<Float>, std::vector<Boolean>, std::vector<Value> >; private: Value root; class value_traversal_visitor : public boost::static_visitor<Value> { public: value_traversal_visitor( Path path ) : path(path) {} Value operator()( Null x ) const { if( path.empty() ) { return x; } // otherwise throw ... } Value operator()( String x ) const { if( path.empty() ) { return x; } } ... // special handling for Array and Object types private: Path path; }; public: Value get( Path path ) { return boost::apply_visitor( value_traversal_visitor( path ), root ); } ... }; 

As you can see, I include the recursive_wrapper header. I tried various calls to boost :: make_recursive_variant and boost :: recursive_wrapper, but always get compiler errors. I don’t understand how the answer from the Cipe Mutually Recursive Variant Type resolves this, because in every attempt I get compiler errors (both from gcC ++ 5.3 and LLVM / clang ++ 3.8) which almost exclusively refers to Boost, which essentially it comes down to the fact that types are not convertible or declarations, or contradictory, or not existing. I would put one of my attempts along with specific compiler error messages here, but I don't know which of the many attempts to use.

I hope someone can set me in the right way ...

Thanks in advance!

Edit

To build on the answer below, here is an example of a working skeleton for types and their use.

 #include <string> #include <unordered_map> #include <vector> #include <boost/variant.hpp> #include <boost/variant/variant.hpp> #include <boost/variant/recursive_wrapper.hpp> using String = std::string; using Integer = long; using Float = double; using Boolean = bool; using Key = std::string; using Value = boost::make_recursive_variant< String, Integer, Float, Boolean, std::unordered_map<Key, boost::recursive_variant_>, boost::variant<std::vector<String>,std::vector<Integer>,std::vector<Float>,std::vector<Boolean>,std::vector<boost::recursive_variant_> > >::type; using Object = std::unordered_map<Key, Value>; using Array = boost::variant<std::vector<String>,std::vector<Integer>,std::vector<Float>,std::vector<Boolean>,std::vector<Value> >; int main( int argc, char* argv[] ) { Value v; v = static_cast<Integer>( 7 ); Object o; v = o; Array a = std::vector<Integer>( 3 ); v = a; return 0; } 
+5
source share
2 answers

You can simply 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>; 

Demo version

Live on coliru

As you can see, bits are not implemented in the code (never write functions that are not in return statements!). Also note the simplifications in the control flow for get and 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() {} 

WARNINGS

  • Note that you should not use void* for Null, because all kinds of unwanted implicit conversions
  • Note that you probably shouldn't use unordered_map because

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

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

+4
source

Not a solution as such, but a way to achieve a recursive variant using std :: variant. I thought this might be of interest, since stl does not provide any api for recursive and non-reselling types. Compiles with 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; } 
+1
source

Source: https://habr.com/ru/post/1271062/


All Articles