Большое спасибо!!!!

я есть следующая диаграмма классов:

Там какой-то неиспользуемый класс, какBinaryOperator, но мой реальный код нуждается в них, поэтому я хочу сохранить их также в примере.

Я хочу использоватьповышение :: карму для того, чтобы получить представление 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 макрос для моих классов, чтобы модулировать генератор.

Я новичок в Карме, я прочитал учебник на сайте Boost, но я не понимаю, как атаковать мою проблему. Я не могу найти хороший учебник по этому макросу.

Я не хочу использовать существующие библиотеки для JSON, потому что сначала я хочу изучать карму, а во-вторых, 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;
}

Ответы на вопрос(2)

Решение Вопроса

товалсильно против ADAPT_ADT (это склонно к очень тонким ошибкам UB, и это означает, что вы пытаетесь адаптировать что-то, что не было разработано для этого. Просто скажите нет).

Вот мой взгляд на это. Давайте поедем по большой дороге и будем максимально ненавязчивыми. Это значит

Мы не можем просто перегрузитьoperator<< напечатать 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>;
}

Вот и все. Это полностью функционально. Давай докажем это

Pretty Printing JSON

Как и в случае с самим деревом выражений, давайте не будем связывать это, а вместо этого создадим симпатичный манипулятор ввода-вывода:

#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"
        }
      ]
    }
  ]
}
 sehe20 окт. 2017 г., 03:39
PS. Я забыл упомянуть, что - очевидно - вы должны рассмотреть библиотеку JSON, но подход все равно будет работать. Если вы умны, вы можете поручитьserialization структурировать только с независимыми средствами доступа к фону / формату и отделить все серверные части от разных TU. Я оставлю это в качестве пресловутого упражнения для читателя.
 Jepessen20 окт. 2017 г., 07:15
Спасибо, дело в том, что json - это только один из многих форматов, которые мне нужно использовать, какой-то формат проприетарный и нет библиотек, поэтому я хочу использовать единый способ для всех. Я решил использовать json для вопроса, потому что известен сообществу больше, чем, например, asciimath или другие форматы, созданные нами.

дело в том, что json - это только один из многих форматов, которые мне нужно использовать, какой-то формат проприетарный и нет библиотек, поэтому я хочу использовать единый способ для всех. Я решил использовать json для вопроса, потому что известно сообществу больше, чем, например, asciimath или другие форматы, созданные нами -Jepessen 9 часов назад

Это ничего не меняет в моей рекомендации. Во всяком случае, это действительно подчеркивает, что вы не хотите навязывать произвольные ограничения.

Проблемы с кармой

Карма - это встроенный DSL для статически генерируемых генераторов. Они хорошо работают для статически типизированных вещей. Ваш AST использует динамический полиморфизм.

Это исключает любую возможность написания краткого генератора, исключающего использование множества сложных семантических действий. Я не помню, чтобы было написано много явных ответов, связанных с кармой, но проблемы как с динамическим полиморфизмом, так и с семантическими действиями во многом совпадают со стороной ци:

Как я могу использовать полиморфные атрибуты с парсерами boost :: spirit :: qi?Boost Spirit: «Семантические действия - это зло»?

Применяются все недостатки, за исключением того, что создание AST не происходит, поэтому эффект распределения ресурсов менее серьезен, чем при использовании синтаксических анализаторов Qi.

Однако та же логика остается неизменной: генераторы кармы статически объединены для эффективности. Однако ваша динамическая иерархия типов исключает большую часть этой эффективности. Другими словами, вы не являетесь целевой аудиторией для кармы.

У кармы есть еще одно структурное ограничение, которое будет кусаться здесь, независимо от того, как устроен ваш AST: (очень) сложно использовать правила с сохранением состояния, чтобы делать красивую печать.

Для меня это ключевая причина, по которой я практически никогда не использую карму. Даже если красивая печать не является целью, вы все равно можете получить аналогичный пробег, просто генерируя выходные данные, посещая AST, используя Boost Fusion напрямую (мы использовали это в нашем проекте для создания различных версий OData XML и JSON-представлений типов API для использования в остальных API-интерфейсах ).

Конечно, естьнесколько генерирующие задачи с состоянием, которые имеют собственные директивы, встроенные в карму, и иногда они попадают в точку для быстрого создания прототипов, например,

Запись UBL-матрицы Boost в текстовый файлХотя это становитсяпослушный быстроНепоследовательное поведение колонки директивы генератора в карме повышения, Многие причуды возникают из-за того, что суб-генераторы «подсчитываются» с помощью w.r.t.columns[] директиваДавайте сделаем это в любом случае

Поскольку я не мазохист, я заимствую концепцию издругой ответ: создание промежуточного представления, которое намного облегчает карму.

В этом примере промежуточное представление может быть чрезвычайно простым, но я подозреваю, что другие ваши требования, такие как«Например, asciimath или другие форматы, созданные нами» потребуется более детальный дизайн.

