Reputation: 3871
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
Upvotes: 6
Views: 3968
Reputation: 81986
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!";
}
Upvotes: 6