Помечать 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() например.
Два варианта, которые я пробовал до сих пор, это
-
Класс синтаксического анализатора, который можно вызвать, это позволяет мне строить из строк и экземпляров std:: function , но общий вызываемый объект сначала должен быть преобразован в std:: function, а оттуда в синтаксический анализатор, и C++ не будет выполнять два неявных преобразования
-
Наследование от std:: function, так что я могу неявно преобразовать функции, но это, кажется, имеет много gotchas в условия только преобразования вызываемых объектов в парсер.
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"
.