C++20 Callable Concept in Variadic Function Templates - c++

Currently using g++11.3.0, C++20.
I'm trying to compile this bit of code below, where the function foo() is supposed to be able to take an arbitrary number of template arguments, and use them to provide arguments when calling the passed lambda func. I wanted to create a Callable concept that restricts func to only invocables. The code compiles fine as it is. But when I change the line template <typename ...T, typename Func> to template <typename ...T, Callable Func> to use the Callable concept, I get the following error:
error: no matching function for call to ‘foo<int, float, double>(main()::<lambda(int&&, float&&, double&&)>)’
Code:
#include <functional>
template <typename Func, typename ...Args>
concept Callable = requires (Func&& func, Args&&... args)
{
std::invoke(std::forward<Func>(func), std::forward<Args>(args)...);
};
template <typename ...T, typename Func>
void foo(Func&& func)
{
// T() is used just to show that each template argument is used to provide some sort
// of an argument for the lambda.
func(T()...);
}
int main()
{
foo<int, float, double>([] (int&&, float&&, double&&) {});
return 0;
}

Declaring Callable Func will cause the compiler will only check whether Callable<F> is true, that is, whether F can be invoked without arguments.
You need to pass the argument types to Callable to constrain the single Func template parameter, for example
template <typename ...T, Callable<T...> Func>
void foo(Func&& func)
{
// T() is used just to show that each template argument is used to provide some sort
// of an argument for the lambda.
func(T()...);
}

