Following with the tuple continuation monad, say I define a functor std_tuple to go from the cathegory of the monad-tuple to std::tuple:
auto std_tuple = [](auto... args)
{
return [=](auto f){ return f(std::make_tuple(args...)); };
};
Now we can use monad-tuples in contexts expecting std::tuple:
template<typename... ARGS>
void f( const std::tuple<ARGS...>& t ){}
int main()
{
tuple(1,2,3)(std_tuple)(f);
}
So far so good. Except this doesn't compile. Clang 3.4.1 complains:
note: candidate template ignored: couldn't infer template argument '$auto-1-0'
on the f(t) call inside the std_tuple functor.
Is this correct, are not those template argumments deducible? In case afirmative, why?
A simple case that reproduces your problem:
void f(int) {}
void f(double) {}
template<class T> void call_with_3( T t ) { t(3); }
int main() {
call_with_3( f );
}
Here we can see that which f to call cannot be determined at the point where we pass it to call_with_3. Now, you seemingly don't have multiple overloads (you only have one f!), but...
A template is not an instance. A template function is a factory of functions, not a function.
There is no object or value there to pass around.
When you pass a function name as an argument, overload resolution kicks in. If the target type is known (as a function reference or pointer) it is used to do overload resolution on the function name.
In this case, you are passing a function name to a template (auto argument), so there is no overload resolution that can be done, so no particular value can be found, so you get an error.
You can create an object whose effect is to do overload resolution on the invoked arguments with a given function name. I call them overload set objects.
static struct f_overload_set_t {
template<class... Args>
auto operator()(Args&&... args) const {
return f(std::forward<Args>(args)...);
}
} f_overload_set;
in C++11 you need a ->decltype( f( std::declval<Args>()... ) ) after the const.
Now f_overload_set(blah) will, when invoked will (almost) do what happens when you f(blah), but f_overload_set is an actual object. So you can pass it around.
Macros that generate such overload sets are relatively easy to write. They can also use lambdas, as the above is a lot like a stateless lambda if you think about it.
The nice thing about the stateless lambda based macro overload set generator is that it can be created at point-of-use. From #dyp's comment above:
#define OVERLOAD_SET( FUNC )\
([](auto&&... args){\
return FUNC(std::forward<decltype(args)>(args)...);\
})
(note: no brackets around FUNC, as that blocks ADL). Brackets around everything else, because otherwise if used within a subscript operation (operator[]), it would be parsed as a [[ starting an attribute, among other spots (thanks to #ecatmur))
which makes your code:
template<typename... ARGS>
void f( const std::tuple<ARGS...>& t ){}
int main() {
tuple(1,2,3)(std_tuple)(OVERLOAD_SET(f));
}
Related
Boost.Hana offers boost::hana::is_valid to check whether a SFINAE-friendly expression is valid.
You can use it like this
struct Person { std::string name; };
auto has_name = hana::is_valid([](auto&& p) -> decltype((void)p.name) { });
Person joe{"Joe"};
static_assert(has_name(joe), "");
static_assert(!has_name(1), "");
However, there's a note about the argument to is_valid being a nullary function:
To check whether calling a nullary function f is valid, one should use the is_valid(f)() syntax. […]
How can I even use it by passing to it a nullary function? I mean, if a function is nullary, then how is its body gonna have any dependent context to which SFINAE can apply?
I think that maybe "lambda captures" might have something to do with the answer, but I can't really figure it out how.
Use case is that of checking that f is actually nullary, e.g
if constexpr (is_valid(f)()) {
f(); // Treat f as a "no args function"
} else if constexpr (is_valid(f, arg1)) {
f(arg1);
}
what the documentation says is that unlike functions of non zero arity, the is_valid predicate can only be invoked in the form:
is_valid(f)(); // Invoking "is_valid(f)", i.e. no parentheses, does NOT
// check that f is a nullary function.
reminder: to check whether e.g. a 2 arguments function call is valid you can say:
is_valid(f, arg1, arg2);
// or
is_valid(f)(arg1, arg2)
Take for example the following Demo
void f0()
{
std::cout << "Ok f0\n";
}
void f1(int)
{
std::cout << "Ok f1\n";
}
template <class F>
void test(F fun)
{
if constexpr (hana::is_valid(fun)())
{
fun();
}
else if constexpr (hana::is_valid(fun, 2))
{
fun(2);
}
}
int main() {
test(f0);
test(f1);
}
It may be obvious, but you have to keep in mind that SFINAE does not happen ON f0 or f1. SFINAE happens in the guts of is_valid between the different flavors of is_valid_impl (comments mine):
// 1
template <
typename F, typename ...Args, typename = decltype(
std::declval<F&&>()(std::declval<Args&&>()...)
)>constexpr auto is_valid_impl(int) { return hana::true_c; }
// 2
template <typename F, typename ...Args>
constexpr auto is_valid_impl(...) { return hana::false_c; }
// Substitution error on 1 will trigger version 2
So your question
"I mean, if a function is nullary, then how is its body gonna have any dependent context to which SFINAE can apply?"
has little meaning since SFINAE does not happen on the user provided function. After all, we are setting up nothing to enable SFINAE on our functions. Implementing SFINAE requires to provide more than 1 candidates that "guide" the instantiation process (check SFINAE sono buoni).
The term "SFINAE friendly" here, has to do with f (our function) being usable as type parameter for the SFINAE implementation in is_valid. For example, if in our Demo f was overloaded (replace f1(int) with f0(int)) you would get a substitution error:
<source>:22:6: note: candidate: 'template<class F> void test(F)'
22 | void test(F fun)
| ^~~~
<source>:22:6: note: template argument deduction/substitution failed:
because when the compiler reaches the deepest point of is_valid_impl it tries to instantiate the favorable version (version 1, that doesn't have ... parameter) but it cannot tell what the type of F is and produces a hard error.
For reference, the way SFINAE works is that if it could use type F, the compiler would:
make an attempt on version 1
If successfull return true (so IT IS valid)
If not successfull it would go for version 2 (substitution error is not a failure) which does not use type F as F(Args...) and hence produce a false.
Hi I'm trying to sort out an issue with the following code:
template<typename... Args>
using Func = void (*)(Args... args);
template<typename... Args>
void do_test(Func<Args&...> f, Args&... args) {
for (int i = 0; i != 100; i++)
f(args...);
}
int main(){
int x = 0;
do_test(Func<int&>([](int &y) { y++; }), x); // OK
// Error - mismatched types 'void (*)(Args& ...)' and 'main()::<lambda(int&)>'
do_test([](int &y) { y++; }, x);
return x;
}
https://godbolt.org/z/UaXxFJ
Can anyone explain why it is necessary to wrap the lambda in Func<int&>( )? Is there a way to avoid it? - because if the argument list is non-trivial it becomes quite tedious having to list out the argument types twice.
The goal here is to make a visitor pattern that the compiler can optimize away. I'm using it for an image processing algorithm where I want to reuse the code of the outer loops, with various bits of inner code. The Args are being used as something similar to a lambda capture, except using traditional function pointers so that the compiler can optimize them away - which it seems not to be able to do with std::function<>
You can just allow the function to take any type, whether function pointer or lambda:
template<typename F, typename... Args>
void do_test(F f, Args&... args) {
for (int i = 0; i != 100; i++)
f(args...);
}
Depending on your use case, consider taking f by-const-reference or forwarding reference (i.e. F&&), as well.
You should also consider changing the way you take the args function parameters. Usually in such a situation you would take them by forwarding reference, meaning Args&&... args instead of Args&... args. Otherwise you won't be able to call the function with rvalues arguments.
Or if you have some specific reason to accept only this one specific function pointer type, you can make the first function parameter a non-deduced context, given that the template arguments can already be deduced from the other function parameters:
template<typename... Args>
void do_test(std::type_identity_t<Func<Args&...>> f, Args&... args) {
for (int i = 0; i != 100; i++)
f(args...);
}
std::type_identity_t is a C++20 feature, but can be easily implemented:
template<typename T>
struct type_identity {
using type = T;
};
template<typename T>
using type_identity_t = typename type_identity<T>::type;
Everything left to the scope resolution operator :: in type_identity<T>::type is a non-deduced context and so the first function parameter will not be used to deduce the Args, which in turn means that implicit conversions will be considered (e.g. the lambda to function pointer conversion).
Alternatively, as mentioned by #FrançoisAndrieux in the question comments, you can use the lambda + trick to convert the lambda to a function pointer at the call site:
do_test(+[](int &y) { y++; }, x);
Also note that taking a function pointer of this specific type means that the function can only be called with functions that have exactly this type. For example args is always deduced to a reference type, so any possible function than may be used with this one must take only reference parameters. This is usually not what you want. Usually you want the loose behavior of std::function<R(Args...)>, which can be constructed from any function object that is callable with the specified Args and returns something that can be implicitly converted to R.
Let's consider the following functions:
// run_cb_1(): Explicitly defined prototype
void run_cb_1(const std::function<void(int)> & callback, int p)
{
callback(p);
}
// run_cb_2(): One template parameter
template <typename T>
void run_cb_2(const std::function<void(T)> & callback, const T & t)
{
callback(t);
}
// run_cb_3(): Variable number of template parameters
template <typename ... Args>
void run_cb_3(const std::function<void(Args...)> & callback, const Args & ... args)
{
callback(args...);
}
Now if I want to use these functions as follows:
int main()
{
auto f = [](int a){
std::cout << a << '\n';
};
run_cb_1(f, 5); // OK
run_cb_2(f, 5); // KO --> I understand why
run_cb_2<int>(f, 5); // OK
run_cb_3(f, 5); // KO --> I understand why
run_cb_3<int>(f, 5); // KO --> I don't understand why...
return 0;
}
I get a "no matching function call" with run_cb_2() and run_cb_3() while it works perfectly fine with run_cb_1().
I think it behaves as expected because I did not provided the type for the template argument (since it can not be deduced trivially as it is for run_cb_1()).
But specifying the template type solves the problem for run_cb_2() (as I would expect) but not for run_cb_3().
I know I can solve it either by explicitly declaring f as:
std::function<void(int)> f = [](int a){
std::cout << a << '\n';
};
or by passing f as:
run_cb_2(std::function<void(int)>(f), 5);
run_cb_3(std::function<void(int)>(f), 5);
My question is: Why does the template argument deduction fail with run_cb_3() (with variadic template parameters) even when explicitly specifying the template type(s) ?
It is obvious that I missed something (maybe basic) but I don't know what it is.
Any help will be appreciated.
The reason this fails is because there isn't just one type the compiler can use. When you do
run_cb_2<int>(f, 5);
The compiler looks at run_cb_2 and sees that there is only one template parameter. Since you've provided that it skips the deduction phase and stamps out run_cb_2<int>.
With
run_cb_3<int>(f, 5);
You're in a different boat. run_cb_3 has a variadic template parameter which means just supplying int is not enough to skip the deduction. You specified the first argument, but there could be more so it goes into the argument deduction phase to figure it out. That means it checks callback to make sure what it deduces there matches what it deduces for args. Since the lambda is not a std::function it can't deduce Args... from it. Once that happens the compiler stops and issues an error.
With run_cb_3<int>, you don't explicitly provide full Args..., just the first type; it might have other.
It is used for example in function such as std::make_unique:
template <class T, class... Args>
std::unique_ptr<T> make_unique(Args&&... args);
and
std::make_unique<MyObj>(var1, var2); // T = MyObj
// Args... = [decltype((var1)), decltype((var2))]
Extra args are deduced from argument.
To force evaluation, you might use:
(&run_cb_3<int>)(f, 5); // OK
Demo
The code below doesn't compile (see error below the code). Can you please explain why?
template <class F, class... Arg>
void for_each_argument(F f, Arg&&... arg)
{
f(std::forward<Arg>(arg...));
}
int main()
{
for_each_argument(
[](const auto& a){std::cout<< a;}, "Aa", 3, 4);
return 0;
}
Here is an error message:
7:4: error: expression contains unexpanded parameter pack
'Arg'
f(std::forward(arg...));
You have several issues in your code. First of, your original line
f(std::forward<Arg>(arg...));
Is not correct syntax at all - you are expanding arg without properly expanding Arg in template. Now, you can fix at least that by
f(std::forward<Arg>(arg)...);
This would be better, but still wrong - you will call your lambda once with 3 arguments, while it only accepts one - and instead, you want to call lambda 3 times with a single argument.
There are several ways to do this. First, and the least preferred, is to call the function recursively, as other answer suggests. This prompts ugly syntax, and also adds burden on compiler for recursive template instantiation. Much better solution is to expand the argument using array trick, for example (ignoring the forward for simplicity):
auto lam = [&f](const auto& a) { f(a); return true;}
bool arr[] = { lam(std::forward<ARG>(arg))... };
(void)arr;
In C++ 17 you can use fold expression to achieve even more neat syntax:
(f(std::forward<ARG>(arg)), ...);
Expanding a parameter pack works in contexts that expect a comma separated list.
That is, your code:
f(std::forward<Arg>(arg...));
It attempting to expand into:
f( "Aa", 3, 4 );
And the lambda you have supplied does not support such a call.
To expand a parameter pack into multiple function calls, use a recursive function.
template <class F>
void for_each_argument(F f)
{
// (No args)
}
template <class F, class FirstArg, class... MoreArgs>
void for_each_argument(F f, FirstArg&& first_arg, MoreArgs&&... more_args)
{
f( std::forward<FirstArg>(first_arg) );
for_each_argument( f, std::forward<MoreArgs>(more_args)... );
}
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.