I have a problem similar to that described here: C++ Mutually Recursive Variant Type
我有一个类似于这里描述的问题:C ++相互递归变体类型
I am trying to create a JSON representation in C++. Many libraries already offer excellent JSON representations and parsers that are very fast, but I am not reinventing this wheel. I need to create a C++ JSON representation that supports certain space optimizations under specific conditions. In short, if and only if a JSON array contains homogenous data, rather than storing every element as bloated variant types, I need compact storage of native types. I also need to support heterogeneous arrays and standard nested JSON objects.
我试图在C ++中创建一个JSON表示。许多库已经提供了非常快的优秀JSON表示和解析器,但我并没有重新发明这个*。我需要创建一个C ++ JSON表示,支持在特定条件下的某些空间优化。简而言之,当且仅当JSON数组包含同质数据,而不是将每个元素存储为膨胀变体类型时,我需要本机类型的紧凑存储。我还需要支持异构数组和标准嵌套JSON对象。
The following is the "if wishes were horses, beggars would ride" version of the code, which is meant to clearly illustrate intent, but is obviously broken because types are used before any declaration exists. I want to avoid specifying the same information multiple times in types (i.e. Array, Object, and Value should not require duplicated type specifications). I also want to avoid any unnecessarily high run-time costs.
以下是“如果希望是马,乞丐会骑”版本的代码,这是为了清楚地说明意图,但显然是因为在任何声明存在之前使用类型而被破坏。我想避免在类型中多次指定相同的信息(即Array,Object和Value不应该要求重复的类型规范)。我还想避免任何不必要的高运行时间成本。
#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 am including the recursive_wrapper
header. I have tried various invocations of boost::make_recursive_variant and boost::recursive_wrapper, but I always get compiler errors. I do not see how the answer from C++ Mutually Recursive Variant Type solves this, because in every attempt, I get compiler errors (from both gcc++ 5.3 and LLVM/clang++ 3.8) that almost exclusively reference Boost that essentially boil down to types not being convertible or declarations either conflicting or not existing. I would put one of my attempts along with specific compiler error messages here, but I wouldn't know which of the many attempts to use.
如您所见,我包含recursive_wrapper标头。我尝试过各种boost :: make_recursive_variant和boost :: recursive_wrapper的调用,但我总是遇到编译器错误。我没有看到C ++ Mutually Recursive Variant Type的答案如何解决这个问题,因为在每一次尝试中,我都会遇到编译器错误(来自gcc ++ 5.3和LLVM / clang ++ 3.8)几乎完全引用Boost,它基本上归结为不可转换的类型或声明冲突或不存在。我会在这里尝试我的一个尝试以及特定的编译器错误消息,但我不知道使用多少尝试中的哪一个。
I'm hoping somebody can set me on the right path...
我希望有人能让我走上正确的道路......
Thanks in advance!
提前致谢!
Edit
Just to build on the accepted answer below, here is an example of a working skeleton for the types and their usages.
只是建立在下面接受的答案的基础上,这里是一个类型及其用法的工作框架的例子。
#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;
}
2 个解决方案
#1
4
You could just use recursive_variant_
placeholder with make_recursive_variant
.
你可以使用带有make_recursive_variant的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
住在科利鲁
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.
正如您所看到的,代码中没有未实现的位(永远不会写函数缺少返回语句!)。还要注意get和私有访问者实现的控制流程的简化。
#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请注意,您可能不应该使用unordered_map,因为
- some JSON implementations allow duplicate property names
- some JSON applications depend on the ordering of the properties
一些JSON实现允许重复的属性名称
一些JSON应用程序依赖于属性的顺序
请注意,您不应将void *用于Null,因为所有不必要的隐式转换都是如此
See also https://github.com/sehe/spirit-v2-json/blob/master/json.hpp#L37
另见https://github.com/sehe/spirit-v2-json/blob/master/json.hpp#L37
#2
1
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
本身不是解决方案,但这是使用std :: variant实现变体递归的一种方法。我认为这可能是有意义的,因为stl不为递归或前向声明的类型提供任何api。使用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
4
You could just use recursive_variant_
placeholder with make_recursive_variant
.
你可以使用带有make_recursive_variant的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
住在科利鲁
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.
正如您所看到的,代码中没有未实现的位(永远不会写函数缺少返回语句!)。还要注意get和私有访问者实现的控制流程的简化。
#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请注意,您可能不应该使用unordered_map,因为
- some JSON implementations allow duplicate property names
- some JSON applications depend on the ordering of the properties
一些JSON实现允许重复的属性名称
一些JSON应用程序依赖于属性的顺序
请注意,您不应将void *用于Null,因为所有不必要的隐式转换都是如此
See also https://github.com/sehe/spirit-v2-json/blob/master/json.hpp#L37
另见https://github.com/sehe/spirit-v2-json/blob/master/json.hpp#L37
#2
1
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
本身不是解决方案,但这是使用std :: variant实现变体递归的一种方法。我认为这可能是有意义的,因为stl不为递归或前向声明的类型提供任何api。使用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;
}