повторить к std :: tuple с известным N во время компиляции

c++ boost-spirit-x3

416 просмотра

1 ответ

я хочу проанализировать во время компиляции указанное количество элементов. Я попробовал repeat()[]директиву. Следующий код показывает мой случай:

 using namespace x3;
 std::tuple<float, float, float> tup;
 std::string str{"0.3 0.2 0.1"};
 auto ret = parse(std::begin(str), std::end(str), repeat(3)[ float_ >> (' ' | eol) ] , tup); 

Сообщение об ошибке компилятора:

error: static assertion failed: Expecting a single element fusion sequence
             static_assert(traits::has_size<Attribute, 1>::value

Это работает, если бы я написал это:

parse(std::begin(str), std::end(str), float_ >> ' ' >> float_ >> ' ' >> float_ ] , tup);

но с большим количеством элементов это путаница.

Есть ли способ сократить грамматику с помощью директивы repeat ?

Автор: Roby Источник Размещён: 08.11.2019 10:55

Ответы (1)


0 плюса

Решение

Как вы можете видеть здесь, синтезированный атрибут x3::repeat(3)[x3::float_]- это a vector<float>, который не соответствует вашему атрибуту (в основном это последовательность слияния размера 3). Обратите внимание, что синтезированный атрибут не зависит от передаваемого вами значения.

Чтобы получить то, что вам нужно, вам понадобится еще одна директива, тип которой зависит от значения, которое вы передаете. Эта директива затем генерирует последовательность, в которой ее тема повторяется N раз (просто «делегирование» работы x3::sequence, чтобы убедиться, что все работает правильно в отношении распространения атрибутов). Я могу придумать, по крайней мере, два способа, которыми это могло бы работать: что-то вроде repeat<N>[parser]или что-то подобное repeat(integral_constant<int,N>)[parser]. В приведенном ниже коде я выбрал второй подход boost::hana::integral_constant, который позволяет вам использовать:

custom::repeat(3_c)[ x3::float_ >> (' ' | x3::eol | x3::eoi) ]

Полный код (работает на WandBox)

custom_repeat.hpp

#include <type_traits>
#include <boost/spirit/home/x3.hpp> 

namespace custom
{
    struct repeat_gen
    {
        template <int Size>
        struct repeat_gen_lvl1
        {

            //using overloads with integral constants to avoid needing to partially specialize a function

            //this actually builds the sequence of parsers
            template <typename Parser,int N>
            auto generate_sequence(Parser const& parser, std::integral_constant<int,N>) const
            {
                return generate_sequence(parser,std::integral_constant<int,N-1>{}) >> parser;
            }

            template <typename Parser>
            auto generate_sequence(Parser const parser,std::integral_constant<int,1>) const
            {
                return parser;
            }

            template<typename Subject>
            auto operator[](Subject const& subject) const
            {
                //here the actual sequence is generated
                return generate_sequence(boost::spirit::x3::as_parser(subject), std::integral_constant<int,Size>{});
            }
        };

        template <typename IntConstant>
        repeat_gen_lvl1<int(IntConstant::value)>
        operator()(IntConstant) const
        {
            //returns an object that know the size of the wanted sequence and has an operator[] that will capture the subject
            return {};
        }

        template <typename IntegerType, typename Enable=std::enable_if_t<std::is_integral<IntegerType>::value> >
        auto operator()(IntegerType n) const
        {
            return boost::spirit::x3::repeat(n);
        }
    };

    //this object's only purpose is having an operator()
    auto const repeat = repeat_gen{};
}

main.cpp

#include <iostream>

#include <boost/spirit/home/x3.hpp>

#include <boost/fusion/include/std_tuple.hpp>
#include <boost/fusion/include/std_pair.hpp>
#include <boost/fusion/include/adapt_struct.hpp>
#include <boost/fusion/include/io.hpp>
#include <boost/fusion/include/as_vector.hpp>
#include <boost/fusion/include/is_sequence.hpp>

#include <boost/hana/integral_constant.hpp>

#include <boost/mpl/int.hpp>

#include "custom_repeat.hpp"

namespace x3 = boost::spirit::x3;

using namespace boost::hana::literals;

template <typename T>
void print_attr(const std::vector<T>& vec)
{
    std::cout << "Vector: ";
    for(const auto& elem : vec)
        std::cout << "[" << elem << "]";
}

template <typename Sequence, typename Enable=std::enable_if_t<boost::fusion::traits::is_sequence<Sequence>::value> >
void print_attr(const Sequence& seq)
{
    std::cout << "Sequence: " << boost::fusion::as_vector(seq);
}

template <typename Attr,typename Parser>
void parse(const std::string& str, const Parser& parser)
{
    Attr attr;
    std::string::const_iterator iter = std::begin(str), end = std::end(str);
    bool ret = x3::parse(iter, end, parser, attr);

    if(ret && (iter==end))
    {
        std::cout << "Success.\n";
        print_attr(attr);
        std::cout << std::endl;
    }
    else
    {
        std::cout << "Something failed. Unparsed: ->|" << std::string(iter,end) << "|<-" << std::endl;
    }
}

struct float_holder
{
    float val;
};

BOOST_FUSION_ADAPT_STRUCT(float_holder,val);

int main()
{
    boost::mpl::int_<2> two;
    std::integral_constant<int,1> one;

    parse<std::tuple<float,float,float> >("0.3 0.2 0.1", custom::repeat(3_c)[ x3::float_ >> (' ' | x3::eol | x3::eoi) ] );
    parse<std::pair<float,float> >("0.2 0.1", custom::repeat(two)[ x3::float_ >> (' ' | x3::eol | x3::eoi) ] );
    parse<float_holder>("0.1", custom::repeat(one)[ x3::float_ >> (' ' | x3::eol | x3::eoi) ] );

    parse<std::vector<float> >("0.3 0.2 0.1", custom::repeat(3)[ x3::float_ >> (' ' | x3::eol | x3::eoi) ] );

}
Автор: llonesmiz Размещён: 20.08.2016 02:54
Вопросы из категории :
32x32