why use invoke helper rather than just call functor? - c++

As shown in the 'possible implementation' of std::apply we see that the standard library function std::invoke is used to invoke the callable object F.
Is it needed in this situation? if so, for what reason?
What are the benefits of writing:
template<typename F, typename ... Args>
decltype(auto) func(F &&f, Args &&... args){
return std::invoke(std::forward<F>(f), std::forward<Args>(args)...);
}
over:
template<typename F, typename ... Args>
decltype(auto) func(F &&f, Args &&... args){
return std::forward<F>(f)(std::forward<Args>(args)...);
}
?

A pointer to member is Callable, and invoke (or INVOKE, as that soon-to-be-seven-bullet-point construct is known in the Standard)'s magic handles this case (well, four and soon to be six cases actually), while the function call syntax doesn't.

I want to complement T.C.'s answer with a syntactic example:
struct X {
int x;
int foo(int a) const { return a + x; }
};
and you have an X object and a pointer to a member function, e.g.:
X obj = X{1000};
auto fn = &X::foo;
and need to call func.
With the call syntax, this won't work:
func_call(fn, obj, 24); // compiler error
error: must use '.' or '->' to call pointer-to-member function in [...]
Instead you have to work around it:
func_call([obj, fn](int a) { return (obj.*fn)(a); }, 24);
If you had the invoke method, you could just have written:
func_invoke(fn, obj, 24);

Related

std::function argument to receives any number of arguments

I've defined a template function that receives std::function. and I want to send a member function. that works fine (example: test2)
How can I rewrite it so std::function receives any number of argument? (test3)
another question - can this be done without the std::bind?
struct Cls
{
int foo() { return 11; }
};
template<typename T>
void test2(std::function<T()> func)
{}
template<typename T, typename... Args>
void test3(std::function<T(Args... args)> func, Args&&... args)
{}
int main()
{
Cls ccc;
test2<int>(std::bind(&Cls::foo, ccc)); // Ok
test3<int>(std::bind(&Cls::foo, ccc)); // does not compile!
}
This is the error I receive:
no matching function for call to ‘test3<int>(std::_Bind_helper<false, int (Cls::*)(), Cls&>::type)’ 34 | test3<int>(std::bind(&Cls::foo, ccc));
The issue is that std::bind doesn't return a std::function, it returns some unspecified callable type. Therefore the compiler can't deduce the correct template parameters for test3.
The way to work around that is to make your function accept any callable type instead of trying to accept a std::function:
template <typename Func, typename... Args>
void test3(Func&& func, Args&&... args)
{}
This will work with a std::bind result, a lambda, a raw function pointer, a std::function, etc.
can this be done without the std::bind?
Sort of. A pointer to a member function always requires an instance of the class to be called on. You can avoid needing to explicitly std::bind the instance into a wrapper by passing it as a parameter and using the std::invoke helper to call the function. For pointers to member functions, std::invoke treats the first parameter as the object to call the member on.
For example:
struct Cls
{
int foo() { return 11; }
};
template <typename Func, typename... Args>
void test3(Func&& func, Args&&... args)
{
std::invoke(std::forward<Func>(func), std::forward<Args>(args)...);
}
void bar(double, int) {}
int main()
{
Cls ccc;
test3(bar, 3.14, 42); // Works with a raw function pointer
test3(&Cls::foo, ccc); // Works with a pointer to member function
test3([](int){}, 42); // Works with other callable types, like a lambda
}
Demo
can this be done without the std::bind?
Yes this can be done without std::bind as shown below.
struct Cls
{
int foo() { return 11; }
int func(int, double)
{
return 4;
}
};
template<typename className, typename... Param,typename Ret, typename... Args>
void test4(Ret (className::*ptrFunc)(Param... param),className& Object, Args... args)
{
(Object.*ptrFunc)(args...);
//std::invoke(ptrFunc, Object, args...); //WITH C++17
}
int main()
{
Cls ccc;
test4(&Cls::foo, ccc); //works
test4(&Cls::func, ccc, 4,5); //works
}
Working demo
With C++17 we can use std::invoke to replace the call (Object.*ptrFunc)(args...); with:
std::invoke(ptrFunc, Object, args...); //WITH C++17
C++17 std::invoke demo

How to pass a template function to another function and apply it

