Неоднозначный вариант и буст духа х3

Попытка настроить пример calc boost spirit x3 для разбора функций, которые могут принимать функции в качестве аргументов. Однако он не компилируется.

namespace client{ namespace ast{
    struct ts;
    struct fnc;
    typedef boost::variant<
    ts,
    boost::recursive_wrapper<fnc>
    > node;
    struct ts{
        unsigned int id;
    };
    struct fnc{
        std::vector<char> id;
        std::vector<node> args;
    };
}}
BOOST_FUSION_ADAPT_STRUCT(
    client::ast::ts,
    (unsigned int, id)
)
BOOST_FUSION_ADAPT_STRUCT(
    client::ast::fnc,
    (std::vector<char>, id)
    (std::vector<client::ast::node>, args)
)
namespace client{
    namespace x3 = boost::spirit::x3;
    namespace calc_grammar{
        using x3::uint_;
        using x3::alpha;
        using x3::alnum;
        using x3::lit;
        using x3::char_;
        x3::rule<class funct, ast::fnc> const funct("function");
        x3::rule<class ts, ast::ts> const ts("timeseries");
        x3::rule<class funct_name, std::vector<char>> const funct_name("function_name");
        auto const funct_def = funct_name >> lit('(') >> -((ts|funct)%lit(',')) >> lit(')');
        auto const ts_def = lit('#') >> uint_ >> lit('#');
        auto const funct_name_def = lit('@') >> alpha >> *(alnum|char_('_'));
        auto const calc = x3::grammar(
            "calc", 
            funct = funct_def, 
            ts = ts_def,
            funct_name = funct_name_def
        );
    }
    using calc_grammar::calc;
}

ошибка C2665: 'boost::detail::variant::make_initializer_node::apply::initializer_node::initialize': ни одна из 5 перегрузок не смогла преобразовать все типы аргументов

Также есть примечание для пользователя в variant.hpp.

// NOTE TO USER :
// Compile error here indicates that the given type is not 
// unambiguously convertible to one of the variant's types
// (or that no conversion exists).

Но я не умнее...


