【发布时间】:2017-08-21 18:42:14
【问题描述】:
我遇到了与此处所述类似的问题:C++ Mutually Recursive Variant Type
我正在尝试在 C++ 中创建 JSON 表示。许多库已经提供了 优秀 JSON 表示和非常快的解析器,但我并没有重新发明这个轮子。我需要创建一个在特定条件下支持某些空间优化的 C++ JSON 表示。简而言之,当且仅当 JSON 数组包含同质数据,而不是将每个元素存储为臃肿的变体类型时,我才需要压缩存储原生类型。我还需要支持异构数组和标准的嵌套 JSON 对象。
以下是代码的“如果愿望是马,乞丐会骑”版本的代码,旨在清楚地说明意图,但由于在任何声明存在之前就使用了类型,因此显然被破坏了。我想避免在类型中多次指定相同的信息(即数组、对象和值不应该需要重复的类型规范)。我还想避免任何不必要的高运行时成本。
#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 标头。我尝试过各种 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;
}
【问题讨论】:
-
为什么不将 unique_ptr 用于递归类型?这基本上就是包装器所做的。
-
@MichaëlRoy 我对整个项目最大的问题之一就是如何正确地声明。如果我根据 unique_ptr
-
如果一切都失败了,您可以将有问题的类型包装在一个结构中。这你可以前向声明。我不久前写了一个 AST,特别是我的一些向量必须这样做。你最终得到一个 unique_ptr
。我知道这很丑,但它确实有效。 -
@MichaëlRoy 这就是他们在下面提供的链接中采用的最终方法。我认为这是一种完全灵活的方法,如果做得好,不一定会涉及任何额外的运行时间成本,但我无法摆脱这样一种感觉,即应该有一种方法可以仅使用变体设施来做到这一点。为了其他有类似问题的人,我会本着这种精神寻找答案。也许 sehe 还可以提供一个。
标签: c++ json boost boost-variant