2016-04-11 10 views
4

Wie kann der Funktor geändert werden, um als Lambda-Wrapper zu arbeiten? Wie erstellt man Funktor, der Lambda mit erfaßter Variable umhüllt?

template<typename T> 
class F { 
    T f; 
public: 
    F(T t){ 
     f = t; 
    } 
    T& operator()(){ 
    return f; 
    } 
}; 

int main() 
{ 
    int x = 5; 
    F<int (*)(int, int)> f([x](int a, int b){return a+b;}); 
    return 0; 
} 

Der Compiler sagt

error: no matching function for call to 'F<int (*)(int, int)>::F(main()::<lambda(int, int)>)' 
    F<int (*)(int, int)> f([x](int a, int b){return a+b;}); 
+0

Verwenden 'std :: function', zB' std :: function f

auto lambda = [x](int a, int b){return a+b;}; F<decltype(lambda)> f(lambda); // OK 

Um es präzise aussehen zu lassen, können wir Makro verwenden ; 'und dann' F f ([x] (int a, int b) {return a + b;}); ' –

Antwort

1

Ein Lambda kann nicht direkt an einen freien Funktionszeiger umgewandelt werden, wenn es etwas einfängt, nur weil sie sind zwei verschiedene Dinge.

Ein Lambda mit Erfassungswerten muss seinen Status irgendwo speichern, aber ein Funktionszeiger ist nur eine Speicheradresse und bietet daher diese Funktionalität nicht. Also würden Sie etwas tun können

static_cast<int(*)(int,int)>([](int a, int b) { return a+b; }) 

aber das ist nicht Ihr Fall.

