Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Avoiding attribute copies with karma generators

I'm using karma to generate representations of large structs, but the structs are being copied during generation. I don't think they need to be, so was wondering how to avoid it.

The quick example below prints "Copy!", as the target struct is copied in 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 );

I can stop the copy by declaring foo_rule's attribute by reference:

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

but that doesn't work with a vector [obviously the foos are therefore copyable, but may be cheap to copy at vector construction, but expensive to copy at generate time :-)]

The example below prints 'Copy!' five times during generation (that is, ignoring copies during vector ctor); 10 times if foo_rule's attribute isn't a reference:

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

Having both rules take references doesn't compile with Boost 1.47 on VC 2008. That is, with:

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

I get extract_from_container instantiated with Attribute = std::vector<foo> and Exposed=std::vector<foo> &. On line 131 of extract_from.hpp, it tries to form Exposed const & and the compiler fails when creating refrence-to-reference.

I feel like I'm missing something, so any pointers would be greatly appreciated!

like image 933
aldous Avatar asked Nov 09 '11 13:11

aldous


1 Answers

I'm sure you've tried it, but i'll say it nonetheless. Have you tried as follows:

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

Update I just tested it with the below snippet (g++ 4.6 with Boost 1.47.0). It confirms that the above works. However, there is room for confusion, as the std::vector<foo> my_vec_foo(5) will also show 5 copies being made. See the BIG LETTER warning in the code and the output:

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

Output:

Copy!
Copy!
Copy!
Copy!
Copy!

START WATCHING NOW
like image 121
sehe Avatar answered Oct 16 '22 14:10

sehe