Обнаружение типов параметров в семантическом действии Spirit
Общий случай: я не могу понять, почему мои действия по грамматике / семантике Spirit не компилируются.
Иногда компилятор будет жаловаться на несовместимость присваивания или типа, и я понятия не имею, что не так. Проблема возникает в двух основных областях:
- прогнозирование типа синтезированных атрибутов для правила / выражения
- следовательно, прогнозирование того, какие типы атрибутов могут быть юридически определены как открытый атрибут для правила (полагаясь на создание преобразований, адаптеров слияния или точек настройки Spirit)
- сопоставляя типы аргументов для моего семантического действия, чтобы
- компилятор сможет скомпилировать вызов функции
- вызов не вызовет ненужных неявных преобразований в процессе
Ошибка компилятора не совсем корректна, и либо документация неверна, либо я ее неправильно понял.
В любом случае, есть ли способ узнать, что именно передает Дух в мое семантическое действие?
Пример кода:
struct mybase { int a,b; };
struct myderived : mybase { int c,d; };
BOOST_FUSION_ADAPT_STRUCT(mybase, (int,a)(int,b));
BOOST_FUSION_ADAPT_STRUCT(myderived, (int,a)(int,b)(int,c)(int,d));
auto base_expr = int_ >> int_; // avoids assigning to struct attribute
rule<decltype(f), mybase() , space_type> base_ = int_ >> int_;
rule<decltype(f), myderived(), space_type> derived_ = base_ >> int_ >> int_;
myderived data;
bool ok = phrase_parse(f,l,derived_,space,data);
Этот код не будет компилироваться с огромным количеством непроницаемых ошибок.
2 ответа
Для наглядности - ошибка вот в чем base_ >> int_ >> int_
был использован в качестве выражения для правила, которое создает myderived
, и с тех пор base_
закреплен за типом mybase
мы должны были бы создать myderrived
из mybase
и два int
с, но нечего сказать Духу, как это сделать.
Вы можете получить повышение, чтобы распечатать тип значения, которое повышение создает при разборе base_ >> int_ >> int_
определив функтор, который будет принимать любые параметры и сообщать вам, что они есть (следующий код адаптирован из некоторого кода, который он помещает в SO чат):
struct what_is_the_attr
{
template <typename> struct result { typedef bool type; };
template <typename T>
static void print_the_type()
{
std::cout << " ";
std::cout << typeid(T).name();
if(std::is_const<typename std::remove_reference<T>::type>::value)
std::cout << " const";
if(std::is_rvalue_reference<T>::value)
std::cout << " &&";
else if(std::is_lvalue_reference<T>::value)
std::cout << " &";
}
template <typename Th, typename Th2, typename... Tt>
static void print_the_type()
{
print_the_type<Th>();
std::cout << ",\n";
print_the_type<Th2, Tt...>();
}
template <typename... Ts>
void operator()(Ts&&...) const
{
std::cout << "what_is_the_attr(\n";
print_the_type<Ts...>();
std::cout << ")" << std::endl;
}
};
Затем, чтобы использовать его, используйте вышеупомянутый субъект в семантическом действии инициализатора для вашего ошибочного правила:
std::string input = "1 2 3 4";
auto f(std::begin(input)), l(std::end(input));
rule<decltype(f), mybase() , space_type> base_ = int_ >> int_;
rule<decltype(f), myderived(), space_type> derived_ = (base_ >> int_ >> int_)[what_is_the_attr()];
myderived data;
bool ok = phrase_parse(f,l,derived_,space,data);
Обратите внимание, что вы не можете использовать автоматическое распространение атрибутов с %=
(если только вы не удалите открытый тип атрибута из объявленного типа правила).
Выполнение этого должно затем привести к закодированному типу, который может быть декодирован с c++filt -t
: Жить на Coliru
$ g++ 9404189.cpp -std=c++0x
$ ./a.out |c++filt -t
what_is_the_attr(
boost::fusion::vector3<mybase, int, int> &,
boost::spirit::context<boost::fusion::cons<boost::spirit::unused_type&, boost::fusion::nil>, boost::fusion::vector0<void> > &,
bool &)
Первая строка, boost::fusion::vector3<mybase, int, int>
, по крайней мере, говорит вам, что boost пытается создать ваш возвращаемый тип из 3 объектов типов mybase
, int
а также int
,
Я мог бы решить эту проблему для этого конкретного случая (на самом деле мы обсуждали варианты в списке), но на самом деле, эта "загадочная" ошибка появляется чаще с Boost Spirit, и было бы неплохо разобраться с общим классом проблем.
Ваш первый ресурс должен быть отличной рабочей документацией, которая точно описывает, каким будет синтезированный атрибут для данного примитива синтаксического анализатора, оператора или директивы. См. Справочный раздел для Духовных Ци Документов.
В некоторых случаях я перенес фокус с "попыток извлечения информации из списка ошибок компилятора" на "активный запрос к Spirit для типов, которые он передает". Для этого я использую метод Polymorphic Callable Type (см. Документацию Spirit/Fusion).
Вот тот, который использует специальные API GCC для печати [sic] типов, которые он обнаруживает:
Функтор what_is_the_attr
#include <cxxabi.h>
#include <stdlib.h>
#include <string>
#include <iostream>
template <typename T> std::string nameofType(const T& v) {
int status;
char *realname = abi::__cxa_demangle(typeid(v).name(), 0, 0, &status);
std::string name(realname? realname : "????");
free(realname);
return name;
}
struct what_is_the_attr {
template <typename> struct result { typedef bool type; };
template <typename T> bool operator()(T& attr) const {
std::cerr << "what_is_the_attr: " << nameofType(attr) << std::endl;
return true;
}
};
Пример использования: определить тип синтезированного атрибута
Вы можете использовать его для точного определения типа синтезированного атрибута выражения синтаксического анализатора:
template <typename Exp>
void detect_attr_type(const Exp& exp)
{
using namespace boost::spirit::qi;
const char input[] = "1 2 3 4";
auto f(std::begin(input)), l(std::end(input)-1);
bool dummy = phrase_parse(
f, l,
exp [ what_is_the_attr() ],
space);
}
(Примечание: это показывает ограничение подхода - метод предполагает, что у вас есть рабочая грамматика "иначе", и вы знаете, как передать ввод, который удовлетворяет выражению, достаточному для запуска семантического действия. В большинстве случаев это будет верно, когда Вы взламываете свой парсер Духа, хотя)
Давайте проверим это. Например, давайте посмотрим, в чем разница между выражением средней сложности и тем же самым qi::raw[]
директива:
int main()
{
detect_attr_type( -(int_ >> *int_) );
detect_attr_type( raw [ -(int_ >> *int_) ] );
}
Выход:
what_is_the_attr: boost::optional<boost::fusion::vector2<int, std::vector<int, std::allocator<int> > > >
what_is_the_attr: boost::iterator_range<char const*>
Внизу мы применим это к вопросу в ОП.
Пример использования: обнаружение типов, переданных в семантические действия
Мы могли бы использовать один и тот же унарный объект функции (what_is_the_attr
) чтобы обнаружить их, семантические действия могут принимать любое количество аргументов, поэтому нам нужно обобщить. Это была бы утомительная работа, если бы не шаблон с переменным числом символов (woot! Для C++0x):
struct what_are_the_arguments {
template <typename...> struct result { typedef bool type; };
template <typename... T> bool operator()(const T&... attr) const {
std::vector<std::string> names { nameofType(attr)... };
std::cerr << "what_are_the_arguments:\n\t";
std::copy(names.begin(), names.end(), std::ostream_iterator<std::string>(std::cerr, "\n\t"));
std::cerr << '\n';
return true;
}
};
Повторение вышеупомянутых тестовых примеров показывает, что Spirit фактически пытается вызвать семантическое действие с тремя аргументами, если это возможно (как задокументировано):
what_are_the_arguments:
boost::optional<boost::fusion::vector2<int, std::vector<int, std::allocator<int> > > >
boost::spirit::unused_type
bool
what_are_the_arguments:
boost::iterator_range<char const*>
boost::spirit::unused_type
bool
Но хорошо, что теперь вы можете применить это к любому семантическому действию:
template <typename ExpWSA> void test(const ExpWSA& exp)
{
const char input[] = "1 2 3 4";
auto f(std::begin(input)), l(std::end(input)-1);
qi::phrase_parse(f, l, exp, qi::space);
}
int main()
{
test(-(-double_ >> *int_) [ phx::bind(what_are_the_arguments(), _1, _2, _0, phx::ref(std::cout), 42) ]);
}
Печать, для этого (извините) очень надуманного примера:
what_are_the_arguments:
boost::optional<double>
std::vector<int, std::allocator<int> >
boost::fusion::vector2<boost::optional<double>, std::vector<int, std::allocator<int> > >
std::ostream
int
Применяется к ОП
Синтезированный атрибут derived
правило не такое, как для int_>>int_>>int_>>int_
:
auto base_expr = int_ >> int_; // avoids assigning to struct attribute
rule<const char*, mybase(), space_type> base_ = base_expr;
test(base_ >> int_ >> int_ [ what_is_the_attr() ] );
test(base_expr >> int_ >> int_ [ what_is_the_attr() ] );
Будет печатать
what_is_the_attr: boost::fusion::vector3<mybase, int, int>
what_is_the_attr: boost::fusion::vector4<int, int, int, int>
Есть твоя проблема. Мы обсудили некоторые обходные пути, основанные на этой диагностике, в оригинальной ветке (и посмотрите другие ответы здесь). Но этот пост должен помочь ответить на общий вопрос.
Полный список кодов
В интегрированной форме, скомпилированной с gcc 4.6.1 --std= C++ 0x и boost 1_48:
#include <cxxabi.h>
#include <iostream>
#include <iterator>
#include <stdlib.h>
#include <string>
#include <vector>
template <typename T> std::string nameofType(const T& v)
{
int status;
char *realname = abi::__cxa_demangle(typeid(v).name(), 0, 0, &status);
std::string name(realname? realname : "????");
free(realname);
return name;
}
struct what_is_the_attr {
template <typename> struct result { typedef bool type; };
template <typename T> bool operator()(T& attr) const {
std::cerr << "what_is_the_attr: " << nameofType(attr) << std::endl;
return true;
}
};
struct what_are_the_arguments {
template <typename...> struct result { typedef bool type; };
template <typename... T> bool operator()(const T&... attr) const {
std::vector<std::string> names { nameofType(attr)... };
std::cerr << "what_are_the_arguments:\n\t";
std::copy(names.begin(), names.end(), std::ostream_iterator<std::string>(std::cerr, "\n\t"));
std::cerr << '\n';
return true;
}
};
#include <boost/fusion/adapted.hpp>
#include <boost/spirit/include/phoenix.hpp>
#include <boost/spirit/include/qi.hpp>
struct mybase { int a,b; };
struct myderived : mybase { int c,d; };
BOOST_FUSION_ADAPT_STRUCT(mybase, (int,a)(int,b));
BOOST_FUSION_ADAPT_STRUCT(myderived, (int,a)(int,b)(int,c)(int,d));
template <typename ExpWSA>
void test(const ExpWSA& exp)
{
using namespace boost::spirit::qi;
const char input[] = "1 2 3 4";
auto f(std::begin(input)), l(std::end(input)-1);
bool dummy = phrase_parse(f, l, exp, space);
}
int main()
{
using namespace boost::spirit::qi;
// Diagnostics for the OP case
auto base_expr = int_ >> int_; // avoids assigning to struct attribute
rule<const char*, mybase(), space_type> base_ = base_expr;
// Derived rule, different formulations
test((base_ >> int_ >> int_) [ what_is_the_attr() ] );
test((base_expr >> int_ >> int_) [ what_is_the_attr() ] );
// Applied to attribute types
test(raw [ -(int_ >> *int_) ] [ what_is_the_attr() ] );
test(-(int_ >> *int_) [ what_is_the_attr() ] );
// applied to semantic actions - contrived example
namespace phx = boost::phoenix;
test(-(-double_ >> *int_) [ phx::bind(what_are_the_arguments(), _1, _2, _0, phx::ref(std::cout), 42) ]);
return 0;
}