Обнаружение типов параметров в семантическом действии Spirit - PullRequest
14 голосов
/ 23 февраля 2012

Общий случай: я не могу понять, почему мои действия по грамматике / семантике 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);

Этот код не будет компилироваться с огромным количеством непонятных ошибок.

( свободно адаптировано из публикации в spirit-general списке )

Ответы [ 2 ]

10 голосов
/ 23 февраля 2012

Я мог бы решить эту проблему для этого конкретного случая (на самом деле мы обсуждали варианты в списке ), но на самом деле, такая «загадочная» ошибка чаще появляется с Boost Spirit, и это будетнеплохо разобраться с общим классом проблем.

Ваш первый ресурс должен быть превосходной спиртовой документацией, которая точно описывает, каким будет синтезированный атрибут для данного примитива синтаксического анализатора, оператораили директива.См. справочный раздел до Spirit Qi Docs .

В некоторых случаях я перенес фокус с «попытки извлечь информацию из списка ошибок компилятора».«активно запрашивать у Духа тех типов, которые он передает».Для этого я использую метод 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;
    }
};

Повторяется для вышеуказанного тестаслучаи показывают, что Дух на самом деле пытается назвать семантическое действие с тремя аргументами , если это возможно (как задокументировано ):

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

Применительно к OP

СинтезированныйАтрибут правила 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;
}
6 голосов
/ 23 февраля 2012

Для ясности - ошибка здесь в том, что base_ >> int_ >> int_ использовалось в качестве выражения для правила, которое создает myderived, и, поскольку base_ имеет тип mybase, нам пришлось бы создать myderrived из mybase и двух int с, но Духу нечего сказать, как это сделать.

Вы можете получить boost, чтобы распечатать тип значения, которое boost создает при разборе base_ >> int_ >> int_, определив функтор, который будет принимать любые параметры и сообщать вам, что они есть (следующий код адаптирован из некоторого кода, приведенного ниже). в ТАКОМ чате):

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: Live On 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.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...