// I can't change this function!
template<typename Function, typename... Args>
void RpcWriteKafka(Function func, Args&&... args) {
func(std::forward<Args>(args)...);
}
// I can change this one if necessary.
template<typename FUNC, typename... Args, typename CALLBACK, typename... CArgs>
void doJob(std::tuple<CALLBACK, CArgs&&...> tp, FUNC func, Args&&... args) {
// SetValues(std::forward<Args>(args)...);
std::apply(func, tp);
}
int main() {
doJob(std::make_tuple([](int i){}, 1), RpcWriteKafka, 1);
return 0;
}
As you see, some library provided the template function RpcWriteKafka. It needs the parameter about a callback function(func) and its parameters(args...).
I want to define my own function doJob, which allows me to call it like this: doJob(std::make_tuple([](int i){}, 1), RpcWriteKafka, 1);. I'm expecting that the first parameter, which is a std::tuple, could be passed to the second parameter RpcWriteKafka.
Why do I used std::tuple: How to pass a function with parameter pack to a function with parameter pack
For now, it can't be compiled.
The compiler generated two errors:
mismatched types 'CArgs&&' and 'int', which comes from the 1 in that tuple;
the second parameter RpcWriteKafka is unresolved overloaded function type.
So how to solve the two issues? Is it possible to define such a function doJob so that I can call it easily as shown in the main above?
First, the first parameter type of doJob should be std::tuple<CALLBACK, CArgs...> instead of std::tuple<CALLBACK, CArgs&&...> since CArgs&&
cannot be deduced in such context.
Second, since RpcWriteKafka is a function template, you can't pass it to doJob like this, instead, you need to wrap it with lambda, so this should work (I omit the Args&&... because it is not used)
template<typename CALLBACK, typename... CArgs, typename FUNC>
void doJob(std::tuple<CALLBACK, CArgs...>&& tp, FUNC func) {
std::apply(func, tp);
}
int main() {
doJob(std::make_tuple([](int i){}, 1),
[](auto... args) { RpcWriteKafka(args...); });
}

c++14 Variadic lambda capture for function binding

I'm currently reading a few books to get caught up on c++14 features. I am trying to use a variadic template to bind arguments to a function. I know how to do this using std::bind, but I would also like to implement this function with a c++14 lambda expression, just for common knowledge and understanding, and for any possible performance benefits. I've read that lambdas can be inlined while std::bind cannot inline because it takes place through a call to a function pointer.
Here is the code from myFunctions.h:
#include <functional>
int simpleAdd(int x, int y) {
return x + y;
}
//function signatures
template<class Func, class... Args>
decltype(auto) funcBind(Func&& func, Args&&...args);
template<class Func, class... Args>
decltype(auto) funcLambda(Func&& func, Args&&...args);
/////////////////////////////////////////////////////////////////
//function definitions
template<class Func, class... Args>
inline decltype(auto) funcBind(Func&& func, Args&&... args)
{
return bind(forward<Func>(func), forward<Args>(args)...);
}
template<class Func, class ...Args>
inline decltype(auto) funcLambda(Func && func, Args && ...args)
{ //The error is caused by the lambda below:
return [func, args...]() {
forward<Func>(func)(forward<Args>(args)...);
};
}
Here is the main code I am running:
#include<iostream>
#include<functional>
#include "myFunctions.h"
using namespace std;
int main()
{
cout << "Application start" << endl;
cout << simpleAdd(5,7) << endl;
auto f1 = funcBind(simpleAdd,3, 4);
cout << f1() << endl;
//error is occurring below
auto f2 = funcLambda(simpleAdd, 10, -2);
cout << f2() << endl;
cout << "Application complete" << endl;
Error C2665 'std::forward': none of the 2 overloads could convert all the argument types
Error C2198 'int (__cdecl &)(int,int)': too few arguments for call
I think the error might be occurring when the variadic arguments are getting forwarded to the lambda, but I'm not really sure.
My question is how do I properly formulate this code so that I can use a lambda to capture the function and its arguments, and call it later.
I've read that lambdas can be inlined while std::bind cannot inline
because it takes place through a call to a function pointer.
If you pass simpleAdd to something that then binds the arguments, then whether you use bind or not doesn't matter. What do you think the lambda captures with func? It's a function pointer.
The lambda-vs-function-pointer case is about writing bind(simpleAdd, 2, 3) vs. [] { return simpleAdd(2, 3); }. Or binding a lambda like [](auto&&...args) -> decltype(auto) { return simpleAdd(decltype(args)(args)...); } vs. binding simpleAdd directly (which will use a function pointer).
In any event, implementing it is surprisingly tricky. You can't use by-reference capture because things can easily get dangling, you can't use a simple by-value capture because that would always copy the arguments even for rvalues, and you can't do a pack expansion in an init-capture.
This follows std::bind's semantics (invoking the function object and passing all bound arguments as lvalues) except that 1) it doesn't handle placeholders or nested binds, and 2) the function call operator is always const:
template<class Func, class ...Args>
inline decltype(auto) funcLambda(Func && func, Args && ...args)
{
return [func = std::forward<Func>(func),
args = std::make_tuple(std::forward<Args>(args)...)] {
return std::experimental::apply(func, args);
};
}
cppreference has an implementation of std::experimental::apply.
Note that this does unwrap reference_wrappers, like bind, because make_tuple does it.
Your original code breaks down because args are const in the lambda's function call operator (which is const by default), and the forward ends up attempting to cast away constness.
You use a tuple:
template<class Func, class ...Args>
inline decltype(auto) funcLambda(Func && func, Args && ...args)
{ //The error is caused by the lambda below:
auto tpl = make_tuple(std::forward<Args>(args)...);
//Use move just in case Args has move-only types.
return [func, tpl = move(tpl)]() {
apply(func, tpl);
};
}
Where apply is defined something like this:
namespace detail {
template <class F, class Tuple, std::size_t... I>
constexpr decltype(auto) apply_impl( F&& f, Tuple&& t, std::index_sequence<I...> )
{
return f(std::get<I>(std::forward<Tuple>(t))...);
}
} // namespace detail
template <class F, class Tuple>
constexpr decltype(auto) apply(F&& f, Tuple&& t)
{
return detail::apply_impl(std::forward<F>(f), std::forward<Tuple>(t),
std::make_index_sequence<std::tuple_size<std::decay_t<Tuple>>>::value);
}
apply is a feature of one of the library TS versions. With C++17, apply_impl could call invoke, which would work for any callable.

