In C++14, I would like to be able to save function arguments and call the function at a later time. My goal is to be able to do something like:
auto delayed_function{ some_callable };
delayed_function( 1, 2, 3 ); // arguments are stored, but some_callable is not called yet
delayed_function.call(); // some_callable called with (1,2,3)
The main issue is that I need to take the arguments and store them. It seems to be that storing them in a std::tuple would work the best. The question is what exactly the type of the tuple should be. My thinking is that I need to deduce the type of the tuple when the arguments are provided (rather than trying to deduce the type from the arguments to the callable), since when the arguments are provided, they may be references, rvalues, etc., and I'll only know that from the argument types. What that means, however, is that even if I know the type of the callable, I won't know the type of the tuple except in the template method where the arguments are provided (operator() in my above example). So it sounds like I need some sort of type erasure. My implementation for the argument storage is currently:
template <typename Callable>
class arg_tuple
{
public:
template <typename ... Args>
arg_tuple( Args&&... args )
{
using TupleType = decltype( std::make_tuple(std::forward<Args>(args)...) );
args_ =
std::make_unique< typed_arg<TupleType> >(
std::make_tuple(std::forward<Args>(args)...)
);
}
void
invoke( Callable f )
{
args_->invoke( f );
}
private:
template <typename T = void>
struct untyped_arg {
virtual ~untyped_arg() = default;
virtual void invoke( Callable f ) = 0;
};
template <typename T>
struct typed_arg
: public untyped_arg<> {
template <typename Arg>
typed_arg( Arg&& arg )
: value(std::forward<Arg>(arg) ) {}
virtual void
invoke( Callable f )
{
// By moving value, I can only call invoke once, but it potentially saves
// copying, and I can use move-only types in the arg list.
my_apply( f, std::move(value) );
}
T value;
};
std::unique_ptr<untyped_arg<>> args_;
};
where "my_apply" is essentially std::apply, but since I'm using C++14, I had to implement it manually. The good news is that this basically works:
auto f = [](int i) { printf("i+3=%d\n", i+3); };
using Callable = decltype(f);
arg_tuple<Callable> t(20);
t.invoke(f);
What bothers me about the implementation, however, is that Callable is a template parameter to arg_tuple. I would really like it to be a template parameter to the invoke method, not the entire class. The reason I did it this way is that arg_tuple doesn't know the type of the stored tuple, so invoke needs a virtual function to be able to get down to where the type tuple type is known (typed_arg). Virtual methods cannot have template parameters, however, so that's where I get stuck.
If there's a way that I could make Callable a template method of invoke, then could make it a forwarding reference, so I wouldn't have to take it by value. Otherwise, I think I may need multiple overloads of invoke for lvalue, const lvalue, and rvalue references, in case Callable is a mutable object.
Is there a better way to do this?
There are couple ways to do it.
Using future:
auto value = std::async(std::launch::deferred, delayed_function, 1, 2, 3);
...
value.get();
Or just using lambda:
auto f = [arg]() { return delayed_function(arg, 2, 3); }
...
f();
Collected from the Internet
Please contact [email protected] to delete if infringement.
Comments