Boost Spirit Karma — Выходной вектор строк с ограничениями и распространением ошибок?

Я хочу вывести вектор строк, используя Boost Spirit Karma. Генерация вывода должна завершиться неудачей, если какая-либо из строк не удовлетворяет ограничениям. Я пробовал следующее:

#include <boost/spirit/include/karma.hpp>

namespace ka = boost::spirit::karma;

int main()
{
typedef std::ostream_iterator<char> iterator_t;

std::string is1{"123"}, is2{"def"};
std::vector<std::string> iv1{"123", "456"}, iv2{"123","def"};

auto num = +ka::char_("0-9");
auto nums = num % ka::lit(";");

assert(ka::generate(iterator_t{std::cout}, num << ka::eol, is1) == true);
assert(ka::generate(iterator_t{std::cout}, num << ka::eol, is2) == false);

assert(ka::generate(iterator_t{std::cout}, nums << ka::eol, iv1) == true);
assert(ka::generate(iterator_t{std::cout}, nums << ka::eol, iv2) == false); // Assertion Fails
}

Есть ли способ заставить правило потерпеть неудачу, если какое-либо из подчиненных правил не выполнено?

1

Решение

Вот одно из возможных решений, которое создает пользовательскую директиву (в значительной степени основанную на Вот, полный код) называется full он возвращает true только тогда, когда его субъект возвращает true, а количество сгенерированных элементов равно количеству элементов в контейнере, переданном как атрибут.

Изменения, которые я сделал:

  • заменены columns_delimiter с element_counter_delimiter,
  • заменены simple_columns_generator с full_container_generator,
  • заменены columns с full,
  • удалил функцию-член final_delimit_out,
  • слегка изменены generate в element_counter_delimiter а также full_container_generator,
  • добавленной adjust_size для того, чтобы объяснить тот факт, что % генерирует 2*num_elem - 1 раз (n целых и n-1 точка с запятой)

Живой пример

#include <iostream>
#include <boost/spirit/include/karma.hpp>

//START OF FULL.HPP
#include <boost/spirit/include/karma_generate.hpp>

///////////////////////////////////////////////////////////////////////////////
// definition the place holder
namespace custom_generator
{
BOOST_SPIRIT_TERMINAL(full);
}

///////////////////////////////////////////////////////////////////////////////
// implementation the enabler
namespace boost { namespace spirit
{
// We want custom_generator::full to be usable as a directive only,
// and only for generator expressions (karma::domain).
template <>
struct use_directive<karma::domain, custom_generator::tag::full>
: mpl::true_ {};
}}

///////////////////////////////////////////////////////////////////////////////
// implementation of the generator
namespace custom_generator
{
template <typename T>
struct adjust_size
{
static std::size_t call(std::size_t val)
{
return val; //with kleene and repeat just return the value
}
};

template <typename Left, typename Right>
struct adjust_size<boost::spirit::karma::list<Left,Right> >
{
static std::size_t call(std::size_t val)
{
return (val+1)/2; //with list you output n elements and n-1 semicolons
}
};
// special delimiter wrapping the original one that counts the number of elements
template <typename Delimiter>
struct element_counter_delimiter
{
element_counter_delimiter(Delimiter const& delim)
: delimiter(delim), count(0) {}

// This function is called during the actual delimiter output
template <typename OutputIterator, typename Context
, typename Delimiter_, typename Attribute>
bool generate(OutputIterator& sink, Context&, Delimiter_ const&
, Attribute const&) const
{
// first invoke the wrapped delimiter
if (!boost::spirit::karma::delimit_out(sink, delimiter))
return false;

// now we count the number of invocations
++count;

return true;
}

Delimiter const& delimiter;   // wrapped delimiter
mutable unsigned int count;   // invocation counter
};

// That's the actual full generator
template <typename Subject>
struct full_container_generator
: boost::spirit::karma::unary_generator<
full_container_generator<Subject> >
{
// Define required output iterator properties
typedef typename Subject::properties properties;

// Define the attribute type exposed by this parser component
template <typename Context, typename Iterator>
struct attribute
: boost::spirit::traits::attribute_of<Subject, Context, Iterator>
{};

full_container_generator(Subject const& s)
: subject(s)
{}

// This function is called during the actual output generation process.
// It dispatches to the embedded generator while supplying a new
// delimiter to use, wrapping the outer delimiter.
template <typename OutputIterator, typename Context
, typename Delimiter, typename Attribute>
bool generate(OutputIterator& sink, Context& ctx
, Delimiter const& delimiter, Attribute const& attr) const
{
std::size_t elems_in_container = boost::spirit::traits::size(attr);
element_counter_delimiter<Delimiter> d(delimiter);
if (!subject.generate(sink, ctx, d, attr))
return false;
return elems_in_container == adjust_size<Subject>::call(d.count);
}

// This function is called during error handling to create
// a human readable string for the error context.
template <typename Context>
boost::spirit::info what(Context& ctx) const
{
return boost::spirit::info("full", subject.what(ctx));
}

Subject subject;
};
}

