більше духовного божевілля - типи парсерів (правила проти int_parser <>) та методи метапрограмування


80

Питання напівжирним шрифтом внизу, проблема також узагальнена фрагментом коду перегонки до кінця.

Я намагаюся об'єднати мою систему типів (система типів робить це і від типу до рядка) в один компонент (як визначено Лакосом). Я використовую 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=під час призначення, а a 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;  
  }
};

Я думаю, підхід один запобігає потокобезпечний код? ?

друге: цілісний синтаксичний аналізатор обертається у shared_ptr. Є дві причини, за якими я турбуюся про TMP для системи друку: 1 ефективність, 2 централізація проблем на компоненти. використання покажчиків перемагає першу причину.

три: оператор = визначається як не-операція. variant гарантує, що значення за lhsзамовчуванням будується перед призначенням.

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


1
варіант 1 є небезпечним для потоку, лише якщо: parser_int32_tмає стан і береться посилання. Якщо без громадянства або зроблена копія, це безпечно. З семантики я б сказав, що зроблена копія.
Matthieu M.

Це досить заплутане занепокоєння, я не можу бути впевненим, що об’єкт аналізатора не має стану. Крім того, існують посилальна та конкретна семантика з правилом механіка - тобто правило може містити посилання на інші правила, але вони також можуть бути самими конкретними синтаксичними аналізаторами (я думаю), і я не знаю, як ця семантика застосовується до конкретних парсерів .
Хасан Сайєд,

@MatthieuM: Правильно, копія робиться, якщо .alias()не використовується.
ildjarn

@ildjarn, але правило - це не конкретний аналізатор: D вміст правила - це вираз, еквівалент дерева синтаксичного аналізу.
Хасан Сайєд,

1
Я не можу оцінити, чи буде №1 безпечним для потоків чи ні, але можу дати унцію порад, про які легко забути. Статичне призначення компілятор оцінює лише раз. Уявіть собі невелику перевірку в коді (if (! Evaluated_yet) evaluate () else noop ()). перший раз, коли будь-який відповідний об’єкт-член rbl_int4_parser_rule_definition буде викликаний де завгодно, він буде побудований один раз. це майже абсолютно еквівалентно використанню глобального сингтона. чи не могли б ви використати глобальний синглтон цього типу для вирішення тієї ж проблеми? (ігноруючи початковий порядок тощо), якщо так, це повинно бути безпечним для потоків.
std''OrgnlDave

Відповіді:


11

Я не настільки впевнений, що отримую повний обсяг запитання, але тут є кілька підказок

  • Рядок прокоментував зі // THIS is what I need to do.мною компіляції (проблема вирішена? Я думаю, ви насправді мали на увазі призначення парсера, а не правила?)

  • Ініціалізація function-local staticбула визначена безпечною для потоків в останньому стандарті (C ++ 11). Перевірте підтримку компілятора для різьбової роботи C ++ 0x. (Якщо ініціалізатор кине, прохід оператора ініціалізації, до речі, спробує ініціалізувати знову).

  • правила alias()

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

    Ви можете створювати "логічні копії" правил без необхідності фактично копіювати значення виразу прото. Як зазначається у поширених запитаннях, це головним чином для того, щоб дозволити ліниві зв'язки

  • Trick Nabialek може бути саме те , що вам потрібно, в основному це ліниво вибирає аналізатор для подальшого розбору

    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;
    
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.