Polymorphic signature of function as template argument (using lambdas)

I'm trying hard for some hours and didn't manage to get this working.
I have a templated class spinlock:
template<typename T> class spinlock {
// ...
volatile T *shared_memory;
};
I'm trying to create something like this:
// inside spinlock class
template<typename F, typename... Ars>
std::result_of(F(Args...))
exec(F fun, Args&&... args) {
// locks the memory and then executes fun(args...)
};
But I'm trying to use a polymorphic function so that I can do this:
spinlock<int> spin;
int a = spin.exec([]() {
return 10;
});
int b = spin.exec([](int x) {
return x;
}, 10); // argument here, passed as x
// If the signature matches the given arguments to exec() plus
// the shared variable, call it
int c = spin.exec([](volatile int &shared) {
return shared;
}); // no extra arguments, shared becomes the
// variable inside the spinlock class, I need to make
// a function call that matches this as well
// Same thing, matching the signature
int d = spin.exec([](volatile int &shared, int x) {
return shared + x;
}, 10); // extra argument, passed as x... should match too
// Here, there would be an error
int d = spin.exec([](volatile int &shared, int x) {
return shared + x;
}); // since no extra argument was given
Basically, I'm trying to make an exec function that accepts F(Args...) or F(volatile T &, Args...) as an argument.
But I can't manage to make automatic detection of types.
How could I accomplish that?
Firstly, this signature will not compile:
// inside spinlock class
template<typename F, typename... Ars>
std::result_of(F(Args...))
exec(F fun, Args&&... args) {
// locks the memory and then executes fun(args...)
};
The return type needs to be
typename std::result_of<F(Args...)>::type
If your compiler implements N3436 then this function will not participate in overload resolution when fun(args...) is not a valid expression, but that is not required in C++11 and not implemented by many compilers yet. You will need to implement your own SFINAE check to prevent result_of giving an error when fun(args...) is not valid, or rewrite it without result_of
template<typename F, typename... Args>
auto
exec(F fun, Args&&... args) -> decltype(fun(std::forward<Args>(args)...))
{
// locks the memory and then executes fun(args...)
}
Then you can overload it for functions that need the additional parameter passed in:
template<typename F, typename... Args>
auto
exec(F fun, Args&&... args) -> decltype(fun(*this->shared_memory, std::forward<Args>(args)...))
{
// locks the memory and then executes fun(*shared_memory, args...)
}
When fun(std::forward<Args>(args)...) is not valid the first overload will not participate in overload resolution. When fun(*this->shared_memory, std::forward<Args>(args)...) is not valid the second overload will not participate in overload resolution. If neither is valid the call will be ill-formed, if both are valid the call will be ambiguous.