///////////////////////////////////////////////////////////////////////////////
// instantiation of the generator
namespace boost { namespace spirit { namespace karma
{
// This is the factory function object invoked in order to create
// an instance of our full_container_generator.
template <typename Subject, typename Modifiers>
struct make_directive<custom_generator::tag::full, Subject, Modifiers>
{
typedef custom_generator::full_container_generator<Subject> result_type;

result_type operator()(unused_type, Subject const& s, unused_type) const
{
return result_type(s);
}
};
}}}

//END OF FULL.HPPint main()
{
typedef std::ostream_iterator<char> iterator_t;
namespace ka=boost::spirit::karma;

std::string is1{"123"}, is2{"def"};
std::vector<std::string> iv1{"123", "456"}, iv2{"123","def"}, iv3{"123", "456", "789"}, iv4{"123", "456", "def"};

using custom_generator::full;

ka::rule<iterator_t,std::string()> num = +ka::char_("0-9"); //this rule needs to have attribute std::string
//that wasn't the case with the original "auto num =..."//and it caused that the delimiter count went way higher than it should
ka::rule<iterator_t,std::vector<std::string>()> nums = full[num%ka::lit(";")];assert(ka::generate(iterator_t{std::cout}, num << ka::eol, is1) == true);
assert(ka::generate(iterator_t{std::cout}, num << ka::eol, is2) == false);

assert(ka::generate(iterator_t{std::cout}, nums << ka::eol, iv1) == true);
assert(ka::generate(iterator_t{std::cout}, ka::buffer[nums << ka::eol], iv2) == false); //using buffer as mentioned by sehe
assert(ka::generate(iterator_t{std::cout}, nums << ka::eol, iv3) == true);
assert(ka::generate(iterator_t{std::cout}, ka::buffer[nums << ka::eol], iv4) == false);
}
2

Другие решения

Я не знаю, какую версию Boost вы используете, но

  • 1_42_0 не компилирует ваш фрагмент
  • 1_49_0 не выходит из строя и печатает:

    123
    123;456
    
  • 1_54_0 не выходит из строя и печатает:

    123
    123;456
    

Итак, я не могу воспроизвести проблему. Тем не менее, концептуально, я думаю, что вы ищете karma::buffer[]:

Директива генератора для буферизации временного вывода (buffer[])

Все компоненты генератора (кроме генератора Alternative (|)) передают свои сгенерированные выходные данные непосредственно в базовый выходной поток. Если генератор выходит из строя на полпути, сгенерированный до сих пор результат не «откатился». Директива генератора буферизации позволяет избежать генерирования этого нежелательного вывода. Он временно перенаправляет выходные данные встроенного генератора в буфер. Этот буфер сбрасывается в основной поток только после успешного выполнения встроенного генератора, но отбрасывается иначе.

Чтобы вы могли добавить

ka::rule<iterator_t, std::string()>              num  = +ka::char_("0-9");
ka::rule<iterator_t, std::vector<std::string>()> nums = ka::buffer [ num % ka::lit(";") ];

Заметка Я не исключаю, что вы смотрели на неопределенное поведение, потому что деревья выражений Proto плохо сочетаются с auto из-за устаревших ссылок на временные выражения в подвыражениях.

3