Помечать std:: функцию именем?


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

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, так что я могу неявно преобразовать функции, но это, кажется, имеет много gotchas в условия только преобразования вызываемых объектов в парсер.

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

1 ответ:

Вам не нужен raw typedef функции std; ваш синтаксический анализатор больше, чем любая функция 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".