Musings on C++

Tuples: Expanding into Function Parameters

It’s been quite a while since my last blog post. I’m still doing various things with C++, but haven’t had anything worth blogging about. Recently I began doing some testing with std::tuple to see what compilers might be able to optimize away and what they cannot. One of the first things I wanted to do was use it to store parameters – either for object construction or for use on a callable.

EDIT: A comment by FOONATHAN on this entry has a much tighter implementation of the code which can be found here: http://ideone.com/lUbOFA
The only small thing missing is that the tuple should ideally be forwarded inside apply. Other than that it is much cleaner, but the current VS2015 CTP seems to have a compiler bug and it fails to deduce one of the template parameters for this code.

The Mandate
Write some code to expand a std::tuple into the argument list for any callable, using forwarding mechanics to ensure the rvalue bindings are preferred when the tuple itself is an rvalue (or moved) so that move-only contents of the tuple are also supported.

So basically the method is fairly straight forward: a helper function recursively calls itself, adding on each successive std::get for each ‘layer’, until it reaches index 0 for the tuple – at which point it can pass the variadic arguments it has built up on to the function we want to unpack the tuple into. Since the helper function will need to have a partial specialization for the terminal case when the tuple index is 0 (and the desired function is finally called), it needs to be wrapped as a function object so the struct can be specialized.

In the situation where tuple_into_callable is called with a function as the first parameter (as opposed to a lambda, for example) there is a case where the function could be overloaded. In the case of an overload it would ordinarily be up to the caller to specify which overload is intended. We want the compiler to choose which overload is called based on the tuple’s contained types. So the solution is a handy macro called wrap_overload() which simply turns an overloaded function signature into a generic lambda which is not overloaded. This way the tuple_into_callable is able to have it’s template parameter deduced (theres only one to choose from so the caller does not need to specify), and the whole overload selection is delayed until the actual call is made.

Currently under Visual Studio 2015’s latest CTP there is a bug where a function returning decltype(auto) deduces an incorrect return type in the case where it’s return type is that of a function being passed in as a parameter. The TUPLE_FWD_RETURN macro is a little hack to get around that. (For those interested, the return type deduced is a && which causes the object being returned to go out of scope too early causing an incorrect call to the destructor before the object is used. Correct behavior would be the deduction of a plain value type (non-reference) so that either RVO or move construction can bubble it up.)

I think the test code kind of speaks for itself. It tests that the correct overload for a target function is called, tests having rvalue and lvalue tuples, and also tests being able to work with a tuple containing a move-only type. The only thing to remember is that if the tuple contains a move-only type, then it needs to be moved into the tuple_into_callable function.

Thank you very much for that. One of the downsides in working in an environment where engineers are reluctant to even look at the new standards means I don’t have the resources to bounce ideas off. I didn’t know of the index_sequence. This is a fantastic solution. Unfortunately VS2015 isn’t wanting to deduce the template argument for integer_sequence in apply_impl, so since GCC manages to it’s probably another compiler issue.