代码之家  ›  专栏  ›  技术社区  ›  Addy

Boost Karma:使用Boost::optional编写规则

  •  3
  • Addy  · 技术社区  · 7 年前

    我正在尝试使用 boost::variant boost::optional 在业力产生器中。我已经能够将问题简化为:

    using FooVariant = boost::variant<int>;
    using FooOptional = boost::optional<FooVariant>;
    
    template<typename OutputIt = boost::spirit::ostream_iterator>
    struct FooGenerator
        : boost::spirit::karma::grammar<OutputIt, FooOptional()>
    {
        FooGenerator()
            : FooGenerator::base_type(start_)
        {
            namespace bsk = boost::spirit::karma;
            start_ = '[' << ( bsk::int_ | '*' ) << ']';
        }
    
        boost::spirit::karma::rule<OutputIt, FooOptional()> start_;
    };
    
    int main()
    {
        FooOptional fop1;
        std::cout << boost::spirit::karma::format(FooGenerator<>(), fop1) << std::endl;
    
        FooOptional fop2 = FooVariant{123};
        std::cout << boost::spirit::karma::format(FooGenerator<>(), fop2) << std::endl;
    }
    

    此处的输出为:

    [*]
    [*]
    

    这就是 我所希望的。

    我改变的第一件事或多或少是通过改变 FooVariant 收件人: using FooVariant = int; . 这将输出:

    [*]
    [123]
    

    这就是我想看到的!所以在我的第一段代码中,变体只有一种类型,所以我尝试添加第二种类型,只是为了看看:

    using FooVariant = boost::variant<int, double>;
    ...
    start_ = '[' << ( ( bsk::int_ | bsk::double_ ) | '*' ) << ']';
    

    但接下来我们又回到:

    [*]
    [*]
    

    然后,我尝试为变体添加一条专门的规则:

    using FooVariant = boost::variant<int, double>;
    using FooOptional = boost::optional<FooVariant>;
    
    template<typename OutputIt = boost::spirit::ostream_iterator>
    struct FooGenerator
        : boost::spirit::karma::grammar<OutputIt, FooOptional()>
    {
        FooGenerator()
            : FooGenerator::base_type(start_)
        {
            namespace bsk = boost::spirit::karma;
            foovar_ = (bsk::int_ | bsk::double_);
            start_ = '[' << ( foovar_ | '*' ) << ']';
        }
    
        boost::spirit::karma::rule<OutputIt, FooVariant()> foovar_;
        boost::spirit::karma::rule<OutputIt, FooOptional()> start_;
    };
    
    int main()
    {
        FooOptional fop1;
        std::cout << boost::spirit::karma::format(FooGenerator<>(), fop1) << std::endl;
    
        FooOptional fop2 = FooVariant{123};
        std::cout << boost::spirit::karma::format(FooGenerator<>(), fop2) << std::endl;
    }
    

    这会导致编译错误:

    alternative_function.hpp:127:34: error: no member named 'is_compatible' in
          'boost::spirit::traits::compute_compatible_component<boost::variant<int, double>, boost::optional<boost::variant<int, double> >, boost::spirit::karma::domain>'
                if (!component_type::is_compatible(spirit::traits::which(attr_)))
                     ~~~~~~~~~~~~~~~~^
    

    看起来模板生成正在尝试确保 boost::变量 boost::可选 但对我来说,问题是“为什么要确保它们完全兼容?”

    我怎样才能做到这一点?

    1 回复  |  直到 7 年前
        1
  •  4
  •   sehe    7 年前

    我在另一个答案中告诉过你我会如何处理这个问题: Boost Karma: generate default text when boost::optional is unset

    这不仅避免了这个问题,还简化了AST/数据类型。

    现在,既然你强迫我,我就坚持下去。问题产生于以下事实: foovar_ | '|' 是一个 替代表达式 它以某种方式验证了属性必须是兼容的。在检查期间,假设

    • 如果 a 具有属性 attr(a) b 具有属性 attr(b) 然后
    • a | b 应该有 variant<attr(a), attr(b)

    它会进行一些逻辑检查(比如 attr(a) == attr(b) )但不是逻辑检查如果 属性(b) unused_type 那么它应该与 optional<attr(a)> 太多了,而不仅仅是 variant<attr(a), unused_type> 甚至 variant<attr(a)> .

    现在,为什么我没那么感兴趣。所以,你可以这样做 武力 东西。

        foovar_ = bsk::int_ | bsk::double_;
        fooopt_ = (bsk::eps(is_initialized_(bsk::_val)) | '*') << -foovar_;
        start_ = '[' << fooopt_ << ']';
    

    这个 钥匙 将其编码为 替代表达式 . 在这里,我们只是信任 -foovar_ 说点什么,还是不说。此外,我们也

    bsk::eps(is_initialized_(bsk::_val)) | '*'
    

    含义:如果变量已初始化,则仅此而已,否则,生成 '*' .

    现在,您不需要严格遵守第三条规则,如果您喜欢只写代码,您可以只写:

        start_ = '[' << (bsk::eps(is_initialized_(bsk::_val)) | '*') << -foovar_ << ']';
    

    演示

    哦,我差点忘了:

    struct is_initialized_f {
        template<typename T>
        bool operator()(boost::optional<T> const& opt) const { return opt.is_initialized(); }
    };
    boost::phoenix::function<is_initialized_f> is_initialized_;
    

    实现帮助程序Phoenix actor以检查初始化状态。

    Live On Coliru

    #include <boost/spirit/include/karma.hpp>
    #include <boost/spirit/include/phoenix.hpp>
    using FooVariant = boost::variant<int, double>;
    using FooOptional = boost::optional<FooVariant>;
    
    template<typename OutputIt = boost::spirit::ostream_iterator>
    struct FooGenerator
        : boost::spirit::karma::grammar<OutputIt, FooOptional()>
    {
        FooGenerator()
            : FooGenerator::base_type(start_)
        {
            namespace bsk = boost::spirit::karma;
            namespace phx = boost::phoenix;
    
            foovar_ = bsk::int_ | bsk::double_;
            //fooopt_ = (bsk::eps(is_initialized_(bsk::_val)) | '*') << -foovar_;
            start_ = '[' << (bsk::eps(is_initialized_(bsk::_val)) | '*') << -foovar_ << ']';
        }
    
      private:
        struct is_initialized_f {
            template<typename T>
            bool operator()(boost::optional<T> const& opt) const { return opt.is_initialized(); }
        };
        boost::phoenix::function<is_initialized_f> is_initialized_;
        boost::spirit::karma::rule<OutputIt, FooVariant()> foovar_;
      //boost::spirit::karma::rule<OutputIt, FooOptional()> fooopt_;
        boost::spirit::karma::rule<OutputIt, FooOptional()> start_;
    };
    
    int main()
    {
        for (FooOptional fop : { FooOptional{}, {FooVariant{123}}, {FooVariant{3.14}} }) {
            if (std::cout << boost::spirit::karma::format(FooGenerator<>(), fop))
                std::cout << "\n";
            else
            {
                std::cout.clear();
                std::cout << "#Error\n";
            }
        }
    }
    

    印刷品

    [*]
    [123]
    [3.14]
    

    ¹笑话

    我们会得到与“某人忘记了”或“这是一种回归”押韵的非权威答案

    推荐文章