ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

c – 用于组成类的Boost Karma生成器

2019-10-07 05:05:00  阅读:190  来源: 互联网

标签:c json boost boost-spirit


我有以下类图:

class diagram

有一些未使用的类,如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的新手,我已经阅读了关于boost网站的教程,但我不明白如何解决我的问题.我找不到关于那个宏的好教程.

我不想将现有的库用于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;
}

解决方法:

我建议不要使用Karma生成JSON.我建议强烈反对ADAPT_ADT(它容易出现非常微妙的UB错误,这意味着你正在尝试调整一些不是为它设计的东西.只是说不.).

这是我的看法.让我们走高速路,尽可能不引人注目.这意味着

>我们不能只重载运算符<<打印json(因为你可能想要自然地打印表达式)
>它还意味着负责生成JSON的任何函数都没有

>必须打扰json实现细节
>必须打扰漂亮的格式

>最后,我不想用任何特定于JSON的表达式来干扰表达式树.最可接受的是不透明的朋友声明.

一个简单的JSON工具:

这可能是最简单的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>;
}

就这样.这完全正常.让我们来证明一下

漂亮的印刷JSON

与表达式树本身一样,让我们​​不要硬连线,而是创建一个漂亮的打印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
    }
};

完整的演示

见它Live On Coliru

#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"
        }
      ]
    }
  ]
}

标签:c,json,boost,boost-spirit
来源: https://codeday.me/bug/20191007/1864705.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有