提升精神业力 - 输出具有约束和传播失败的字符串向量?

时间:2013-10-17 08:27:19

标签: c++ boost boost-spirit 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
}

如果任何子规则不成功,是否有办法使规则失败?

2 个答案:

答案 0 :(得分:3)

我不知道您使用的是什么版本的提升,但是

  • 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不能很好地混合,因为子表达式中对临时表的陈旧引用

答案 1 :(得分:2)

这是一个可行的解决方案,它创建一个名为full的自定义指令(严重基于解释为herefull code的指令),只有当其主题返回true且数字返回时才返回true生成的元素数等于作为属性传递的容器中的元素数。

我所做的改变是:

  • columns_delimiter替换为element_counter_delimiter
  • simple_columns_generator替换为full_container_generator
  • columns替换为full
  • 删除了成员函数final_delimit_out
  • generateelement_counter_delimiter中略微修改full_container_generator
  • 添加adjust_size以说明%生成2*num_elem - 1次(n个整数和n-1个分号)的事实

Live Example

#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.HPP


int 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);
}