///////////////////////////////////////////////////////////////////////////////
// A simple intermediate representation
#include <boost/variant.hpp>
namespace output_ast {
    struct Function;
    struct Value;
    using Expression = boost::variant<Function, Value>;

    using Arguments = std::vector<Expression>;

    struct Value    { std::string name, value; };
    struct Function { std::string name; Arguments args; };
}

Во-первых, поскольку мы собираемся использовать карму, нам нужно адаптировать промежуточное представление:

#include <boost/fusion/include/struct.hpp>
BOOST_FUSION_ADAPT_STRUCT(output_ast::Value, name, value)
BOOST_FUSION_ADAPT_STRUCT(output_ast::Function, name, args)
Генератор

Вот самый простой генератор, о котором я могу подумать, дать и взять 2 вещи:

Я подправил его в течение значительного времени, чтобы получить некоторый «читаемый» формат. Это становится проще, если вы удалите все незначительные пробелы.Я решил не хранить избыточную информацию (например, статическое представление типа в промежуточном представлении). Это немного усложнит, в основном, делаяtype Правило больше похоже наname а такжеvalue.
namespace karma_json {
    namespace ka = boost::spirit::karma;

    template <typename It>
    struct Generator : ka::grammar<It, output_ast::Expression()> {
        Generator() : Generator::base_type(expression) {
            expression = function|value;

            function
                = "{\n  " << ka::delimit(",\n  ") 
                   [name << type(+"Function") ]
                << arguments 
                << "\n}"
                ;

            arguments = "\"arguments\": [" << -(("\n  " << expression) % ",") << ']';

            value
                = "{\n  " << ka::delimit(",\n  ") 
                    [name << type(+"Value") ]
                << value_ 
                << "\n}"
                ;

            type   = "\"type\":\"" << ka::string(ka::_r1) << "\"";
            string = '"' << *('\\' << ka::char_("\\\"") | ka::char_) << '"';
            name   = "\"name\":" << string;
            value_ = "\"value\":" << string;
        }

      private:
        ka::rule<It, output_ast::Expression()> expression;
        ka::rule<It, output_ast::Function()> function;
        ka::rule<It, output_ast::Arguments()> arguments;
        ka::rule<It, output_ast::Value()> value;
        ka::rule<It, std::string()> string, name, value_;
        ka::rule<It, void(std::string)> type;
    };
}

Пост скриптум

Я делал упрощенный взгляд на полноту. И столкнулся с этимотлично демонстрация совершенно неочевидных особенностей обработки атрибутов. Следующее (просто удаление пробелов) делаетне работай:

function = '{' << ka::delimit(',') [name << type] << arguments << '}';
value = '{' << ka::delimit(',') [name << type] << value_ << '}' ;

Вы можете прочитатьроман об ошибкахВот если тебе нравится драма. Проблема в том, чтоdelimit[] Блок волшебным образом объединяет атрибуты в одну строку (да). Сообщение об ошибке отражает, что строковый атрибут не использовался, например, когда начинаяarguments генератор.

Самый прямой способ лечения симптома - разбить атрибут, но реального пути нет:

function = '{' << ka::delimit(',') [name << ka::eps << type] << arguments << '}';
value = '{' << ka::delimit(',') [name << ka::eps << type] << value_ << '}' ;

Нет разницы

function = '{' << ka::delimit(',') [ka::as_string[name] << ka::as_string[type]] << arguments << '}';
value = '{' << ka::delimit(',') [ka::as_string[name] << ka::as_string[type]] << value_ << '}' ;

Было бы неплохо, если бы это действительно сработало. Никакое количество добавления не включает или заменяет на такие заклинания, какka::as<std::string>()[...] ошибка компиляции исчезла.

Итак, чтобы просто закончить эту историю, мы опустимся до утомительно-утомительного:

function = '{' << name << ',' << type << ',' << arguments << '}';
arguments = "\"arguments\":[" << -(expression % ',') << ']';

Смотрите раздел с надписью«Упрощенная версия» ниже для живого демо.

Используй это

Кратчайший способ создания этой грамматики - создать промежуточное представление:

///////////////////////////////////////////////////////////////////////////////
// Expression -> output_ast
struct serialization {
    static output_ast::Expression call(Expression const* e) {
        if (auto* f = dynamic_cast<Function const*>(e)) {
            output_ast::Arguments args;
            for (auto& a : f->m_arguments) args.push_back(call(a));
            return output_ast::Function { f->getName(), args };
        }

        if (auto* v = dynamic_cast<Value const*>(e)) {
            return output_ast::Value { v->getName(), v->getValue() };
        }

        return {};
    }
};

auto to_output(Expression const* expression) {
    return serialization::call(expression);
}

И используйте это:

using It = boost::spirit::ostream_iterator;
std::cout << format(karma_json::Generator<It>{}, to_output(plus1));
Полная демонстрация

Жить на Wandbox¹

#include <boost/lexical_cast.hpp>
#include <iostream>
#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 intermediate representation
#include <boost/variant.hpp>
namespace output_ast {
    struct Function;
    struct Value;
    using Expression = boost::variant<Function, Value>;

