Boost Karma,重新排列结构体元素

3

假设我有一个这样的结构体:

struct MyStruct
{
    int a;
    int b; 
    int c;
}

BOOST_FUSION_ADAPT_STRUCT
(
    MyStruct,
    (int, a)
    (int, b)
    (int, c)
)

假设我有一个简单的生成器:

struct MyStructGenerator
    : boost::spirit::karma::grammar<boost::spirit::ostream_iterator, MyStruct()>
{
    MyStructGenerator() : MyStructGenerator::base_type(start_)
    {

        namespace bsk = boost::spirit::karma;

        start_ = '<' 
            << bsk::int_
            << ','
            << bsk::int_
            << ','
            << bsk::int_
            << '>';
    }

    ~MyStructGenerator() = default;
    boost::spirit::karma::rule<boost::spirit::ostream_iterator, MyStruct()> start_;
};

我运行了以下代码:

int main()
{
    MyStruct ms = { 3, 2, 1 };
    std::cout << boost::spirit::karma::format(MyStructGenerator(), ms) << std::endl;
}

我当然希望看到<3, 2, 1>。但我无法弄清如何在规则内更改顺序?如果我想要看到<1, 2, 3>,甚至如果我想要看到<2, 1, 3>怎么办?
此外,有没有什么方法可以不用BOOST_FUSION_ADAPT_STRUCT来实现这一点?
2个回答

3

问题1 如果我想要看到<1, 2, 3>,或者甚至想看到<2, 1, 3>怎么办?

只需更改适应的顺序:

简化版的 Wandbox 实时演示

#include <boost/fusion/adapted/struct.hpp>

struct MyStruct { int a, b, c; };
BOOST_FUSION_ADAPT_STRUCT(MyStruct, a, b, c)

#include <boost/spirit/include/karma.hpp>
namespace bsk = boost::spirit::karma;

template <typename It = boost::spirit::ostream_iterator>
struct MyGen : bsk::grammar<It, MyStruct()> {
    MyGen() : MyGen::base_type(start_) {
        using namespace bsk;
        start_ = '<' << int_ << ',' << int_ << ',' << int_ << '>';
    }
  private:
    bsk::rule<It, MyStruct()> start_;
};

int main() {
    MyGen<> gen;
    std::cout << format(gen, MyStruct { 3, 2, 1 }) << "\n";
}

打印出<3,2,1>,但是
BOOST_FUSION_ADAPT_STRUCT(MyStruct, c, b, a)

打印 <1,2,3>

没有适应,怎样做?

