[inline] entry methods should use perfect forwarding in C++11
Currently inline entry methods are not as useful as they could be in C++11 because they do not use perfect forwarding. For example, if I call an inline entry method:
there is a still a copy done, even though in C++11 move semantics can be used to elide the copy. The necessary change to the generated
receive_data method is to have:
template <class TemporalId, class ReceiveData_t> #if __cplusplus >= 201103L void CProxyElement_AlgorithmArray< Tentacle, Metavariables, OrderedActionsList, InboxTagList, TemporalId, SpectreArrayIndex, InitialDataBox>::receive_data(TemporalId&& impl_noname_1, ReceiveData_t&& impl_noname_2, bool enable_if_disabled, const CkEntryOptions* impl_e_opts) #else /* current code */ #endif // __cplusplus >= 201103L
then for the call to the local object:
#if __cplusplus >= 201103L obj->template receive_data<ReceiveTag>(std::forward<TemporalId>(impl_noname_1), std::forward<ReceiveData_t>(impl_noname_2), enable_if_disabled); #else obj->template receive_data<ReceiveTag>(impl_noname_1, impl_noname_2, enable_if_disabled); #endif
I would argue that without the use of perfect forwarding
[inline] entry methods do not actually work as advertised in C++11 and so that is why I'm filing this as a bug rather than a feature request. The current workaround would be to pass a const pointer to a non-const object, then move that, but that does not actually do the same thing: if the sender wanted the receiver to copy the data (ie it's meant to be a const &) then you have a bug.
#5 Updated by Eric Mikida about 2 months ago
Nils, unless I'm misunderstanding something, the above example would still either require that the objects function take a const ref, or by value (therefore still making a copy).
This is because for the code generated when the non-inlined path is taken, if the objects method just takes a regular reference, you get a compilation error for trying to bying a non-const lvalue reference to a temporary object.
Is that intended, or is there something I'm missing?
#6 Updated by Eric Mikida about 2 months ago
Nevermind. The restriction I mentioned was due to a different change for marshalling objects without default constructors. It makes this patch less straightforward but I think I understand the workaround now. I still want to clarify that for inline methods you would want the decls to look something like the following, right? Specifically, the function for the object takes a reference (const or non-const should both be options).
In the ci:
entry [inline] void receiveData(DataType d)
In the h
void receiveData(DataType& d)
#7 Updated by Eric Mikida about 1 month ago
After some more exploration, I'm not sure why this change is needed. Where is the extra copy you are referring to happening? The call to the proxy is pass-by-reference, and if you have your object method take a const reference, then the whole chain is pass-by-reference and no copies are made. If the objects method is not a const reference, then there is a single copy made between the proxy and your object to get pass-by-value semantics.
I guess I'm just confused by what exactly the bug is, where the extra copy occurs, and what this fix aims to do. (You can probably ignore my last updates)
#9 Updated by Nils Deppe 24 days ago
The reason is that currently without perfect forwarding the called function would receive an lvalue reference, which you should never move out of and could be really problematic. In order for the called function to properly be able to grab the data in the most efficient way I need to know whether or not I can move arguments passed to it. For example, say I call the same inline entry methods twice, once with an rvalue reference and once with a non-const lvalue reference. In the rvalue case I can safely move the data, in the lvalue case I cannot, resulting in an extra copy.
#12 Updated by Eric Mikida 3 days ago
The patch where this is implemented just replaces the currently generated code with the code that Nils provided above. However this breaks the API by enforcing that rvalue references always be passed, so something like
proxy.entryMethod(obj) would have to be rewritten to
proxy.entryMethod(std::move(obj)), which we don't want. We could have instead have the method overridden with the header Nils provided, however this would be an all or nothing approach, either all args would be r value refs, or none would. Is that sufficient? Is there a better solution I may be missing?