4
\$\begingroup\$

I have a small "plugin system" (I'm not sure this is the right name). It allow you to store objects (plugins), and then call some methods from each of them. With this approach we have absolutely no overhead on iterating/object pass/callback call.

// Need this SET, because in C++11 auto can only be static const. 
// And we may need to change them later.
#ifndef SET
    #define SET(NAME, VALUE) decltype(VALUE) NAME = VALUE
#endif

Plugin1 plugin1(param1, param2);
Plugin2 plugin2(param1, param2);
Plugin3 plugin3(param1, param2);

SET(plugins, std::forward_as_tuple(           // Pay attention here. We store &&, not objects
    plugin1, 
    plugin2,
    plugin3
));

Then, when I need to call some function from each of plugins, or do something with each of them, I iterate through plugins at compile time (I check this with generated asm code, and it just calls or even inline do_it, without anything else) and call the callback function (I'm not showing iterate code here; it's trivial):

struct Call{
    float k=0;

    template<typename T, int Index>        // lambda function not efficient than this. Tested -O2 clang, gcc 4.8
    inline void do_it(T &&t){               
        std::cout << "value = " <<t << " ; " << "id = " << Index << std::endl;
    }
};

And if I need to do something with a specific plugin, I just directly use plugin1, plugin2, and plugin3. No need to call std::get<>. Plus the IDE highlights available plugins when you type them.

Is it ok to store rvalue references on objects, or I have to store objects directly in my tuple?

Like this:

SET(plugins, std::tuple(
    Plugin1(param1, param2), 
    Plugin2(param1, param2),
    Plugin3(param1, param2)
));

When I iterate, I pass a plugin as a usual reference:

struct Call{
    float k=0;

    template<typename T, int Index>        // lambda function not efficient than this. Tested -O2 clang, gcc 4.8
    inline void do_it(T &t){               
        std::cout << "value = " <<t << " ; " << "id = " << Index << std::endl;
    }
};

With this approach we have additional move constructor calls for each element in the tuple. The previous version is free.

I ask about this because I previously read about data locality and now I worry about how data are placed in memory.

\$\endgroup\$
3
\$\begingroup\$

First, you are not storing &&, but rather &. Why? because plugin1 etc. are lvalue references, and because of the reference collapsing rules.

Second, you can use a tuple of references as you would use a reference itself. That is, everything is fine unless you return a tuple of references to local objects, in which case references are dangling.

Third, between forwarding (forward_as_tuple) and copying (make_tuple) there's another option:

template<class... A>
constexpr std::tuple<A...> auto_tuple(A&&... a)
{
    return std::tuple<A...>(std::forward<A>(a)...);
}

which keeps a reference to lvalues, and copies rvalues only. I find this most convenient.

In general, storing references or objects depends on what you want to do. Think of what you would choose if it was only one object, then keep the same choice for the tuple.

Check here for more.

| improve this answer | |
\$\endgroup\$
  • \$\begingroup\$ "Third, between forwarding (forward_as_tuple) and copying (make_tuple) there's another option:" - Maybe I something misunderstand, but here en.cppreference.com/w/cpp/utility/tuple/forward_as_tuple said "A std::tuple object created as if by std::tuple<Types&&...>(std::forward<Types>(args)...)" what is exactly the same as your code. Or no? \$\endgroup\$ – tower120 May 19 '14 at 23:10
  • \$\begingroup\$ Well, even as they store lvalue references, it is still good as for me. But you are right, this is rather work for std::tie, not for std::forward_as_tuple :) \$\endgroup\$ – tower120 May 19 '14 at 23:15
  • \$\begingroup\$ @tower20 forward_as_tuple returns tuple<A&&...>. My auto_tuple returns tuple<A...>, so when it takes an rvalue reference e.g. int&& it keeps only int and so makes a copy. For an lvalue reference e.g. int& it keeps the same int& so doesn't copy. \$\endgroup\$ – iavr May 20 '14 at 7:31
  • \$\begingroup\$ " My auto_tuple returns tuple<A...>" - Ah, I see. Isn't rvalue references is the same as lvalue, but for temporary objects? \$\endgroup\$ – tower120 May 20 '14 at 10:58
  • \$\begingroup\$ @tower20 Yes, but you need to understand that in template<typename A> fun(A&& a);, A&& is a universal reference: given an argument of type int&& / int&, template parameter A is deduced as int / int& respectively. \$\endgroup\$ – iavr May 20 '14 at 12:27

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.