Related

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

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 ) {

Parameter pack must be at the end of the parameter list... When and why?

I don't get the reason for which a parameter pack must be at the end of the parameter list if the latter is bound to a class, while the constraint is relaxed if the parameter list is part of a member method declaration.
In other terms, this one compiles:
class C {
template<typename T, typename... Args, typename S>
void fn() { }
};
The following one does not:
template<typename T, typename... Args, typename S>
class C { };
Why is the first case considered right and the second one is not?
I mean, if it's legal syntax, shouldn't it be in both the cases?
To be clear, the real problem is that I was defining a class similar to the following one:
template<typename T, typename... Args, typename Allocator>
class C { };
Having the allocator type as the last type would be appreciated, but I can work around it somehow (anyway, if you have a suggestion it's appreciated, maybe yours are far more elegant than mine!!).
That said, I got the error:
parameter pack 'Args' must be at the end of the template parameter list
So, I was just curious to fully understand why it's accepted in some cases, but it is not in some others.
Here is a similar question, but it simply explains how to solve the problem and that was quite clear to me.
It is valid for function templates but only when argument deduction can help the compiler resolve the template parameters, as it stands your function template example is virtually useless because
template<typename T, typename... Args, typename S> void fn() { }
int main() { fn<int, int, int>(); }
test.cpp: In function 'int main()':
test.cpp:2:32: error: no matching function for call to 'fn()'
int main() { fn<int, int, int>(); }
^
test.cpp:1:57: note: candidate: template<class T, class ... Args, class S> void fn()
template<typename T, typename... Args, typename S> void fn() { }
^
test.cpp:1:57: note: template argument deduction/substitution failed:
test.cpp:2:32: note: couldn't deduce template parameter 'S'
int main() { fn<int, int, int>(); }
the compiler has no way of determining which template parameters belong to the parameter pack, and which to S. In fact as #T.C. points out it should actually be a syntax error because a function template defined in this manner cannot ever be instantiated.
A more useful function template would be something like
template<typename T, typename... Args, typename S> void fn(S s) { }
as now the compiler is able to unambiguously match the function parameter s with the template type S, with the side effect that S will always be deduced - all explicit template parameters after the first will belong to Args.
None of this works for (primary) class templates, parameters aren't deduced and it's expressly forbidden:
From draft n4567
http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2015/n4567.pdf
[temp.param] / 11
[...]If a template-parameter of a primary class template or alias
template is a template parameter pack, it shall be the last
template-parameter.[...]
(if they were deduced it would be ambiguous as in the function template example).
The first one is not right. The compiler is just buggy and failed to diagnose it. [temp.param]/11:
A template parameter pack of a function template shall not be followed
by another template parameter unless that template parameter can be
deduced from the parameter-type-list of the function template or has a
default argument (14.8.2).
If the function type T(Args...) is meaningful to the end-user, one way to fix this would be to use a partial specialization instead:
template<class F, class Alloc> class C; //undefined
template<class T, class... Args, class Alloc>
class C<T(Args...), Alloc> {
// implementation
};
Depending on the actual requirements, type-erasing the allocator might also be worth considering.

C++ Multiple variadic template not binded to function arguments

I was trying to use some variadic template parameters but I was quickly stopped by an error I didn't managed to understand.
#include <tuple>
template <typename T>
struct Foo
{
typedef T type;
};
// return a tuple of pair of args and Foo templated on Types
template <typename Head, typename ...Args, typename Type, typename ...Types>
auto func(Head arg, Args... args)
{
return std::tuple_cat(std::make_tuple(std::make_pair(arg, Foo<Type>())),
func<Args..., Types...>(args...));
}
template <typename Head, typename Type>
auto func(Head arg)
{
return std::make_tuple(std::make_pair(arg, Foo<Type>()));
}
int main()
{
func<int, bool, char>(1, 2, 3);
}
Here func try to unpack template parameters and make a tuple of pair of a func argument and a Foo struct templated on the second variadic template but I am having:
test.cc:25:3: error: no matching function for call to 'func'
func<int, bool, char>(1, 2, 3);
^~~~~~~~~~~~~~~~~~~~~
test.cc:11:6: note: candidate template ignored: couldn't infer template argument 'Type'
auto func(Head arg, Args... args)
^
test.cc:18:6: note: candidate function template not viable: requires single argument 'arg', but 3
arguments were provided
auto func(Head arg)
^
1 error generated.
Why Type cannot be inferred ? (gcc told me the same)
I am sure that multiple variadic template is possible after seing std::tuple_cat implementation (https://gcc.gnu.org/onlinedocs/libstdc++/libstdc++-api-4.5/a01066_source.html) and I think this is a simple example and someone should knows better that me if there is a solution or if the standard doesn't accept this.
Thanks for your help,
Put deduced arguments last.
... template arguments are greedy -- they will consume passed in arguments and wont "save" any for later template arguments.
Once that is done, deduction occurs from non-template function arguments.
Swap like this:
template <typename Type, typename ...Types, typename Head, typename ...Args>
auto func(Head arg, Args... args)
Also, get rid of the other overload, so func<int,int>(3) is not ambiguous.
This makes the recursion break, but that is easy to fix:
template <class... Types, class... Args>
auto func(Args... args)
{
return std::make_tuple(std::make_pair(args, Foo<Types>())...);
}
Which has the bonus of being nice and short.
A key data point here is that a variadic parameter will be matched by an empty list. This has several consequences.
The first consequence is that given the following template declaration:
template <typename Head, typename ...Args, typename Type, typename ...Types>
and the following instantiation:
<int, bool, char>
There are several ways the instantiation can match the template:
1.
Head int
...Args bool
Type char
... Types (empty list)
2.
Head int
...Args (empty list)
Type bool
... Types char
So, that's one source of ambiguity. The reference in main() can match either template definition. Although there are various arcane rules that in some instances can resolve this kind of ambiguity, it's always better not to rely on arcane template disambiguation rules, or at least go no further than use several standard, basic SFINAE approaches.
But, back on topic, right off the bat the compiler is going to be behind an eight-ball here, not having a clear candidate to resolve the template reference in main(). But it gets worse. Given
auto func(Head arg, Args... args)
and
auto func(Head arg)
Because, once again, a variatic parameter, in this case it's Args..., can match an empty list, then, for example:
func(4)
Can match either function signature.
Combine all of these factors together, and the compiler can't really make heads or tails here.

explicit call to variadic function template with empty parameter pack

Consider this simple (bad) function template for which lots of variations exist on this site:
template <typename R, typename... Args>
R call_with(std::function<R(Args...)> f,
Args... args)
{
return f(args...);
}
And two attempts at calling it:
call_with([]{}); // (a)
call_with<void>([]{}); // (b)
I cannot call (a) because a lambda is not a std::function<R(Args...)> so template deduction fails. Straightforward.
However, (b) also fails. I suspect this is because the compiler cannot determine that I mean to provide all the type arguments and reasons that I am simply providing R - so it is trying (and failing) to deduce Args... for the same reason that the initial call failed.
Is there a way to explicitly specify that I am providing all the template arguments? To clarify, I am interested only in how to explicitly provide the template arguments so that there is no template deduction - I am not looking for the correct way to write call_with or for a way to make template deduction succeed when called with a lambda.
The short answer to your--edited--question is: if you cannot change the declaration of call_with(), then either use the type casts demonstrated by #CoffeeandCode, or use the technique described below to create a wrapper for call_with().
The problem is in the fact that the compiler is trying to deduce the template arguments from the first function argument. You can prevent this if you write your code like this:
#include <functional>
#include <iostream>
// identity is a useful meta-function to have around.
// There is no std::identity, alas.
template< typename T>
struct identity
{
using type = T;
};
template <typename R, typename... Args>
R call_with( typename identity<std::function<R(Args...)>>::type f,
Args... args)
{
return f(args...);
}
int main()
{
call_with<void>([](int){ std::cout << "called" << std::endl; }, 2);
}
Using a template meta-function to "generate" the std::function type means that the compiler cannot even try to deduce the function type from the first argument and it will only use the other arguments.
You would still need to explicitly provide the return type of course, but for the other arguments you can now choose whether to explicitly specify them or leave it up to the compiler to deduce them from the arguments given.
If you really want to enforce that all template arguments are provided and not deduced, you can also wrap the argument pack in an invocation of identity this way:
template <typename R, typename... Args>
R call_with( typename identity<std::function<R(Args...)>>::type f,
typename identity<Args>::type... args)
In summary, if you want to prevent the compiler from deducing function template argument types that also appear as function parameters, wrap them in a meta function such as identity.
You could specify the function type beforehand, like this:
int main(){
std::function<void()> f = []{};
call_with(f);
}
or, in a little more messy but more compact way:
int main(){
call_with(static_cast<std::function<void()>>([]{}));
}
This is because the compiler doesn't know what return type and arguments to deduce for your template parameters before you ask it to implicitly convert a lambda, which is an unspecified function object defined by the compiler, to a std::function with those template parameters.
Really, you should just change your function warpper to be more generic:
template<typename Functor, typename ... Args>
auto wrapper(Functor &&f, Args &&... args) -> decltype(f(std::forward<Args>(args)...)){
return f(std::forward<Args>(args)...);
}
This should work for any function or functor type. It's also a really good example of the use of trailing return types.
Here's a live example

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);
}