больше душевного безумия - парсер-типы (rules vs int_parser <>) и методы метапрограммирования - PullRequest
80 голосов
/ 19 мая 2011

Вопрос выделен жирным шрифтом внизу, проблема также обобщена фрагментом кода дистилляции ближе к концу.

Я пытаюсь объединить мою систему типов (система типов переходит от типа к строке и обратно) в один компонент (как определено Лакосом). Я использую boost::array, boost::variant и boost::mpl, чтобы добиться этого. Я хочу, чтобы правила парсера и генератора для моих типов были объединены в одном варианте. есть неопределенный тип, тип int4 (см. ниже) и тип int8. Вариант читается как variant<undefined, int4,int8>.

int4 черты:

struct rbl_int4_parser_rule_definition
{
  typedef boost::spirit::qi::rule<std::string::iterator, rbl_int4()> rule_type;

  boost::spirit::qi::int_parser<rbl_int4> parser_int32_t;

  rule_type rule;

  rbl_int4_parser_rule_definition()
  {
    rule.name("rbl int4 rule");
    rule = parser_int32_t;  
  }
};

template<>
struct rbl_type_parser_rule<rbl_int4>
{
  typedef rbl_int4_parser_rule_definition string_parser;
};

приведенный выше вариант начинается с неопределенного, а затем я инициализирую правила. У меня была проблема, которая вызвала 50 страниц ошибок, и мне наконец удалось отследить ее, Variant использует operator= во время назначения, а boost::spirit::qi::int_parser<> не может быть назначен другому (operator =).

Для сравнения, у меня нет проблем с моим неопределенным типом:

struct rbl_undefined_parser_rule_definition
{
  typedef boost::spirit::qi::rule<std::string::iterator, void()> rule_type;
  rule_type rule;

  rbl_undefined_parser_rule_definition()
  {
    rule.name("undefined parse rule");
    rule = boost::spirit::qi::eps;
  }
};

template<>
struct rbl_type_parser_rule<rbl_undefined>
{
  typedef rbl_undefined_parser_rule_definition string_parser;
};

Дистилляция проблемы:

#include <string>
#include <boost/spirit/include/qi.hpp>
#include <boost/variant.hpp>
#include <boost/cstdint.hpp>

typedef boost::spirit::qi::rule<std::string::iterator,void()> r1;
typedef boost::spirit::qi::rule<std::string::iterator,int()> r2;

typedef boost::variant<r1,r2> v;

int main()
{
  /*
  problematic
  boost::spirit::qi::int_parser<int32_t> t2;
  boost::spirit::qi::int_parser<int32_t> t1;


  t1 = t2;
  */

  //unproblematic
  r1 r1_;
  r2 r2_;
  r1_ = r2_;

  v v_;
  // THIS is what I need to do.
  v_ = r2();
}

Между конкретными парсерами и правилами существует смысловой разрыв. Мой мозг в настоящее время курит, поэтому я не собираюсь думать о праматизме, У меня вопрос, как мне решить эту проблему? Я могу придумать три подхода к решению проблемы.

one: Статические функции-члены:

struct rbl_int4_parser_rule_definition
{
  typedef boost::spirit::qi::rule<std::string::iterator, rbl_int4()> rule_type;

  //boost::spirit::qi::int_parser<rbl_int4> parser_int32_t;

  rule_type rule;

  rbl_int4_parser_rule_definition()
  {
    static boost::spirit::qi::int_parser<rbl_int4> parser_int32_t;

    rule.name("rbl int4 rule");
    rule = parser_int32_t;  
  }
};

Я полагаю, один подход предотвращает потокобезопасный код? ?

two: Интегральный синтаксический анализатор заключен в shared_ptr. Есть две причины, по которым я беспокоюсь о системе печати: 1 - эффективность, 2 - объединение проблем в компоненты. использование указателей побеждает первую причину.

three: operator = определяется как неиспользуемый. вариант гарантирует, что lhs по умолчанию создается перед присваиванием.

Edit: Я думаю, что вариант 3 имеет больше смысла (оператор = не работает). Как только контейнер правил создан, он не изменится, и я назначаю его только для принудительной установки черты правила типа в его смещение.

1 Ответ

11 голосов
/ 02 февраля 2012

Я не уверен, что понял весь вопрос, но вот несколько советов

  • Строка с комментариями // THIS is what I need to do. прекрасно компилируется со мной (проблема решена? Полагаю, вы действительно имели в виду назначение парсера, а не правила?)

  • Инициализация локальной функции static была определена как поточно-ориентированная в последнем стандарте (C ++ 11). Проверьте поддержку компилятором потоков C ++ 0x. (Если инициализатор выбрасывает, проход инструкции инициализации, кстати, попытается снова инициализировать).

  • правила alias()

    Как описано в http://boost -spirit.com / home / article / doc-addendum / faq / # aliases

    Вы можете создавать «логические копии» правил без необходимости фактически копировать прототипное выражение. Как часто задаваемые вопросы говорят, это в основном, чтобы позволить ленивое связывание

  • * * * * * * * * * * * * * * * * * * * * * * * * *

    , может быть, именно то, что вам нужно, в основном он лениво выбирает парсер для последующего разбора *1029*
    one = id;
    two = id >> ',' >> id;
    
    keyword.add
        ("one", &one)
        ("two", &two)
        ;
    
    start = *(keyword[_a = _1] >> lazy(*_a));
    

    В вашем контексте я мог видеть keyword, определенный как

    qi::symbols<char, qi::rule<Iterator>*> keyword;
    

    делает всю работу с атрибутами из семантических действий. С другой стороны,

    qi::symbols<char, qi::rule<Iterator, std::variant<std::string,int>() >*> keyword;
    
  • Приведите правила к тому же типу (как показано в предыдущей строке, в основном)

    В этой части я запутался: вы говорите, что хотите объединить систему типов. Возможно, нет необходимости в синтаксических анализаторах со строгим типом (отдельные сигнатуры атрибутов).

    typedef boost::variant<std::string,int> unified_type;
    typedef qi::rule<std::string::iterator, unified_type() > unified_rule;
    
    unified_rule rstring = +(qi::char_ - '.');
    unified_rule rint    = qi::int_;
    
    unified_rule combine = rstring | rint;
    
...