C++ 相互递归变体类型(再次)
C++ Mutually Recursive Variant Type (Again)
我遇到的问题与此处描述的类似:C++ Mutually Recursive Variant Type
我正在尝试用 C++ 创建一个 JSON 表示。许多库已经提供了非常快的 excellent JSON 表示和解析器,但我并没有重新发明这个轮子。我需要创建一个 C++ JSON 表示,在特定条件下支持某些 space 优化。简而言之,当且仅当 JSON 数组包含同质数据时,而不是将每个元素都存储为臃肿的变体类型,我需要原生类型的紧凑存储。我还需要支持异构数组和标准嵌套 JSON objects.
以下是代码的 "if wishes were horses, beggars would ride" 版本,旨在清楚地说明意图,但显然有问题,因为在任何声明存在之前就使用了类型。我想避免在类型中多次指定相同的信息(即 Array、Object 和 Value 不应要求重复的类型规范)。我还想避免任何不必要的高 run-time 成本。
#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 );
}
...
};
如您所见,我包括了 recursive_wrapper
header。我尝试了 boost::make_recursive_variant 和 boost::recursive_wrapper 的各种调用,但我总是遇到编译器错误。我看不出 C++ Mutually Recursive Variant Type 的答案是如何解决这个问题的,因为在每次尝试中,我都会遇到编译器错误(来自 gcc++ 5.3 和 LLVM/clang++ 3.8),这些错误几乎完全引用了 Boost,基本上可以归结为类型不可转换或声明冲突或不存在。我会把我的一次尝试连同特定的编译器错误消息放在这里,但我不知道使用许多尝试中的哪一个。
我希望有人可以让我走上正确的道路...
提前致谢!
编辑
只是为了在下面接受的答案的基础上,这里有一个类型及其用法的工作框架示例。
#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;
}
你可以 use recursive_variant_
placeholder with make_recursive_variant
.
要点如下:
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>;
现场演示
如您所见,代码中有未实现的位(切勿编写缺少 return 语句的函数!)。另请注意 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() {}
注意事项
- 请注意,您不应将
void*
用于 Null,因为各种不需要的隐式转换
请注意,您可能不应该使用 unordered_map
,因为
- 一些 JSON 实现允许重复 属性 名称
- 一些 JSON 应用程序取决于属性的顺序
另见 https://github.com/sehe/spirit-v2-json/blob/master/json.hpp#L37
本身不是解决方案,但这是使用 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;
}
我遇到的问题与此处描述的类似:C++ Mutually Recursive Variant Type
我正在尝试用 C++ 创建一个 JSON 表示。许多库已经提供了非常快的 excellent JSON 表示和解析器,但我并没有重新发明这个轮子。我需要创建一个 C++ JSON 表示,在特定条件下支持某些 space 优化。简而言之,当且仅当 JSON 数组包含同质数据时,而不是将每个元素都存储为臃肿的变体类型,我需要原生类型的紧凑存储。我还需要支持异构数组和标准嵌套 JSON objects.
以下是代码的 "if wishes were horses, beggars would ride" 版本,旨在清楚地说明意图,但显然有问题,因为在任何声明存在之前就使用了类型。我想避免在类型中多次指定相同的信息(即 Array、Object 和 Value 不应要求重复的类型规范)。我还想避免任何不必要的高 run-time 成本。
#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 );
}
...
};
如您所见,我包括了 recursive_wrapper
header。我尝试了 boost::make_recursive_variant 和 boost::recursive_wrapper 的各种调用,但我总是遇到编译器错误。我看不出 C++ Mutually Recursive Variant Type 的答案是如何解决这个问题的,因为在每次尝试中,我都会遇到编译器错误(来自 gcc++ 5.3 和 LLVM/clang++ 3.8),这些错误几乎完全引用了 Boost,基本上可以归结为类型不可转换或声明冲突或不存在。我会把我的一次尝试连同特定的编译器错误消息放在这里,但我不知道使用许多尝试中的哪一个。
我希望有人可以让我走上正确的道路...
提前致谢!
编辑
只是为了在下面接受的答案的基础上,这里有一个类型及其用法的工作框架示例。
#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;
}
你可以 use recursive_variant_
placeholder with make_recursive_variant
.
要点如下:
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>;
现场演示
如您所见,代码中有未实现的位(切勿编写缺少 return 语句的函数!)。另请注意 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() {}
注意事项
- 请注意,您不应将
void*
用于 Null,因为各种不需要的隐式转换 请注意,您可能不应该使用
unordered_map
,因为- 一些 JSON 实现允许重复 属性 名称
- 一些 JSON 应用程序取决于属性的顺序
另见 https://github.com/sehe/spirit-v2-json/blob/master/json.hpp#L37
本身不是解决方案,但这是使用 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;
}