person user2515328    schedule 24.06.2013    source источник
comment
Пока вы не получите лучшего ответа, добавление конструктора по умолчанию, который ничего не делает, и конструктора, который принимает целое число без знака и сохраняет его в id в вашей структуре ts, кажется, заставляет его работать для меня с g++ 4.8.1.   -  person llonesmiz    schedule 24.06.2013
comment
@cv_and_he полностью согласен с анализом; @ user2515328 вы можете сообщить об этой проблеме в списке пользователей [spirit-general] — я думаю, что разработчики активно собирают отзывы (boost-spirit .com сейчас не работает)   -  person sehe    schedule 25.06.2013
comment
@cv_and_he спасибо за подсказки, но так и не исправил. используя mvsc, я думаю, я должен был поместить это в свой первоначальный пост.   -  person user2515328    schedule 25.06.2013
comment
@not-sehe, я попробую это, я поставил это здесь первым, потому что это казалось менее пугающим.   -  person user2515328    schedule 25.06.2013
comment
@ user2515328 Я согласен: ТАК мило. Но у boost-spirit-x3 на данный момент очень ограниченная аудитория, поэтому ваши шансы выше список (и ваши отзывы также будут доведены до заинтересованных разработчиков!). Ваше здоровье. (Кстати, я создал новый тег)   -  person sehe    schedule 25.06.2013
comment
MSVC не имеет большого значения: раскомментируйте два конструктора в этом примере, чтобы проверить мой быстрый тест: ideone.com/ZlWcMR (и diff: paste.ubuntu.com/5797692) (PS. с кторами раскомментировано, должно скомпилироваться)   -  person sehe    schedule 25.06.2013
comment
@not-sehe также не компилируется, независимо от уровня комментирования ctors. такая же ошибка.   -  person user2515328    schedule 25.06.2013
comment
@ user2515328 Вокай. Мне грустно это слышать. Я могу только предположить, что это будет из-за слабой поддержки багги-адиков в MSVC :( (я все равно сообщу об этом, возможно, это связано с ошибкой в ​​boost-svn's variant.hpp для MSVC)   -  person sehe    schedule 25.06.2013
comment
@not-sehe, да, я отправил письмо в Spirit-general и теперь скрещиваю пальцы. Спасибо!   -  person user2515328    schedule 25.06.2013
comment
@ user2515328 Я еще не видел сообщения. Вы сначала подписались (с адресом электронной почты, который хотели использовать)?   -  person sehe    schedule 25.06.2013
comment
@ not-sehe Я думал, что да, каким-то образом мне тоже удалось испортить это, я думаю ... попробовал еще раз сейчас.   -  person user2515328    schedule 25.06.2013


Ответы (1)


Я заметил этот старый вопрос. За это время X3 немного развился, и я все равно отвечу на него сейчас.

Я подозревал, что основная проблема могла быть связана с (отсутствующими) (неявными) конструкторами для вариантов членов.

Во всяком случае, вот живая демонстрация с более легкой грамматикой:

namespace grammar_def {
    using namespace x3;

    rule<class funct, ast::fnc> const funct("function");

    auto const ts        = lexeme [ '#' >> uint_ >> '#' ];
    auto const fname     = lexeme [ '@' >> raw [ alpha >> *(alnum | '_') ] ];
    auto const expr      = ts|funct;

    auto const funct_def = fname >> '(' >> -expr % ',' >> ')';

    BOOST_SPIRIT_DEFINE(funct)
}

Я также добавил несколько помощников потоковой передачи вывода. Обратите внимание, как я изменил тип id на std::string для простоты (сложно/невозможно перегрузить operator<< для vector<char>, не затрагивая namespace std):

namespace client { namespace ast {
    static std::ostream& operator<<(std::ostream& os, ts const& v) {
        using namespace boost::fusion;
        return os << tuple_open("") << tuple_close("") << tuple_delimiter("") << as_vector(v);
    }

    static std::ostream& operator<<(std::ostream& os, fnc const& v) {
        using namespace boost::fusion;
        return os << tuple_open("") << tuple_close("") << tuple_delimiter("") << as_vector(v);
    }
    template<typename T>
    static std::ostream& operator<<(std::ostream& os, std::vector<T> const& v) {
        os << "("; for (auto& el : v) os << (&el==&v[0]?"":", ") << el; return os << ")";
    }
} }

Демо

У этого есть больше (необязательно) сантехники, чтобы обеспечить более богатую отладочную информацию:

Жить на Coliru

//#define BOOST_SPIRIT_X3_DEBUG
#include <iostream>
#include <boost/fusion/include/adapt_struct.hpp>
#include <boost/fusion/include/as_vector.hpp>
#include <boost/fusion/include/io.hpp>
#include <boost/spirit/home/x3.hpp>

namespace client { namespace ast {
    struct ts;
    struct fnc;

    //using string = std::vector<char>;
    using string = std::string; // for easier printing/debugging

    struct ts {
        unsigned int id;
        ts(unsigned id=0):id(id) {}
    };

    typedef boost::variant<ts, boost::recursive_wrapper<fnc> > node;

    struct fnc {
        string id;
        std::vector<node> args;
    };
} }

BOOST_FUSION_ADAPT_STRUCT(client::ast::ts, id)
BOOST_FUSION_ADAPT_STRUCT(client::ast::fnc, id, args)

//namespace std { static ostream& operator<<(ostream&os, vector<char> const& v) { return os.write(&v[0], v.size()); } }

namespace client { namespace ast {
    static std::ostream& operator<<(std::ostream& os, ts const& v) {
        using namespace boost::fusion;
        return os << tuple_open("") << tuple_close("") << tuple_delimiter("") << as_vector(v);
    }

    static std::ostream& operator<<(std::ostream& os, fnc const& v) {
        using namespace boost::fusion;
        return os << tuple_open("") << tuple_close("") << tuple_delimiter("") << as_vector(v);
    }
    template<typename T>
    static std::ostream& operator<<(std::ostream& os, std::vector<T> const& v) {
        os << "("; for (auto& el : v) os << (&el==&v[0]?"":", ") << el; return os << ")";
    }
} }

namespace client {
    namespace x3 = boost::spirit::x3;
    namespace grammar_def {
        using namespace x3;

        x3::rule<class funct, ast::fnc> const funct("function");

        auto const ts     // = x3::rule<class ts, ast::ts> {"timeseries"}
                             = lexeme [ '#' >> uint_ >> '#' ];
        auto const fname  // = x3::rule<class fname, ast::string> {"function_name"}
                             = lexeme [ '@' >> raw [ alpha >> *(alnum | '_') ] ];
        auto const expr   // = rule<struct expr_, ast::node > {"expr"}
                             = ts|funct;

        auto const funct_def = fname >> '(' >> -expr % ',' >> ')';

        BOOST_SPIRIT_DEFINE(funct)
    }

    auto const& grammar = x3::skip(x3::space) [grammar_def::funct];
}

#include <iostream>

int main() {
    std::string const s {
        "@pow( #1#, \n"
            "     @trunc(\n"
            "           @pi ()\n"
            "   ) )"};
    std::cout << "Parsing '" << s << "'\n";

    auto f = s.begin();
    client::ast::fnc parsed;

    if (parse(f, s.end(), client::grammar, parsed)) {
        std::cout << "Parse succeeded: " << parsed << "\n";
    } else {
        std::cout << "Parse failed\n";
    }

    if (f != s.end())
        std::cout << "Remaining unparsed input: '" << std::string(f, s.end()) << "'\n";
}

Отпечатки:

Parsing '@pow( #1#, 
     @trunc(
           @pi ()
   ) )'
Parse succeeded: pow(1, trunc(pi()))
person sehe    schedule 04.01.2016