【问题标题】:Avoiding attribute copies with karma generators使用业力生成器避免属性副本
【发布时间】:2011-11-09 13:40:19
【问题描述】:

我使用karma 来生成大型结构的表示,但是在生成过程中会复制这些结构。我不认为他们需要这样做,所以想知道如何避免它。

下面的快速示例打印“复制!”,因为目标结构被复制到rule::generate

namespace karma = spirit::karma;
namespace phoenix = boost::phoenix;

struct foo
{
    foo() { }
    foo( foo const &other ) { std::cout << "Copy!"; }
    int f() const { return 42; }
};

std::string output;
typedef std::back_insert_iterator< std::string > iterator;
karma::rule< iterator, foo() > foo_rule = 
    karma::int_[ karma::_1 = phoenix::bind( &foo::f, karma::_val ) ];
foo my_foo;
iterator it( output );
karma::generate( it, foo_rule, my_foo );

我可以通过引用声明foo_rule的属性来停止复制:

karma::rule< iterator, foo &() > foo_rule

但这不适用于向量[显然foos 因此是可复制的,但在向量构造时复制可能很便宜,但在生成时复制成本高:-)]

下面的示例打印“复制!”生成期间五次(即在向量 ctor 期间忽略副本);如果foo_rule 的属性不是引用,则为 10 次:

std::vector<foo> my_vec_foo(5);
karma::rule< iterator, std::vector<foo>() > vec_foo_rule = *foo_rule;
karma::generate(it, vec_foo_rule, my_vec_foo);

在 VC 2008 上使用 Boost 1.47 无法编译同时引用这两个规则。也就是说,使用:

karma::rule< iterator, foo &() > foo_rule /* = ... */;
karma::rule< iterator, std::vector<foo> &() > vec_foo_rule /* = ... */;

我用Attribute = std::vector&lt;foo&gt;Exposed=std::vector&lt;foo&gt; &amp; 实例化了extract_from_container。在 extract_from.hpp 的第 131 行,它尝试形成 Exposed const &amp; 并且在创建引用引用时编译器失败。

我觉得我错过了一些东西,所以任何指针都将不胜感激!

【问题讨论】:

  • +1 表示极好的最小问​​题。也许,你可以让它更加复制/粘贴(现在对 Spirit 没有深入了解的人不太可能让它运行)。我测试了我的答案,帖子包含了一个完整的最小样本。干杯

标签: c++ boost-spirit boost-spirit-karma


【解决方案1】:

我确定你已经尝试过了,但我还是会说出来。你有没有试过如下:

std::vector<foo> my_vec_foo(5);
karma::rule< iterator, std::vector<foo>&() > vec_foo_rule = *foo_rule;
karma::generate(it, vec_foo_rule, my_vec_foo);

更新我刚刚用下面的 sn-p 测试了它(g++ 4.6 和 Boost 1.47.0)。它证实了上述工作。但是,存在混淆的空间,因为std::vector&lt;foo&gt; my_vec_foo(5) 也将显示正在制作的 5 个副本。请参阅代码和输出中的 BIG LETTER 警告:

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

namespace karma = boost::spirit::karma;
namespace phoenix = boost::phoenix;

struct foo
{
    foo() { }
    foo( foo const &other ) { std::cerr << "Copy!\n"; }
    int f() const { return 42; }
};

int main()
{
    std::string output;
    typedef std::back_insert_iterator< std::string > iterator;
    iterator it( output );
    karma::rule< iterator, foo&() > foo_rule = 
        karma::int_[ karma::_1 = phoenix::bind( &foo::f, karma::_val ) ];

    foo my_foo;
    karma::generate( it, foo_rule, my_foo );

    std::vector<foo> my_vec_foo(5);

    std::cerr << "\nSTART WATCHING NOW" << std::endl;

    karma::rule< iterator, std::vector<foo>&() > vec_foo_rule = *foo_rule;
    karma::generate(it, vec_foo_rule, my_vec_foo);
}

输出:

Copy!
Copy!
Copy!
Copy!
Copy!

START WATCHING NOW

【讨论】:

  • 五个副本应该清楚,因为这就是调整大小(和特殊 ctor)的工作方式。他们默认构建一个元素作为第二个参数并将其复制到所有保留的插槽中。
  • @Xeo:你认为我应该解释一下吗?我认为这很明显:)
  • 只是想向任何可能想知道的人指出这一点。 :) 另外,您的示例缺少一些包含,并且似乎无法在 Ideone 上编译。看来他们使用的Boost版本太低了。
  • @Xeo:“缺少包含”似乎措辞有点强硬。这是风格问题:我碰巧知道(在 linux 上)所有间接标头都存在,并且在 SO 上更喜欢简洁。请注意,OP 缺少更多(关键)包括。 那些会很难找到。最后,我从未承诺过它可以在 ideone 上运行 :)(我不知道为什么它很重要)
  • 哦,好吧,这并不重要,我只是想看看生成器的确切输出,因为我决定在不久的将来更多地了解 Spirit。 :)
猜你喜欢
  • 2017-12-06
  • 2017-10-07
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多