static std::string test1_cb(const std::string &a, const std::string &b) { return a+b; } // ... auto callback=x::make_postponedcall(test1_cb, std::string("foo"), std::string("bar")); // ... callback->invoke();
The first argument
to x::make_postponedcall
()
is a callback object of some kind.
It can be a simple function pointer, a lambda,
or an object that implements an
operator()
().
Any remaining parameters get forwarded as parameters the function call.
The function call does not occur immediately.
Rather,
x::make_postponedcall
() returns
an x::ref
, a reference to a
reference-counted object, a handle that
can be freely copied and passed around.
Dereferencing the handle and calling
invoke
() executes the function call, with the
arguments that were previously passed to
x::make_postponedcall
().
In other words, x::make_postponedcall
()
“freezes” the function call, the object/function getting
called and its parameters. The frozen function call
gets saved in an internal object,
whose handle gets returned. invoke
()
“thaws” and invokes the saved function call.
The callback object, and any arguments passed to
x::make_postponedcall
()
must be copy-constructible.
invoke
() can be called multiple times. Each
invocation repeats the same function call.
x::make_postponedcall
() copies the callback
parameters into an object that holds them, until it's time to make
the actual call. The types of the stored callback arguments is determined
by the types of the arguments to
x::make_postponedcall
(). In the above example,
the literal string constants are explicitly casted into
std::string
s, and they get stored as such.
Without the explicit cast, they would get stored as
const char *s, and coerced to
std::string
s at the time of the callback
invocation.
The callback object may take its parameters by reference, getting a mutable reference to the parameter it the storage object. Invoking a postponed function call simply passes each stored parameter to the callback object, so it can be passed by reference:
static std::string test1_cb(std::string &a, std::string &b) { // ...
Note that this requires that the call to
x::make_postponedcall
() now must supply
std::string
s as parameters, so that's what gets
stored in the callback handle. A literal
const char * won't cut it any more.
The callback object can modify the argument that's passed by reference. Invoking the callback again passes the reference to the modified value!
As previously mentioned, the callback can be an object, rather than a function pointer:
class add_cl { public: int operator()(int a, int b) { return a+b; } }; // ... const add_cl callback; auto handle=x::make_postponedcall(callback, 2, 2);
Note that the callback object gets copied into the returned handle, so it must be copy-constructible. A lambda will also work, just as well:
auto call2=x::make_postponedcall([](const std::string &a, const std::string &b) { return a+b; }, str1, str2);
x::make_postponedcall
()
returns a x::ref
to a complicated template class, parametrized by the
decayed
callback object type, callback return value type, and decayed callback
parameter types. An auto
declaration is the only way
to maintain one's sanity. If the return type of the callback invocation
is known, x::make_postponedcall
()
can be converted to a
x::postponedcallBaseObj<T> >
, where
T
is the return value type:
static size_t length_cb(const std::string &a, const std::string &b) { return a.size() + b.size(); } // ... x::ref<x::postponedcallBaseObj<size_t> > handle=x::make_postponedcall(test1_cb, "a", "bb"));
x::make_postponedcall()
saves the postponed parameters in a
std::tuple
.
It uses the following set of template classes to convert the tuple back
into a parameter pack, for the subsequent function call.
These templates can be used independently. To convert a tuple into a
parameter pack:
Define a class with an opaque template parameter:
#include <x/tuple_param_pack.H> template<typename ParamPackArgsType> class postponedCallbackHelper;Specialize the template for “
size_t ...n
”, with the specialized class ofx::tuple_param_pack<n...>
and use “std::get<n>(tuple
)...” to get your parameter pack:template<size_t ...n> class postponedCallbackHelper<x::tuple_param_pack<n...>> { public: template<typename callback_type, typename tuple_arg_type> static auto invoke(callback_type &callback, tuple_arg_type &args) -> decltype(callback(std::get<n>(args)...)) { return callback(std::get<n>(args)...); }Instantiate the template class, specifying the template parameter as “
x::tuple_2_param_pack<sizeof...(Args)>::type
” whereArgs
is the tuple pack.class F callback; std::tuple<TupleArgs...> arguments; // ... postponedCallbackHelper<x::tuple_2_param_pack<sizeof...(TupleArgs)>::type>::invoke(callback, arguments);