First, I think if the order does matter it's probably best to explicitly construct those elements prior to the call, then pass them in. Much easier to read, but far less fun!
This is just expanding on Kerrek's answer:
#include <utility>
namespace detail
{
// the ultimate end result of the call;
// replaceable with std::result_of? I do not know.
template <typename F, typename... Args>
static auto ordered_call_result(F&& f, Args&&... args)
-> decltype(std::forward<F>(f)
(std::forward<Args>(args)...)); // not defined
template <typename R>
class ordered_call_helper
{
public:
template <typename F, typename... Args>
ordered_call_helper(F&& f, Args&&... args) :
mResult(std::forward<F>(f)(std::forward<Args>(args)...))
{}
operator R()
{
return std::move(mResult);
}
private:
R mResult;
};
template <>
class ordered_call_helper<void>
{
public:
template <typename F, typename... Args>
ordered_call_helper(F&& f, Args&&... args)
{
std::forward<F>(f)(std::forward<Args>(args)...);
}
};
// perform the call then coax out the result member via static_cast,
// which also works nicely when the result type is void (doing nothing)
#define ORDERED_CALL_DETAIL(r, f, ...) \
static_cast<r>(detail::ordered_call_helper<r>{f, __VA_ARGS__})
};
// small level of indirection because we specify the result type twice
#define ORDERED_CALL(f, ...) \
ORDERED_CALL_DETAIL(decltype(detail::ordered_call_result(f, __VA_ARGS__)), \
f, __VA_ARGS__)
And an example:
#include <iostream>
int add(int x, int y, int z)
{
return x + y + z;
}
void print(int x, int y, int z)
{
std::cout << "x: " << x << " y: " << y << " z: " << z << std::endl;
}
int get_x() { std::cout << "[x]"; return 11; }
int get_y() { std::cout << "[y]"; return 16; }
int get_z() { std::cout << "[z]"; return 12; }
int main()
{
print(get_x(), get_y(), get_z());
std::cout << "sum: " << add(get_x(), get_y(), get_z()) << std::endl;
std::cout << std::endl;
ORDERED_CALL(print, get_x(), get_y(), get_z());
std::cout << "sum: " << ORDERED_CALL(add, get_x(), get_y(), get_z()) << std::endl;
std::cout << std::endl;
int verify[] = { get_x(), get_y(), get_z() };
}
That last line is there to verify brace initializers do in fact have effect, normally.
Unfortunately as has been discovered from other answers/comments, GCC does not get it right so I cannot test my answer. Additionally, MSVC Nov2012CTP also does not get it right (and has a nasty bug that chokes on the ordered_call_result
†). If someone wants to test this with clang, that would be swell.
†For this particular example, the trailing return type can be decltype(f(0, 0, 0))
instead.