Einige Lösungen können sein:

  • keine Funktionszeiger verwenden, sondern verwendet ein std::function<int(int,int>) statt
  • eine freie Funktion bereitzustellen, die das Lambda-ruft (keine gute Lösung in Ihrem Fall meist gemeint werden verwendet, um mit Legacy-Code inerface würde ich sagen
  • eine Template-Funktion verwenden, die die Verpackung von Lambda liefert selbst Zeiger auf Funktion. Ähnlich wie bei der Lösung here
vorgeschlagen
2

Es ist komplizierter ... Intern Lambda-Funktionen, die Variablen erfassen sind keine Funktionen als solche, sind Datenstrukturen. Ich habe keine Lösung gefunden und viele Anfragen und Fragen nicht gelöst, dann habe ich diesen minimalen Code entwickelt, um den Lambda-Zeiger ohne std :: function oder irgendeine andere Standardfunktion oder Abhängigkeit zu umhüllen. Reine C++ 11.

Akzeptiert alle Arten von Lambda-Captures, Argumente durch Verweis, return void und unterstützt Top-Level-Funktionen und Member-Methoden.

// Type checkers 
template<typename _Type> 
struct IsVoid 
{ 
    static const bool value = false; 
}; 

template<> 
struct IsVoid<void> 
{ 
    static const bool value = true; 
}; 

// Callable signature interfce 
template<typename _ReturnType, typename..._ArgTypes> 
struct Callable 
{ 
    typedef _ReturnType ReturnType; 
    typedef _ReturnType (*SignatureType)(_ArgTypes...); 

    virtual _ReturnType operator()(_ArgTypes...args) = 0; 
}; 

// Function and lambda closure wrapper 
template<typename _ClosureType, typename _ReturnType, typename..._ArgTypes> 
struct Closure: public Callable<_ReturnType, _ArgTypes...> 
{ 
    typedef _ClosureType ClosureType; 

    const _ClosureType closureHandler; 

    Closure(const _ClosureType& handler) 
     : closureHandler(handler) 
    { 
    } 

    _ReturnType operator()(_ArgTypes...args) override 
    { 
     if(IsVoid<_ReturnType>::value) 
      closureHandler(args...); 
     else 
      return closureHandler(args...); 
    } 
}; 

// Fuction template selector 
template <typename _FunctionType> 
class Function 
    : public Function<decltype(&_FunctionType::operator())> 
{ 
}; 

// Function, lambda, functor... 
template <typename _ReturnType, typename... _ArgTypes> 
class Function<_ReturnType(*)(_ArgTypes...)> 
{ 
public: 
    typedef Function<_ReturnType(*)(_ArgTypes...)> SelfType; 
    typedef _ReturnType(*SignatureType)(_ArgTypes...); 
    Callable<_ReturnType, _ArgTypes...>* callableClosure; 

    Function(_ReturnType(*function)(_ArgTypes...)) 
     : callableClosure(new Closure<SignatureType, _ReturnType, _ArgTypes...>(function)) 
    { 
    } 

    // Captured lambda specialization 
    template<typename _ClosureType> 
    Function(const _ClosureType& function) 
     : callableClosure(new Closure<decltype(function), _ReturnType, _ArgTypes...>(function)) 
    { 
    } 

    _ReturnType operator()(_ArgTypes... args) 
    { 
     if(IsVoid<_ReturnType>::value) 
      (*callableClosure)(args...); 
     else 
      return (*callableClosure)(args...); 
    } 
}; 

// Member method 
template <typename _ClassType, typename _ReturnType, typename... _ArgTypes> 
class Function<_ReturnType(_ClassType::*)(_ArgTypes...)> 
{ 
public: 
    typedef Function<_ReturnType(_ClassType::*)(_ArgTypes...)> SelfType; 
    typedef _ReturnType(_ClassType::*SignatureType)(_ArgTypes...); 

    SignatureType methodSignature; 

    Function(_ReturnType(_ClassType::*method)(_ArgTypes...)) 
     : methodSignature(method) 
    { 
    } 

    _ReturnType operator()(_ClassType* object, _ArgTypes... args) 
    { 
     if(IsVoid<_ReturnType>::value) 
      (object->*methodSignature)(args...); 
     else 
      return (object->*methodSignature)(args...); 
    } 
}; 

// Const member method 
template <typename _ClassType, typename _ReturnType, typename... _ArgTypes> 
class Function<_ReturnType(_ClassType::*)(_ArgTypes...) const> 
{ 
public: 
    typedef Function<_ReturnType(_ClassType::*)(_ArgTypes...) const> SelfType; 
    typedef _ReturnType(_ClassType::*SignatureType)(_ArgTypes...) const; 

    SignatureType methodSignature; 

    Function(_ReturnType(_ClassType::*method)(_ArgTypes...) const) 
     : methodSignature(method) 
    { 
    } 

    _ReturnType operator()(_ClassType* object, _ArgTypes... args) 
    { 
     if(IsVoid<_ReturnType>::value) 
      (object->*methodSignature)(args...); 
     else 
      return (object->*methodSignature)(args...); 
    } 
}; 

Tests:

#include <iostream> 

class Foo 
{ 
public: 
    int bar(int a, int b) 
    { 
     return a + b; 
    } 
}; 

int someFunction(int a, int b) 
{ 
    return a + b; 
} 

int main(int argc, char** argv) 
{ 
    int a = 10; 
    int b = 1; 

    // Lambda without capturing 
    Function<int(*)(int)> fn1([] (int b) -> int { 
     return b; 
    }); 

    std::cout << fn1(2) << std::endl; // 2 

    // Lambda capturing variable 
    Function<int(*)(int)> fn2([a] (int c) -> int { 
     return a + c; 
    }); 

    std::cout << fn2(-7) << std::endl; // 3 

    // Lambda capturing scope 
    Function<int(*)(int)> fn3([&] (int c) -> int { 
     return a + c; 
    }); 

    std::cout << fn3(-5) << std::endl; // 5 

    // Arguments by reference 
    Function<void(*)(int&, int)> fn4([] (int& d, int f) { 
     d = d + f; 
    }); 

    fn4(a, -3); // Void call 

    std::cout << a << std::endl; // 7 

    // Top level function reference 
    Function<int(*)(int, int)> fn6(someFunction); 

    std::cout << fn6(a, 4) << std::endl; // 11 

    // Member method 
    Foo* foo = new Foo(); 
    Function<int(Foo::*)(int,int)> fn7(foo->bar); 
    std::cout << fn7(foo, a, 8) << std::endl; // 15 
} 

richtig funktioniert wih gcc 4.9.

Danke für Ihre Frage.

+1

[Ich habe Probleme mit diesem Code] (http: // ideone.com/j2Qz1K) – mak

+0

Hum ... Es ist wegen der Code-Optimierung. Ohne das 'g ++ -Ox' [funktioniert es richtig] (http://coliru.stacked-crooked.com/a/ae63c6895124e850). Ich werde bald rezensieren – joas

0

Verwenden Sie eine einfache Problemumgehung mit decltype.

#define DECLARE_F(OBJECT, LAMBDA) \ 
    auto lambda = LAMBDA; \ 
    F<decltype(lambda)> OBJECT(lambda) 

Usage:

DECLARE_F(f, [x](int a, int b){return a+b;}); // 1 per line if used ## __LINE__