Пометить std :: function именем? - PullRequest
0 голосов
/ 04 мая 2018

Я работаю над библиотекой комбинатора синтаксического анализатора, и мне бы очень хотелось, чтобы мой анализатор просто представлял собой вызываемый объект:

typedef std::function<parse_result(parse_stream)> parser;

Что делает синтаксические комбинаторы хорошими, например:

parser operator &(parser a, parser b) { return both(a,b); }

но Мне бы хотелось две функции:

1) Я бы хотел, чтобы строковые литералы автоматически повышались до парсера, чтобы вы могли делать такие вещи, как:

parser option = "<" & regexp("[^+>]+");

2) Мне бы хотелось, чтобы у парсера было имя, которое я могу использовать для форматирования ошибок. В случае парсера «оба», приведенного выше, я мог напечатать, что ожидал, например, a.name () и b.name ().

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

  1. класс парсера, который можно вызвать, это позволяет мне строить из строк и экземпляров std :: function , но общий вызываемый объект должен быть сначала преобразован в std :: function, а оттуда в синтаксический анализатор и C ++ не будут выполнять два неявных преобразования

  2. Наследование от std :: function, так что я могу неявно преобразовывать функции, но, похоже, в этом есть много недочетов с точки зрения только преобразования вызываемых в парсер.

У кого-нибудь есть мысли о том, как это структурировать?

1 Ответ

0 голосов
/ 04 мая 2018

Вам не нужен необработанный typedef функции std; Ваш парсер - это больше, чем любая стандартная функция.

struct parser: std::function<parse_result(parse_stream)>{
  using base = std::function<parse_result(parse_stream)>;
  using base::base;
};

это должно разрешить

parser p = []( parse_stream str ) { return parse_result(7); };

, так как мы используем наследующие конструкторы для представления необработанных std::function ctors в parser.

Пока вы можете переопределить:

parser operator&(parser a, parser b) { return both(a,b); }

с версией typedef, поместив & в пространство имен parse_result или parse_stream, я бы посоветовал против этого; в стандарте был чат, чтобы ограничить такого рода аргумент шаблона ADL. С голым типом parser место для таких перегрузок оператора очевидно.

Кроме того, некоторые типы не могут быть перегружены за пределами класса, например &=. С struct вы можете сделать это там.

Ничего из этого не исправлено

parser option = "<" & regexp("[^+>]+");

проблема в том, что правая сторона не имеет представления о том, что делает левая сторона (если regexp не является функцией, возвращающей парсер).

Сначала сделайте это:

struct parser: std::function<parse_result(parse_stream)>{
  using base = std::function<parse_result(parse_stream)>;
  parser( char const* str ):
    base( [str=std::string(str)](parse_stream stream)->parse_result { /* do work */ } )
  {}
  parser( char c ):
    base( [c](parse_stream str)->parse_result { /* do work */ } )
  {}
  using base::base;
};

тогда вы можете добавить

namespace parsing {
  // parser definition goes here
  inline namespace literals {
    inline parser operator""_p( char const* str ) { return str; }
  }
}

и using namespace parsing::literals означает, что "hello"_p - это анализатор, который пытается разобрать строку "hello".

...