我有以下类图:
有一些未使用的类,如BinaryOperator
,但我的真实代码需要它们,所以我想在示例中保留它们。
我想使用boost::karma来获取此的JSON表示。 JSON应该类似于以下内容:
{
"name": "Plus",
"type": "Function",
"arguments": [
{
"name": "IntegerValue",
"type": "Value",
"value": "4"
},
{
"name": "Plus",
"type": "Function",
"arguments": [
{
"name": "IntegerValue",
"type": "Value",
"value": "5"
},
{
"name": "IntegerValue",
"type": "Value",
"value": "6"
}
]
}
]
}
因为它是一个简单的例子,我想为我的类使用BOOST_FUSION_ADAPT_ADT
宏来模块化生成器。
我是Karma的新手,我已经阅读过有关推广网站的教程,但我不明白如何解决我的问题。我无法找到关于该宏的一些好的教程。
我不想使用现有的JSON库,因为起初我想学习Karma,第二名JSON只是一个例子,我需要以多种格式导出我的表达式,我可以做到通过简单地更改生成器,而对我的类使用BOOST_FUSION_ADAPT_ADT
的代码应该是相同的。
您可以找到用于创建示例表达式的代码。我需要从哪里开始才能解决我的问题?
#include <boost/lexical_cast.hpp>
#include <iostream>
#include <vector>
class Expression {
public:
virtual std::string getName() const = 0;
};
class Value : public Expression {
public:
virtual std::string getValue() const = 0;
};
class IntegerValue : public Value {
public:
IntegerValue(int value) : m_value(value) {}
virtual std::string getName() const override { return "IntegerValue"; }
virtual std::string getValue() const override { return boost::lexical_cast<std::string>(m_value); }
private:
int m_value;
};
class Function : public Expression {
public:
void addArgument(Expression* expression) { m_arguments.push_back(expression); }
virtual std::string getName() const override { return m_name; }
protected:
std::vector<Expression*> m_arguments;
std::string m_name;
};
class Plus : public Function {
public:
Plus() : Function() { m_name = "Plus"; }
};
///////////////////////////////////////////////////////////////////////////////
int main(int argc, char **argv) {
// Build expression 4 + 5 + 6 as 4 + (5 + 6)
Function* plus1 = new Plus();
Function* plus2 = new Plus();
Value* iv4 = new IntegerValue(4);
Value* iv5 = new IntegerValue(5);
Value* iv6 = new IntegerValue(6);
plus2->addArgument(iv5);
plus2->addArgument(iv6);
plus1->addArgument(iv4);
plus1->addArgument(plus2);
// Generate json string here, but how?
return 0;
}
答案 0 :(得分:3)
我建议不要使用Karma生成JSON。我建议强烈反对ADAPT_ADT(它容易出现非常微妙的UB错误,这意味着你正在尝试调整一些不是为它设计的东西。只是说不。)
这是我的看法。让我们走高速路,尽可能不引人注目。这意味着
operator<<
来打印json(因为你可能希望自然地打印表达式)这也意味着任何负责生成JSON的函数都不是
最后,我不想用任何特定于JSON的表达式来介入表达式树。可接受的最多是 opaque 朋友声明。
这可能是最简单的JSON表示,但它执行所需的子集并做出一些明智的选择(支持重复属性,例如保留属性顺序):
#include <boost/variant.hpp>
namespace json {
// adhoc JSON rep
struct Null {};
using String = std::string;
using Value = boost::make_recursive_variant<
Null,
String,
std::vector<boost::recursive_variant_>,
std::vector<std::pair<String, boost::recursive_variant_> >
>::type;
using Property = std::pair<String, Value>;
using Object = std::vector<Property>;
using Array = std::vector<Value>;
}
这就是全部。这完全正常。让我们证明一下
与Expression树本身一样,让我们不要硬连线,而是创建一个漂亮的IO操纵器:
#include <iomanip>
namespace json {
// pretty print it
struct pretty_io {
using result_type = void;
template <typename Ref>
struct manip {
Ref ref;
friend std::ostream& operator<<(std::ostream& os, manip const& m) {
pretty_io{os,""}(m.ref);
return os;
}
};
std::ostream& _os;
std::string _indent;
void operator()(Value const& v) const {
boost::apply_visitor(*this, v);
}
void operator()(Null) const {
_os << "null";
}
void operator()(String const& s) const {
_os << std::quoted(s);
}
void operator()(Property const& p) const {
_os << '\n' << _indent; operator()(p.first);
_os << ": "; operator()(p.second);
}
void operator()(Object const& o) const {
pretty_io nested{_os, _indent+" "};
_os << "{";
bool first = true;
for (auto& p : o) { first||_os << ","; nested(p); first = false; }
_os << "\n" << _indent << "}";
}
void operator()(Array const& o) const {
pretty_io nested{_os, _indent+" "};
_os << "[\n" << _indent << " ";
bool first = true;
for (auto& p : o) { first||_os << ",\n" << _indent << " "; nested(p); first = false; }
_os << "\n" << _indent << "]";
}
};
Value to_json(Value const& v) { return v; }
template <typename T, typename V = decltype(to_json(std::declval<T const&>()))>
pretty_io::manip<V> pretty(T const& v) { return {to_json(v)}; }
}
to_json
这个东西被称为一个方便的ADL扩展点,你现在可以使用它了:
std::cout << json::pretty("hello world"); // prints as a JSON String
进行以下工作:
std::cout << json::pretty(plus1);
我们所需要的只是适当的to_json
重载。我们可以在那里记下所有内容,但我们可能最终需要“联系”一个名为to_json
的函数,更糟糕的是,从json
名称空间(json::Value
转发声明类型最小)。这太侵入了。所以,让我们添加一个微小的间接:
auto to_json(Expression const* expression) {
return serialization::call(expression);
}
诀窍是将JSON内容隐藏在一个不透明的结构中,然后我们可以成为朋友:struct serialization
。其余的很简单:
struct serialization {
static json::Value call(Expression const* e) {
if (auto* f = dynamic_cast<Function const*>(e)) {
json::Array args;
for (auto& a : f->m_arguments)
args.push_back(call(a));
return json::Object {
{ "name", f->getName() },
{ "type", "Function" },
{ "arguments", args },
};
}
if (auto* v = dynamic_cast<Value const*>(e)) {
return json::Object {
{ "name", v->getName() },
{ "type", "Value" },
{ "value", v->getValue() },
};
}
return {}; // Null in case we didn't implement a node type
}
};
#include <boost/lexical_cast.hpp>
#include <iostream>
#include <iomanip>
#include <vector>
struct Expression {
virtual std::string getName() const = 0;
};
struct Value : Expression {
virtual std::string getValue() const = 0;
};
struct IntegerValue : Value {
IntegerValue(int value) : m_value(value) {}
virtual std::string getName() const override { return "IntegerValue"; }
virtual std::string getValue() const override { return boost::lexical_cast<std::string>(m_value); }
private:
int m_value;
};
struct Function : Expression {
void addArgument(Expression *expression) { m_arguments.push_back(expression); }
virtual std::string getName() const override { return m_name; }
protected:
std::vector<Expression *> m_arguments;
std::string m_name;
friend struct serialization;
};
struct Plus : Function {
Plus() : Function() { m_name = "Plus"; }
};
///////////////////////////////////////////////////////////////////////////////
// A simple JSON facility
#include <boost/variant.hpp>
namespace json {
// adhoc JSON rep
struct Null {};
using String = std::string;
using Value = boost::make_recursive_variant<
Null,
String,
std::vector<boost::recursive_variant_>,
std::vector<std::pair<String, boost::recursive_variant_> >
>::type;
using Property = std::pair<String, Value>;
using Object = std::vector<Property>;
using Array = std::vector<Value>;
}
///////////////////////////////////////////////////////////////////////////////
// Pretty Print manipulator
#include <iomanip>
namespace json {
// pretty print it
struct pretty_io {
using result_type = void;
template <typename Ref>
struct manip {
Ref ref;
friend std::ostream& operator<<(std::ostream& os, manip const& m) {
pretty_io{os,""}(m.ref);
return os;
}
};
std::ostream& _os;
std::string _indent;
void operator()(Value const& v) const {
boost::apply_visitor(*this, v);
}
void operator()(Null) const {
_os << "null";
}
void operator()(String const& s) const {
_os << std::quoted(s);
}
void operator()(Property const& p) const {
_os << '\n' << _indent; operator()(p.first);
_os << ": "; operator()(p.second);
}
void operator()(Object const& o) const {
pretty_io nested{_os, _indent+" "};
_os << "{";
bool first = true;
for (auto& p : o) { first||_os << ","; nested(p); first = false; }
_os << "\n" << _indent << "}";
}
void operator()(Array const& o) const {
pretty_io nested{_os, _indent+" "};
_os << "[\n" << _indent << " ";
bool first = true;
for (auto& p : o) { first||_os << ",\n" << _indent << " "; nested(p); first = false; }
_os << "\n" << _indent << "]";
}
};
Value to_json(Value const& v) { return v; }
template <typename T, typename V = decltype(to_json(std::declval<T const&>()))>
pretty_io::manip<V> pretty(T const& v) { return {to_json(v)}; }
}
///////////////////////////////////////////////////////////////////////////////
// Expression -> JSON
struct serialization {
static json::Value call(Expression const* e) {
if (auto* f = dynamic_cast<Function const*>(e)) {
json::Array args;
for (auto& a : f->m_arguments)
args.push_back(call(a));
return json::Object {
{ "name", f->getName() },
{ "type", "Function" },
{ "arguments", args },
};
}
if (auto* v = dynamic_cast<Value const*>(e)) {
return json::Object {
{ "name", v->getName() },
{ "type", "Value" },
{ "value", v->getValue() },
};
}
return {};
}
};
auto to_json(Expression const* expression) {
return serialization::call(expression);
}
int main() {
// Build expression 4 + 5 + 6 as 4 + (5 + 6)
Function *plus1 = new Plus();
Function *plus2 = new Plus();
Value *iv4 = new IntegerValue(4);
Value *iv5 = new IntegerValue(5);
Value *iv6 = new IntegerValue(6);
plus2->addArgument(iv5);
plus2->addArgument(iv6);
plus1->addArgument(iv4);
plus1->addArgument(plus2);
// Generate json string here, but how?
std::cout << json::pretty(plus1);
}
根据您的问题输出图片完美:
{
"name": "Plus",
"type": "Function",
"arguments": [
{
"name": "IntegerValue",
"type": "Value",
"value": "4"
},
{
"name": "Plus",
"type": "Function",
"arguments": [
{
"name": "IntegerValue",
"type": "Value",
"value": "5"
},
{
"name": "IntegerValue",
"type": "Value",
"value": "6"
}
]
}
]
}