好的。我可以给你展示一些可能会对你有兴趣的东西:

  1. without Karma: Live On Wandbox:

    #include <boost/fusion/adapted/struct.hpp>
    
    struct MyStruct { int a, b, c; };
    BOOST_FUSION_ADAPT_STRUCT(MyStruct, c, b, a)
    
    #include <iostream>
    #include <boost/fusion/include/io.hpp>
    #include <boost/fusion/include/as_vector.hpp>
    using boost::fusion::as_vector;
    
    int main() {
        MyStruct ms { 3, 2, 1 };
        std::cout << as_vector(ms) << "\n";
    
        std::cout 
            << boost::fusion::tuple_open("<")
            << boost::fusion::tuple_delimiter(",")
            << boost::fusion::tuple_close(">");
    
        std::cout << as_vector(ms) << "\n";
    }
    

    Prints

    (1 2 3)
    <1,2,3>
    
  2. Named adaptations: you can adapt different orders at the same time by using the *_NAMED macros. Here's a demo that shows it both using Fusion IO and the Karma generator.

    Note I slightly modified the struct so it's easier to track which field is 'a', 'b' or 'c'.

    See it Live On Wandbox:

    #include <boost/fusion/adapted/struct.hpp>
    
    struct MyStruct { char a, b, c; };
    BOOST_FUSION_ADAPT_STRUCT_NAMED(MyStruct, AsABC, a, b, c)
    BOOST_FUSION_ADAPT_STRUCT_NAMED(MyStruct, AsBCA, b, c, a)
    BOOST_FUSION_ADAPT_STRUCT_NAMED(MyStruct, AsCBA, c, b, a)
    
    #include <boost/spirit/include/karma.hpp>
    #include <boost/spirit/include/qi.hpp>
    namespace bsk = boost::spirit::karma;
    
    template <typename Attr, typename It = boost::spirit::ostream_iterator>
    struct MyGen : bsk::grammar<It, Attr()> {
        MyGen() : MyGen::base_type(start_) {
            using namespace bsk;
            start_ = '<' << auto_ << ',' << auto_ << ',' << auto_ << '>';
        }
      private:
        bsk::rule<It, Attr()> start_;
    };
    
    #include <iostream>
    #include <boost/fusion/include/io.hpp>
    #include <boost/fusion/include/as_vector.hpp>
    using boost::fusion::as_vector;
    
    template <typename Attr>
    void do_tests(Attr const& ms) {
        std::cout << as_vector(ms) << "\n";
        std::cout << format(MyGen<Attr>{}, ms) << "\n";
    }
    
    int main() {
        std::cout << boost::fusion::tuple_open("<") << boost::fusion::tuple_delimiter(",") << boost::fusion::tuple_close(">");
    
        MyStruct ms { 'a', 'b', 'c' };
    
        using namespace boost::fusion::adapted;
        do_tests(AsABC{ms});
        do_tests(AsCBA{ms});
        do_tests(AsBCA{ms});
    }
    

    Prints

    <a,b,c>
    <a,b,c>
    <c,b,a>
    <c,b,a>
    <b,c,a>
    <b,c,a>
    
  3. Yes you can do without adapting (don't though):

    Live On Wandbox (commenting parts because of compiletime limitations)

    struct MyStruct { char a, b, c; };
    
    #include <boost/spirit/include/karma.hpp>
    #include <boost/spirit/include/phoenix.hpp>
    namespace bsk = boost::spirit::karma;
    namespace phx = boost::phoenix;
    
    template <typename It = boost::spirit::ostream_iterator>
    struct MyGen : bsk::grammar<It, MyStruct()> {
        MyGen() : MyGen::base_type(start_) {
            using boost::proto::deep_copy;
            using namespace bsk;
            auto A = deep_copy(char_[ _1 = phx::bind(&MyStruct::a, _val) ]);
            auto B = deep_copy(char_[ _1 = phx::bind(&MyStruct::b, _val) ]);
            auto C = deep_copy(char_[ _1 = phx::bind(&MyStruct::c, _val) ]);
            start_ =
                '<' << A << ',' << B << ',' << C << '>' << eol <<
                '<' << A << ',' << C << ',' << B << '>' << eol <<
                '<' << B << ',' << A << ',' << C << '>' << eol <<
                '<' << C << ',' << A << ',' << B << '>' << eol <<
                '<' << B << ',' << C << ',' << A << '>' << eol <<
                '<' << C << ',' << B << ',' << A << '>' << eol
                ;
        }
      private:
        bsk::rule<It, MyStruct()> start_;
    };
    
    int main() {
        std::cout << format(MyGen<>{}, MyStruct { 'a', 'b', 'c' });
    }
    

    Prints

    <a,b,c>
    <a,c,b>
    <b,a,c>
    <c,a,b>
    <b,c,a>
    <c,b,a>
    

1
一如既往,非常详尽的回答。这里是另一种可能的方法:http://www.boost.org/libs/spirit/example/karma/reorder_struct.cpp - llonesmiz

2
您可以使用 Phoenix 和 Bind 来访问成员变量。所以,对于您的示例来交换成员变量,代码如下:
#include <iostream>
#include <boost/spirit/include/karma.hpp>
#include <boost/spirit/include/phoenix_core.hpp>
#include <boost/spirit/include/phoenix_operator.hpp>
#include <boost/phoenix/bind/bind_member_variable.hpp>
using namespace std;
struct MyStruct {
    int a;
    int b; 
    int c;
};

struct MyStructGenerator : boost::spirit::karma::grammar<boost::spirit::ostream_iterator, MyStruct()>
{
    MyStructGenerator() : MyStructGenerator::base_type(start_)
    {
        namespace bsk = boost::spirit::karma;
        start_ = '<' 
            << bsk::int_[bsk::_1 = boost::phoenix::bind(&MyStruct::c, bsk::_val)]
            << ','
            << bsk::int_[bsk::_1 = boost::phoenix::bind(&MyStruct::b, bsk::_val)]
            << ','
            << bsk::int_[bsk::_1 = boost::phoenix::bind(&MyStruct::a, bsk::_val)]
            << '>';
    }

    ~MyStructGenerator() = default;
    boost::spirit::karma::rule<boost::spirit::ostream_iterator, MyStruct()> start_;
};

int main() {
    MyStruct ms = { 3, 2, 1 };
    std::cout << boost::spirit::karma::format(MyStructGenerator(), ms) << std::endl;
}

哇,我花了太长时间了 :) 不过这个示例属于我在答案中标记为“不要”的一个部分。另请参阅https://dev59.com/02sy5IYBdhLWcg3w3h2I - sehe
@sehe:我知道不应该使用“语义动作”,但对于快速的hack,我发现它们非常有用。但是,当涉及到spirit/karma时,你的答案总是尽可能完整的;-) - mkaes

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接