Chapter 45. A postponed function call or an object call

Index

Converting tuples to parameter packs
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::strings, and they get stored as such. Without the explicit cast, they would get stored as const char *s, and coerced to std::strings 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::strings 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"));

Converting tuples to parameter packs

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 of x::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 where Args is the tuple pack.

    class F callback;
    std::tuple<TupleArgs...> arguments;
    
    // ...
    
    postponedCallbackHelper<x::tuple_2_param_pack<sizeof...(TupleArgs)>::type>::invoke(callback, arguments);