C++ Passing std::function object to variadic template - c++

I want to pass a callable (std::function object) into a class Foo. The callable refers to a member method of another class which has arbitrary arguments, hence the Foo must be a variadic template. Consider this code:
struct Bar {
void MemberFunction(int x) {}
};
template<typename ...Args>
class Foo {
public:
Foo(std::function<void(Bar*, Args...)> f) {}
};
int main() {
Foo<int> m1(&Bar::MemberFunction);
return 0;
}
This compiles fine. Now I want to write a factory function MakeFoo() which returns a unique_ptr to a Foo object:
template<typename ...Args>
std::unique_ptr<Foo<Args...>> MakeFoo(std::function<void(Bar*, Args...)> f) {
return std::make_unique<Foo<Args...>>(f);
}
Using this function by calling
auto m2 = MakeFoo<int>(&Bar::MemberFunction);
in main, gives me the following compiler errors:
functional.cc: In function ‘int main()’:
functional.cc:21:50: error: no matching function for call to ‘MakeFoo(void (Bar::*)(int))’
auto m2 = MakeFoo<int>(&Bar::MemberFunction);
^
functional.cc:15:35: note: candidate: template<class ... Args> std::unique_ptr<Foo<Args ...> > MakeFoo(std::function<void(Bar*, Args ...)>)
std::unique_ptr<Foo<Args...>> MakeFoo(std::function<void(Bar*, Args...)> f) {
^
functional.cc:15:35: note: template argument deduction/substitution failed:
functional.cc:21:50: note: mismatched types ‘std::function<void(Bar*, Args ...)>’ and ‘void (Bar::*)(int)’
auto m2 = MakeFoo<int>(&Bar::MemberFunction);
It seems to me, that when I call the constructor of Foo, the compiler happily converts the function pointer &Bar::MemberFunction to a std::function object. But when I pass the same argument to the factory function, it complains. Moreover, this problem only seems to occur, when Foo and MakeFoo are variadic templates. For a fixed number of template parameters it works fine.
Can somebody explain this to me?

Why doesn't it work without explicit <int>?
Prior to C++17, template type deduction is pure pattern matching.
std::function<void(Foo*)> can store a member function pointer of type void(Foo::*)(), but a void(Foo::*)() is not a std::function of any kind.
MakeFoo takes its argument, and pattern matches std::function<void(Bar*, Args...)>. As its argument is not a std::function, this pattern matching fails.
In your other case, you had fixed Args..., and all it had to do was convert to a std::function<void(Bar*, Args...)>. And there is no problem.
What can be converted to is different than what can be deduced. There are a myriad of types of std::function a given member function could be converted to. For example:
struct Foo {
void set( double );
};
std::function< void(Foo*, int) > hello = &Foo::set;
std::function< void(Foo*, double) > or_this = &Foo::set;
std::function< void(Foo*, char) > why_not_this = &Foo::set;
In this case there is ambiguity; in the general case, the set of template arguments that could be used to construct some arbitrary template type from an argument requires inverting a turing-complete computation, which involves solving Halt.
Now, C++17 added deduction guides. They permit:
std::function f = &Foo::set;
and f deduces the signature for you.
In C++17, deduction doesn't guides don't kick in here; they may elsewhere, or later on.
Why doesn't it work with explicit <int>?
Because it still tries to pattern match and determine what the rest of Args... are.
If you changed MakeFoo to
template<class T>
std::unique_ptr<Foo<T>> MakeFoo(std::function<void(Bar*, T)> f) {
return std::make_unique<Foo<T>>(f);
}
suddenly your code compiles. You pass it int, there is no deduction to do, and you win.
But when you have
template<class...Args>
std::unique_ptr<Foo<Args...>> MakeFoo(std::function<void(Bar*, Args...)> f) {
return std::make_unique<Foo<T>>(f);
}
the compiler sees <int> and says "ok, so Args... starts with int. What comes next?".
And it tries to pattern match.
And it fails.
How can you fix it?
template<class T>struct tag_t{using type=T; constexpr tag_t(){}};
template<class T>using block_deduction=typename tag_t<T>::type;
template<class...Args>
std::unique_ptr<Foo<Args...>> MakeFoo(
block_deduction<std::function<void(Bar*, Args...)>> f
) {
return std::make_unique<Foo<T>>(f);
}
now I have told the compiler not to deduce using the first argument.
With nothing to deduce, it is satisfied that Args... is just int, and... it now works.

The compiler cannot deduce the template arguments for std::function from a different type, such as member function pointer. Even though a std::function can be constructed from on object of that type, to consider the constructor the template arguments of std::function must be first known.
To help it deduce, add another overload:
template<typename ...Args>
std::unique_ptr<Foo<Args...>> MakeFoo(void(Bar::*f)(Args...)) {
return std::make_unique<Foo<Args...>>(f);
}

MakeFoo<int>( whatever );
is equivalent to invoking the hypothetical
template<typename ...Tail>
std::unique_ptr<Foo<int,Tail...>> MakeFoo( std::function<void(Bar*,int,Tail...)> f) {
return std::make_unique<Foo<int,Tail...>>(f);
}
clearly, in no way the compiler can deduce that Tail is empty given a void(Bar::*)(int)
IMO, the most correct fix ( given the required usage ) is to make the args non deduced:
template< typename T >
struct nondeduced { using type = T; };
template<typename ...Args>
std::unique_ptr<Foo<Args...>> MakeFoo( std::function<void(Bar*, typename nondeduced<Args>::type... )> f ) {

Related

Why does template parameter unpacking sometimes not work for std::function?

I encountered a problem. When I use something like std::function<A(Fs...)> it doesn't work, but std::function<A(Fs..., B)> does work. This is under Clang 8.0; none of it works under GCC.
Here is the example:
#include <functional>
template<typename A, typename B, typename ...Fs>
void func_tmpl1(std::function<A(Fs..., B)> callable)
{
}
template<typename A, typename ...Fs>
void func_tmpl2(std::function<A(Fs...)> callable)
{
}
class Cls1{};
void func0(std::function<void(float, Cls1)> callable)
{
}
int main()
{
std::function<void(float, Cls1)> f1 = [](float a, Cls1 b){};
func0(f1);
func0([](float a, Cls1 b){});
func_tmpl1<void, Cls1, float>(f1); // fails in GCC
func_tmpl2<void, float, Cls1>(f1);
func_tmpl1<void, Cls1, float>( // fails in GCC
[](float a, Cls1 b)
{
}
);
func_tmpl2<void, float, Cls1>( // fails in both
[](float a, Cls1 b)
{}
);
return 0;
}
On Godbolt, we can see GCC always fails, but Clang only fails at the last function call. Can anyone explain what's happening here?
For convenience, let's call the three failed calls in your code #1, #2 and #3.
The problem is, when template arguments corresponding to a template parameter pack are explicitly specified, does the template parameter pack still participates in template argument deduction, and if it does, does deduction fail makes the whole call ill-formed?
From [temp.arg.explicit]/9:
Template argument deduction can extend the sequence of template
arguments corresponding to a template parameter pack, even when the
sequence contains explicitly specified template arguments.
We can infer that the template argument deduction should still be performed.
In the declaration of func_tmpl1, std::function<A(Fs..., B)> is a non-deduced context ([temp.deduct.type]/9: "If the template argument list of P contains a pack expansion that is not the last template argument, the entire template argument list is a non-deduced context."), so template argument deduction for Fs should be ignored and #1 and #2 are both well-formed. There is a GCC bug report.
For #3, template argument deduction obviously fails (std::function<A(Fs...)> vs a lambda type), but does deduction fail really make the code ill-formed? In my opinion, the standard is unclear about this, and there is a related issue. From the response of CWG, #3 is indeed ill-formed.
That looks like a compiler bug; the compiler tries template argument deduction when all the arguments are already explicitly specified and hence no deduction is necessary.
Or maybe the bug is with substitution, which should succeed.
According to the standard, it is possible to explicitly specify variadic pack arguments. See an example in [temp.arg.explicit]/5:
template<class ... Args> void f2();
void g() {
f2<char, short, int, long>(); // OK
}
When all template arguments are known, the compiler is supposed to simply instantiate the template and be done with it; overload resolution then proceeds normally.
To work around the issue we can disable template argument deduction by introducing a non-deduced context. For example like this:
template<typename T> using no_deduce = typename std::common_type<T>::type;
template<typename A, typename B, typename ...Fs>
void func_tmpl1(no_deduce<std::function<A(Fs..., B)>> callable)
{
}
template<typename A, typename ...Fs>
void func_tmpl2(no_deduce<std::function<A(Fs...)>> callable)
{
}
(The ::type here is a dependent type and becomes a non-deduced context)
Now it compiles fine in g++ and clang++. link to coliru
Having said that, note that std::function is primarily meant for type erasure and is a costly abstraction as it incurs an extra indirection at run time and is a heavy object to pass around since it tries to store a copy of any possible functor while avoiding heap allocation (which often still takes place - then it's a large empty object plus a heap allocation).
Since your functions are already templates, you don't really need type erasure; it is easier and more efficient to just take callable as a template argument.
template<typename Func>
void func_tmpl(Func callable) // that's all
{
}
Or, if you have to differentiate by callable arguments, can use some SFINAE:
#include <functional>
class Cls1{};
template<typename A, typename B, typename ...Fs, typename Func,
typename = std::enable_if_t<std::is_invocable_r_v<A, Func, Fs..., B> > >
void func_tmpl1(Func callable)
{
}
template<typename A, typename B, typename ...Fs, typename Func,
typename = std::enable_if_t<std::is_invocable_r_v<A, Func, B, Fs...> > >
void func_tmpl2(Func callable)
{
}
void func0(std::function<void(float, Cls1)> callable)
{
}
int main()
{
std::function<void(float, Cls1)> f1 = [](float a, Cls1 b){};
func0(f1); // func0 is not a template - so it requires type erasure
func0([](float a, Cls1 b){});
func_tmpl1<void, Cls1, float>(f1); // #1 OK
func_tmpl2<void, float, Cls1>(f1); // #2 OK
func_tmpl1<void, Cls1, float>([](float a, Cls1 b) {}); // #3 OK
func_tmpl2<void, float, Cls1>([](float a, Cls1 b) {}); // #4 OK
return 0;
}
link to coliru

Constexpr alias of overloaded template function

Trying to alias make_shared on a specific class type for a specific constructor of that class. My best attempt:
class foo { public: foo(int x) : y(x) {} int y; };
constexpr auto newfoo = static_cast<std::shared_ptr<foo>(*)(int)>(std::make_shared<foo>);
Yields:
error: invalid static_cast from type ‘<unresolved overloaded function type>’ to type ‘std::shared_ptr<foo> (*)(int)’
constexpr auto newfoo = static_cast<std::shared_ptr<foo>(*)(int)>(std::make_shared<foo>);
What am I doing wrong?
std::make_shared is a variadic function template. You are only specifying <foo> as a template parameter, but you would also need an int somewhere in there. Regardless, your approach is bound to fail as it's reliant on how make_shared's template arguments were laid out and because it's generally cumbersome to work with overload sets in C++.
What I suggest is to create a wrapper function instead:
constexpr auto newfoo(int x)
{
return std::make_shared<foo>(x);
}
In my opinion it is easier to write, read, and understand. If you really need SFINAE-friendliness and noexcept, you can repeat the body three times:
constexpr auto newfoo(int x)
-> decltype(std::make_shared<foo>(x))
noexcept(noexcept(std::make_shared<foo>(x)))
{ return std::make_shared<foo>(x); }
A macro can be used to make the above declaration less painful.
If you really want a function pointer, this seems to work:
auto newfoo =
static_cast<std::shared_ptr<foo>(*)(const int&)>(
&std::make_shared<foo, const int&>);
Look at make_shared's declaration:
template< class T, class... Args >
shared_ptr<T> make_shared( Args&&... args );
You need to supply T=foo and something for Args.... Since Args... is a forwarding reference pack, it will always either deduce to lvalue references or rvalue references. This is why <foo, const int&> is a valid set of template parameters and <foo, int> is not.
As Zefick pointed out in the comments, all of this can be simplified to:
constexpr auto newfoo = &std::make_shared<foo, const int&>;
The cast is not really needed here.

Make a code "forwarding referencable"

I opened this post about forwarding reference, this is a (hopefully) MCVE code:
#include <functional>
#include <vector>
using namespace std;
struct MultiMemoizator {
template <typename ReturnType, typename... Args>
ReturnType callFunction(std::function<ReturnType(Args...)> memFunc, Args&&... args) {
}
};
typedef vector<double> vecD;
vecD sort_vec (const vecD& vec) {
return vec;
}
int main()
{
vecD vec;
std::function<vecD(const vecD&)> sortFunc(sort_vec);
MultiMemoizator mem;
mem.callFunction<vecD, vecD>(sortFunc, vec);
}
Since this is not the whole code, maybe I'll have to add extra code based on the answers.
Anyway, as was suggested in this answer, forwarding reference is not possible with this version, since Args is not deduced.
So my question is: is it possible to make this code "forwarding referencable"?
In order to perfect-forward your arguments, you need to have the types deduced. You can do this by deducing the arguments to the function and the parameters to the functor separately:
template <typename ReturnType, typename... FunArgs, typename... Args>
ReturnType callFunction(std::function<ReturnType(FunArgs...)> memFunc,
Args&&... args)
{
//...
}
Then you can call callFunction without template parameters and have everything deduced:
mem.callFunction(sortFunc, vec);
I will add a bit of details regarding #TartanLlama answer on why your code fails to compile (even without the explicit template parameters) but also why (in my own opinion) your code is dangerous.
In the following, I will use only a simple type T instead of your parameter pack Args... because it is simpler to explain and does not change the meaning.
A bit of reminder on forwarding references...
First, let's take a simpler example than yours with simply the following:
template <typename T>
void f (T&&);
Now, let's instanciate f from various sources, let's assume with have the following variables:
std::string s;
const std::string cs;
...then:
f(s); // instanciate f<std::string&>
f(cs); // instanciate f<const std::string&>
f(std::string()); // instanciate f<std::string&&>
You should be wondering: Why is the first instanciation f<std::string&> instead of f<std::string>?, but the standard tells you (§14.8.2.1#3 [temp.deduct.call]):
If P is a forwarding reference and the argument is an
lvalue, the type “lvalue reference to A” is used in place of A for type deduction.
Back to our initial snippet!
Now, let's complicate a bit our example:
template <typename T>
struct A {};
template <typename T>
void f (A<T>, T&&);
And one instantiation:
std::string s;
A<std::string> as;
f(as, s);
The above is equivalent to your example, and will fails to compile, but why... ? Well, as explained above, when you have an lvalue, the deduced type for T&& is T&, not T, and thus the type deduction fails for A<T> because the compiler is expecting A<std::string&> and you are giving a A<std::string>.
So now we know that we have to do the following:
A<std::string&> ars;
A<std::string const&> acrs;
f(ars, s); // good
f(acrs, cs); // good
Why is it dangerous?
Ok so now, this should be good:
A<std::string&&> arrs;
f(arrs, std::string());
But it is not... Because when T is deduced as a rvalue reference, T is simply T, so the compiler is expecting A<std::string>.
So here is the problem: You are going to give a rvalue to a method that is going to forward it to a function expecting an lvalue. That's not wrong, but it is probably not what you'd have expected.
How to deal with it?
The first possibility is to force the type of the first parameter regardless of the deduced type for T, e.g.:
template <typename T>
void f (A<typename std::remove_reference<T>::type>, T&&);
But note:
You would have to add more stuff to deal with const.
One may wonder the usefulness of T&& when the type of the first argument is fixed (in your case, at least).
The second possibility (warning: I don't know if this is standard!) is to move the first parameter at the end and then deduce the type from t:
template <typename T>
void f (T &&t, A<decltype(std::forward<T>(t))>);
Now you have an exact match between the deduced type for T and the expected type for A.
Unfortunately I don't know how to make the above work with variadic templates...

Implicit template argument is invalid, but compiles anyway

I have the following class:
class FunctionCallback
{
public:
static CallbackHandle Create(const std::function<void(void)> &function);
template<typename T,typename... TARGS>
static CallbackHandle Create(const std::function<T(TARGS...)> &function);
};
I then call 'Create' like this:
FunctionCallback::Create<void,float>([](float f) -> void {}); // Whether I use a lambda-function or a function pointer makes no difference
Even though that should be correct (?), visual studio underlines that line in read with the message:
Error: no instance of overloaded function "FunctionCallback::Create" matches the argument list
argument types are: (lambda []void (float f)->void)
However the program compiles fine in visual studio without any warnings or errors.
g++-5 is unable to compile it altogether with a similar message.
When changing it to:
FunctionCallback::Create<void,float>(std::function<void(float)>([](float f) -> void {}));
It doesn't display the message anymore and compiles both on windows and linux.
Why can't it deduce the type properly unless I explicitly specify it?
You almost never want to deduce the type of a type-eraser, that std::function is an example of. With a few exceptions, template type deduction attempts to deduce the exact type of an argument. For a std::function<T>-type parameter this means that the compiler can deduce T if the corresponding argument is a std::function as well. The type of a lambda expression is not a std::function.
Even if you explicitly specify type template arguments, the deduction still continues after corresponding types have been substituted with the arguments. In the case of a parameter pack, the compiler still tries to deduce the rest of arguments, so for FunctionCallback::Create<void,float> the compiler ends up with:
template <typename... TARGS>
static CallbackHandle Create(std::function<void(float, TARGS...)> function);
and this won't match anything else than std::function<void(float, TARGS...)> (or something that derives from this type); otherwise the deduction for TARGS fails.
If your goal is to always specify the parameter pack's types explicitly, you can put that pack (or the entire parameter) in a non-deduced context:
template <typename T> struct identity { using type = T; };
template <typename T> using identity_t = typename identity<T>::type;
struct FunctionCallback
{
template <typename T, typename... TARGS>
static void Create(identity_t<std::function<T(TARGS...)>> function);
};
FunctionCallback::Create<void, float>([](float f) -> void {});
DEMO
In this case, however, you'll have to pay the price of type-erasure. Instead, you can consider accepting any function object type:, letting the compiler to deduce the exact type of the argument:
struct FunctionCallback
{
template <typename F>
static void Create(F function){}
};
FunctionCallback::Create([](float f) -> void {});
DEMO 2

Pass template functor to a template std::function

Here is a code snippet:
#include <functional>
#include <iostream>
#include <memory>
template <typename T>
using Callback = std::function<void(const T)>;
template <typename T>
void Call(const T yyy, const Callback<T>& callback) {
callback(yyy);
}
template <typename T>
class MyCallback {
public:
explicit MyCallback(const T counter_init) : counter_{counter_init} {}
void operator ()(const T xxx) {
std::cout << counter_ << '\t' << xxx << std::endl;
++counter_;
}
private:
T counter_;
};
int main() {
const auto callback = std::make_unique<MyCallback<int>>(0);
Call(111, *callback); // expected output is "0 \t 111"
Call(222, *callback); // expected output is "1 \t 222"
return 0;
}
Clang says it couldn't match std::function against MyCallback and g++ is thinks that MyCallback is derived from Callback.
clang++ -std=c++14 main.cpp && ./a.out
g++ -std=c++14 main.cpp && ./a.out
The easiest way I know that will fix this issue is to use template instead of Callback, so that Call will be defined in the following manner:
template <typename T, typename F>
void Call(const T yyy, F&& callback) {
callback(yyy);
}
But it would be unclear for the next developer which signature does callback has.
Can some one clarify what is going on in the first example from compiler point of view and how can I fix this without applying hack I described above?
Change Call to:
template <class T, class F,
class R=typename std::result_of<F&(const T&)>::type
>
void Call(const T& yyy, F&& f) {
f(yyy);
}
now we invoke f on yyy, and (assuming you have a C++11 compiler that implements SFINAE safe result_of) the overload only works if you can call f on yyy.
std::function is not a generic callable. It is a way to type-erase callables down to "being called with a given signature", copy the callable, and destroy the callable.
Type-erasure and type-deduction are opposite operations. Doing both in one step is usually a sign of a design flaw. std::function should only extremely rarely be deduced from the signature of a passed-in callable.
Instead, determine how you are going to use a given function. Then type-erase down to that use signature, or just test against that use signature and don't type-erase at all.
If you have multiple possible use-signatures, test against each of them, tag dispatch to the right type-erasure path, and type erase there.
The result_of clause is optional, but it improves error messages significantly. It also makes the error detectible "early" as a failed overload, instead of as a hard error. The result_of clause can be converted to a static_assert in the body that would generate an even clearer error message, but that would fail "late" after overload resolution.
An alternative approach, that blocks deduction on the function, is:
template<class T>struct tag{using type=T;};
template<class Tag>using type_t=typename Tag::type;
template<class T>using block_deduction=type_t<tag<T>>;
template <typename T>
using Callback = block_deduction<std::function<void(const T)>>;
and now
template <class T>
void Call(const T& yyy, const Callback<T>& callback) {
callback(yyy);
}
works. It still erases the callback type needlessly (with the resulting overhead).
Deduction is blocked by round-tripping through a struct and getting the ::type. Under the standard, such dependent types are never deduced.
std::function<void(const T)> can be value-constructed with *callback. The problem is Call is a function template. The compiler is unable to automatically instantiate void Call(const int yyy, const Callback<int>& callback) from Call(111, *callback);, since it does not match the call. Conversion is necessary to call the instantiation with Call(111, *callback);. But automatic template argument deduction does not consider such conversions. It considers exact match only. You could manually instantiate it though. So, one way to correct your code would be changing
Call(111, *callback); // expected output is "0 \t 111"
Call(222, *callback); // expected output is "1 \t 222"
to
Call<int>(111, *callback); // expected output is "0 \t 111"
Call<int>(222, *callback); // expected output is "1 \t 222"
As such, a temporary std::function<void(const T)> if first value-constructed with *callback. The function parameter const Callback<int>& callback in Call is then bound to this temporary. Again, this is all about conversion. The function signature is not an exact match for the call.
From [temp.arg.explicit]:
Implicit conversions (Clause 4) will be performed on a function argument to convert it to the type of the
corresponding function parameter if the parameter type contains no template-parameters that participate
in template argument deduction. [ Note: Template parameters do not participate in template argument
deduction if they are explicitly specified. For example,
template<class T> void f(T);
class Complex {
Complex(double);
};
void g() {
f<Complex>(1); // OK, means f<Complex>(Complex(1))
}
—end note ]
In your case, const Callback<T>& does contain a template-parameter that participates in template argument deduction, so no implicit conversion (i.e. MyCallback<int> to std::function<void(const int)>) is allowed.
In order to use std::function, you'd have to have the callback argument not participate in any template argument deduction. That is:
Call<int>(111, *callback); // no argument deduction done here
Alternatively, you could just deduce callback itself, no type-erasure necessary:
template <typename T, typename F>
void Call(const T yyy, F&& callback) {
callback(yyy);
}
Lastly, if you really want the type erasure, you could manually construct your Callback internally to Call:
template <typename T, typename F>
void Call(const T yyy, F&& f) {
Callback<T> callback(std::forward<F>(f));
callback(yyy);
}