Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to test lambda in C++11

Tags:

c++

c++11

lambda

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

like image 798
liuyanghejerry Avatar asked Aug 07 '13 15:08

liuyanghejerry


People also ask

What is lambda function in C++11?

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.

Does C have lambda function?

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.

What does lambda mean in C?

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.

How do you call lambda in C++?

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.


1 Answers

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!";
}
like image 159
Bill Lynch Avatar answered Oct 03 '22 23:10

Bill Lynch