std::bind with pointer to a function object

Given a pointer to a function object:
std::shared_ptr<std::function<double(double)>> f;
is there a built-in construct in C++11 that allows be to use this pointer in a std::bind statement? For example:
std::function<double()> g = std::bind(built_in(f), 0.0);
What I am doing is to use variadic template as:
template<typename Ret, typename... Args>
std::function<Ret(Args...)> get_fn(const std::shared_ptr<std::function<Ret(Args...)>>& f)
{
return [=](Args... args) -> Ret { return (*f)(args...); };
}
This allows me to write:
std::function<double()> g = std::bind(get_fn(f), 0.0);
Thanks in advance.
Edit: I should have been clearer. I need to use the instance of the function object inside the shared pointer for optimization purposes.
If we had a Standard function object for the dereference operator (much like std::plus<T> is for adding T with T) then we could involve a nested bind expression, but that is not the case -- plus we'd need a call operator because the usual substitutions that take place during the evaluation of nested bind expressions don't apply to the first argument! Your solution introduces an additional std::function, so I'm proposing some alternatives that don't.
Write your own call and dereference operators:
struct call {
template<typename Callable, typename... Args>
auto operator()(Callable&& callable, Args&&... args) const
// don't use std::result_of
-> decltype( std::declval<Callable>()(std::declval<Args>()...) )
{ return std::forward<Callable>(callable)(std::forward<Args>(args)...); }
};
struct dereference {
template<typename T>
auto operator()(T&& t) const
-> decltype( *std::declval<T>() )
{ return *std::forward<T>(t); }
};
template<typename IndirectCallable, typename... Args>
auto indirect_bind(IndirectCallable&& f, Args&&... args)
-> decltype( std::bind(call {}
, std::bind(dereference {}, std::declval<IndirectCallable>())
, std::declval<Args>()... ) )
{ return std::bind(call {}
, std::bind(dereference {}, std::forward<IndirectCallable>(f))
, std::forward<Args>()... ); }
You can then do auto g = indirect_bind(f, 0.0);. Here's a proof-of-concept that also shows how placeholders are appropriately dealt with.
I mention the above solution because functors like call and dereference are useful bricks -- I personally have them among my tools. My preferred solution however would be to write a polymorphic functor that does the indirection and call in one go:
template<typename Indirect>
struct indirect_callable_type {
// Encapsulation left as an exercise
Indirect f;
template<typename... Args>
auto operator()(Args&&... args)
// don't use std::result_of
-> decltype( std::declval<Indirect&>()(std::declval<Args>()...) )
{ return f(std::forward<Args>(args)...); }
template<typename... Args>
auto operator()(Args&&... args) const
// don't use std::result_of
-> decltype( std::declval<Indirect const&>()(std::declval<Args>()...) )
{ return f(std::forward<Args>(args)...); }
// Lvalue and rvalue *this version also left as an exercise
};
template<typename T>
indirect_callable_type<typename std::decay<T>::type>
make_indirect_callable(T&& t)
{ return { std::forward<T>(t) }; }
which you can, in fact, use as auto g = std::bind(make_indirect_callable(f), 0.0);.
I should mention that unlike your solution, those requires writing some types out-of-line. This is an unfortunate situation due to the inherent limitations of lambda expressions. Should you wish to stick with what you currently have, I have a minor recommendation that inside the lambda you std::forward<Args>(args)... the parameters. This might prove useful if you ever deal with move-only types.
There's a nice little hack you can use here, which is that INVOKE on a pointer to member function automatically indirects its object argument when passed a pointer or (even) a smart pointer (20.8.2p1 second bullet, definition of INVOKE):
#include <functional>
#include <memory>
#include <iostream>
int main() {
std::shared_ptr<std::function<double(double)>> f
= std::make_shared<std::function<double(double)>>(
[](double d) { return d + 10.0; });
std::function<double()> g = std::bind(
&std::function<double(double)>::operator(), f, 0.0);
std::cout << g() << std::endl;
}
If you don't want to mention explicitly the type of f:
std::function<double()> g = std::bind(
&std::remove_reference<decltype(*f)>::type::operator(), f, 0.0);