128-битная строка в массив с использованием boost :: spirit :: *

В настоящее время я начинаю с boost :: spirit :: *. Я пытаюсь разобрать 128-битную строку в простой массив c с соответствующим размером. Я создал короткий тест, который выполняет свою работу:

    boost::spirit::qi::int_parser< boost::uint8_t, 16, 2, 2 > uint8_hex;
    std::string src( "00112233445566778899aabbccddeeff" );
    boost::uint8_t dst[ 16 ];

    bool r;
    for( std::size_t i = 0; i < 16; ++i )
    {
        r = boost::spirit::qi::parse( src.begin( ) + 2 * i, src.begin( ) + 2 * i + 2, uint8_hex, dst[ i ] );
    }

У меня такое чувство, что это не самый умный способ сделать это :) Есть идеи, как определить правило, чтобы я мог избежать цикла?

Обновление:

Тем временем я придумал следующий код, который очень хорошо справляется со своей задачей:

    using namespace boost::spirit;
    using namespace boost::phoenix;

    qi::int_parser< boost::uint8_t, 16, 2, 2 > uint8_hex;

    std::string src( "00112233445566778899aabbccddeeff" );

    boost::uint8_t dst[ 16 ];
    std::size_t i = 0;

    bool r = qi::parse( src.begin( ),
                        src.end( ),
                        qi::repeat( 16 )[ uint8_hex[ ref( dst )[ ref( i )++ ] = qi::_1 ] ] );

person Mark    schedule 21.01.2015    source источник


Ответы (2)


Буквально не останавливаясь на вопросе, если вы действительно хотите просто проанализировать шестнадцатеричное представление 128-битного целого числа, вы можете сделать это переносимо, используя uint128_t, определенный в Boost Multiprecision:

qi::int_parser<uint128_t, 16, 16, 16> uint128_hex;

uint128_t parsed;
bool r = qi::parse(f, l, uint128_hex, parsed);

Это обязательно будет самый быстрый способ, особенно на платформах, где в наборе команд поддерживаются 128-битные типы.

Live On Coliru

#include <boost/multiprecision/cpp_int.hpp>
#include <boost/spirit/include/qi.hpp>

namespace qi  = boost::spirit::qi;

int main() {
    using boost::multiprecision::uint128_t;
    using It = std::string::const_iterator;
    qi::int_parser<uint128_t, 16, 16, 16> uint128_hex;

    std::string const src("00112233445566778899aabbccddeeff");
    auto f(src.begin()), l(src.end());

    uint128_t parsed;
    bool r = qi::parse(f, l, uint128_hex, parsed);

    if (r) std::cout << "Parse succeeded: " << std::hex << std::showbase << parsed << "\n";
    else   std::cout << "Parse failed at '" << std::string(f,l) << "'\n";

}
person sehe    schedule 21.01.2015

Есть печальная комбинация факторов, которые приводят к тому, что это болезненный крайний случай.

  • Boost Fusion может адаптировать (boost::)array<>, но для этого требуется, чтобы синтаксический анализатор приводил к кортежу элементов, а не к контейнеру.
  • Boost Fusion может адаптировать эти последовательности, но его необходимо настроить так, чтобы разрешить 16 элементов:

    #define FUSION_MAX_VECTOR_SIZE 16
    
  • Даже когда вы это делаете, директива парсера qi::repeat(n)[] ожидает, что атрибут будет типом контейнера.

Вы можете обойти все это некрасивым способом (например, Live On Coliru). Это усложняет работу в будущем.

Я бы предпочел здесь крошечное семантическое действие, чтобы результат был назначен из qi::repeat(n)[]:

    using data_t = boost::array<uint8_t, 16>;
    data_t dst {};

        qi::rule<It, data_t(), qi::locals<data_t::iterator> > rule = 
            qi::eps [ qi::_a = phx::begin(qi::_val) ]
            >> qi::repeat(16) [
                    uint8_hex [ *qi::_a++ = qi::_1 ]
            ];

Это работает без лишнего шума. Идея состоит в том, чтобы взять начальный итератор и записать каждый итератон в следующий элемент.

Live On Coliru

#include <boost/spirit/include/qi.hpp>
#include <boost/spirit/include/phoenix.hpp>

namespace qi  = boost::spirit::qi;
namespace phx = boost::phoenix;

int main() {
    using It = std::string::const_iterator;
    qi::int_parser<uint8_t, 16, 2, 2> uint8_hex;

    std::string const src("00112233445566778899aabbccddeeff");
    auto f(src.begin()), l(src.end());

    using data_t = boost::array<uint8_t, 16>;
    data_t dst {};

        qi::rule<It, data_t(), qi::locals<data_t::iterator> > rule = 
            qi::eps [ qi::_a = phx::begin(qi::_val) ]
            >> qi::repeat(16) [
                    uint8_hex [ *qi::_a++ = qi::_1 ]
            ];

    bool r = qi::parse(f, l, rule, dst);

    if (r) {
        std::cout << "Parse succeeded\n";

        for(unsigned i : dst) std::cout << std::hex << std::showbase << i << " ";
        std::cout << "\n";
    } else {
        std::cout << "Parse failed at '" << std::string(f,l) << "'\n";
    }
}
person sehe    schedule 21.01.2015
comment
Раньше я не видел твоего ответа. Спасибо за это. Тем временем я обновил свой вопрос и предложил другое решение. Поскольку ваше решение совершенно иное, я хотел бы спросить, в чем преимущество вашего решения или в чем мой недостаток? Спасибо. - person Mark; 22.01.2015
comment
Эмм. Какие? Вы только что сказали мне, что я зря потратил время, отвечая на этот вопрос, а теперь просите меня потратить больше времени на анализ вашего собственного кода, чтобы увидеть, в чем разница? (a) Мне трудно поверить, что вы не можете сделать это самостоятельно, поскольку вы явно способны решать более сложные задачи с помощью Spirit (b) Я не понимаю, почему вы, по-видимому, не знаете, в чем разница, но предполагаете что мое решение должно быть лучше? Может быть, у моего кода нет преимущества. Короче: все это не имеет смысла. - person sehe; 23.01.2015
comment
По теме: Похоже, ваше решение по сути то же самое. Я вижу три основных преимущества: A. в моем коде используется итератор, который (a1.) Делает семантическое действие более лаконичным (a2.), Делает код немного более универсальным (он по-прежнему работает для любого итератора вывода) , Б. он использует локальный, что упрощает создание (попробуйте сделать qi::phrase_parse(f, l, rule % ';', qi::space, dst) например) C. (субъективно :) может быть немного более читаемым, возможно, потому, что он предпочитает использование статической сантехники использованию посторонние движущиеся части (i и dst) - person sehe; 23.01.2015