    using Arguments = std::vector<Expression>;

    struct Value    { std::string name, value; };
    struct Function { std::string name; Arguments args; };
}

#include <boost/fusion/include/struct.hpp>
BOOST_FUSION_ADAPT_STRUCT(output_ast::Value, name, value)
BOOST_FUSION_ADAPT_STRUCT(output_ast::Function, name, args)

#include <boost/spirit/include/karma.hpp>
namespace karma_json {
    namespace ka = boost::spirit::karma;

    template <typename It>
    struct Generator : ka::grammar<It, output_ast::Expression()> {
        Generator() : Generator::base_type(expression) {
            expression = function|value;

            function
                = "{\n  " << ka::delimit(",\n  ") 
                   [name << type(+"Function") ]
                << arguments 
                << "\n}"
                ;

            arguments = "\"arguments\": [" << -(("\n  " << expression) % ",") << ']';

            value
                = "{\n  " << ka::delimit(",\n  ") 
                    [name << type(+"Value") ]
                << value_ 
                << "\n}"
                ;

            type   = "\"type\":\"" << ka::string(ka::_r1) << "\"";
            string = '"' << *('\\' << ka::char_("\\\"") | ka::char_) << '"';
            name   = "\"name\":" << string;
            value_ = "\"value\":" << string;
        }

      private:
        ka::rule<It, output_ast::Expression()> expression;
        ka::rule<It, output_ast::Function()> function;
        ka::rule<It, output_ast::Arguments()> arguments;
        ka::rule<It, output_ast::Value()> value;
        ka::rule<It, std::string()> string, name, value_;
        ka::rule<It, void(std::string)> type;
    };
}

///////////////////////////////////////////////////////////////////////////////
// Expression -> output_ast
struct serialization {
    static output_ast::Expression call(Expression const* e) {
        if (auto* f = dynamic_cast<Function const*>(e)) {
            output_ast::Arguments args;
            for (auto& a : f->m_arguments) args.push_back(call(a));
            return output_ast::Function { f->getName(), args };
        }

        if (auto* v = dynamic_cast<Value const*>(e)) {
            return output_ast::Value { v->getName(), v->getValue() };
        }

        return {};
    }
};

auto to_output(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?
    using It = boost::spirit::ostream_iterator;
    std::cout << format(karma_json::Generator<It>{}, to_output(plus1));
}
Выход

Генератор работает так же читабельно / надежно / функционально, как хотелось бы (есть причуды, связанные с разделителями, есть проблемы, когда тип содержит символы, которые нужно заключать в кавычки, нет отступа с состоянием).

Результат выглядит не так, как ожидалось, хотя это действительно 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"
}]
}]
}

Исправить это ... хороший вызов, если вы хотите попробовать.

Упрощенная версия

Упрощенная версия, дополненная обходным путем обработки атрибутов, описанным выше:

Жить на Колиру

namespace karma_json {
    namespace ka = boost::spirit::karma;

    template <typename It>
    struct Generator : ka::grammar<It, output_ast::Expression()> {
        Generator() : Generator::base_type(expression) {
            expression = function|value;

            function = '{' << name << ',' << type << ',' << arguments << '}';
            arguments = "\"arguments\":[" << -(expression % ',') << ']';

            value = '{' << name << ',' << type << ',' << value_ << '}' ;

            string = '"' << *('\\' << ka::char_("\\\"") | ka::char_) << '"';
            type   = "\"type\":" << string;
            name   = "\"name\":" << string;
            value_ = "\"value\":" << string;
        }

      private:
        ka::rule<It, output_ast::Expression()> expression;
        ka::rule<It, output_ast::Function()> function;
        ka::rule<It, output_ast::Arguments()> arguments;
        ka::rule<It, output_ast::Value()> value;
        ka::rule<It, std::string()> string, name, type, value_;
    };
}

Получает следующий вывод:

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

Я склонен думать, что этомного лучшее соотношение цена / качество, чем неудачная попытка «красивого» форматирования. Но настоящая история здесь в том, что затраты на техническое обслуживание в любом случае зашкаливают.

¹ Интересно, что Coliru превышает время компиляции ... Это тоже может быть аргумент, определяющий ваши дизайнерские решения

² заставляет задуматься, сколько людей на самом деле используют карму изо дня в день

 Jepessen21 окт. 2017 г., 10:27
Большое спасибо!!!!
 sehe21 окт. 2017 г., 01:30
Я сомневаюсь, сколько времени я потратил на борьбу с Кармой, чтобы получить ADT / полиморфную версию. Вот что наконец-то сработало:wandbox.org/permlink/H4ybpbM5hEi2c78x TL / DR: тыне можешь используйте полиморфные типы с кармой, конечно же, не с абстрактными типами. Все атрибуты должны быть копируемыми и конструируемыми по умолчанию. BOOST_ADAPT_ADT_NAMED не работает для неизменяемых атрибутов (вздох, почему) и т. Д. В любом случае, теперь у вас есть вся информация, на которой можно основывать решения.

Ваш ответ на вопрос