Normally, to test a if a pointer points to function, use std::is_function
is enough.
However, it cannot work with lambda. Since lambda is an object with operator()
.
Now I have to use both is_function
and is_object
to check if one works like function, as below:
std::is_function<decltype(f)>::value || std::is_object<decltype(f)>::value
So I'm wondering if there is a better way to test if one is lambda or not?
EDIT:
Related code:
template<typename Func>
void deferJob(Func f, int ms=2000)
{
if(! std::is_function<decltype(f)>::value
&& ! std::is_object<decltype(f)>::value){
qDebug()<<"Not function!";
return;
}
QTimer* t = new QTimer;
t->setSingleShot(true);
QObject::connect(t, &QTimer::timeout,
[&f, t](){
qDebug()<<"deferJob";
f();
t->deleteLater();
});
t->start(ms);
}
EDIT2:
Similar question: C++ metafunction to determine whether a type is callable
C++ Lambda expression allows us to define anonymous function objects (functors) which can either be used inline or passed as an argument. Lambda expression was introduced in C++11 for creating anonymous functors in a more convenient and concise way.
Significance of Lambda Function in C/C++ Lambda Function − Lambda are functions is an inline function that doesn't require any implementation outside the scope of the main program. Lambda Functions can also be used as a value by the variable to store.
In C++11 and later, a lambda expression—often called a lambda—is a convenient way of defining an anonymous function object (a closure) right at the location where it's invoked or passed as an argument to a function.
A lambda is also just a function object, so you need to have a () to call it, there is no way around it (except of course some function that invokes the lambda like std::invoke ). If you want you can drop the () after the capture list, because your lambda doesn't take any parameters.
So here are some thoughts that may or may not be helpful.
To create a type_trait that works for functors, lambdas and traditional functions, I think I would look into seeing if the template argument is convertible into a std::function<void()>
. I think that would cover most bases in a clear way.
As we've mentioned in the comments, you can't test a template argument like the way you are doing. The f()
later in the function will cause a compile error, and so you'll never have the opportunity to see the runtime error.
You can try to do something with std::enable_if
. You'd need to create template specializations so that SFINAE can function to choose the correct implementation. This would use that type_trait that I mentioned in bullet 1.
If you did this, you could make the implementation of the other template to be a static_assert
to create a "better" error message.
That being said, the compiler error messages aren't that bad in the first place. (At least in clang and gcc. I haven't looked as msvc).
This doesn't get you a great error message, but it does get you a different one:
#include <cassert>
#include <functional>
#include <type_traits>
template <typename Func>
typename std::enable_if<std::is_convertible<Func, std::function<void()>>::value>::type
deferJob(Func f, int ms=2000) {
}
void normal_function() {}
int main() {
deferJob([]() {}); // works
deferJob(&normal_function); // works
deferJob(3); // compile time error
}
In Clang, I get an error that looks like:
foo.cc:15:2: error: no matching function for call to 'deferJob'
deferJob(3); // compile time error
^~~~~~~~
foo.cc:6:25: note: candidate template ignored: disabled by 'enable_if' [with Func = int]
typename std::enable_if<std::is_convertible<Func, std::function<void()>>::value>::type
In GCC, I get an error that looks like:
foo.cc: In function ‘int main()’:
foo.cc:15:12: error: no matching function for call to ‘deferJob(int)’
deferJob(3); // compile time error
^
foo.cc:15:12: note: candidate is:
foo.cc:7:1: note: template<class Func> typename std::enable_if<std::is_convertible<Func, std::function<void()> >::value>::type deferJob(Func, int)
deferJob(Func f, int ms=2000) {
^
foo.cc:7:1: note: template argument deduction/substitution failed:
foo.cc: In substitution of ‘template<class Func> typename std::enable_if<std::is_convertible<Func, std::function<void()> >::value>::type deferJob(Func, int) [with Func = int]’:
foo.cc:15:12: required from here
foo.cc:7:1: error: no type named ‘type’ in ‘struct std::enable_if<false, void>’
We could go one step further (although doing it this way makes it hard to extend further) and add an additional function:
template <typename Func>
typename std::enable_if<not std::is_convertible<Func, std::function<void()>>::value>::type
deferJob(Func f, int ms=2000) {
static_assert(false, "You should pass a function");
}
This causes clang to report (at compile time):
foo.cc: In function ‘typename std::enable_if<(! std::is_convertible<Func, std::function<void()> >::value)>::type deferJob(Func, int)’:
foo.cc:14:2: error: static assertion failed: You should pass a function
static_assert(false, "You should pass a function");
But sadly, it doesn't give a stack trace, so I would find this far less helpful than any of the earlier messages.
And finally, we could also replace that static assert with your runtime message:
template <typename Func>
typename std::enable_if<not std::is_convertible<Func, std::function<void()>>::value>::type
deferJob(Func f, int ms=2000) {
qDebug() << "Not function!";
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With