functional funciones examples c++ c++11 lambda function-pointers

funciones - std c++



Pasando capturando lambda como puntero de funciĆ³n (7)

Como lo mencionaron los demás, puede sustituir la función Lambda en lugar del puntero de función. Estoy usando este método en mi interfaz C ++ para F77 ODE solucionador RKSUITE.

//C interface to Fortran subroutine UT extern "C" void UT(void(*)(double*,double*,double*),double*,double*,double*, double*,double*,double*,int*); // C++ wrapper which calls extern "C" void UT routine static void rk_ut(void(*)(double*,double*,double*),double*,double*,double*, double*,double*,double*,int*); // Call of rk_ut with lambda passed instead of function pointer to derivative // routine mathlib::RungeKuttaSolver::rk_ut([](double* T,double* Y,double* YP)->void{YP[0]=Y[1]; YP[1]= -Y[0];}, TWANT,T,Y,YP,YMAX,WORK,UFLAG);

¿Es posible pasar una función lambda como puntero de función? Si es así, debo estar haciendo algo incorrectamente porque recibo un error de compilación.

Considere el siguiente ejemplo

using DecisionFn = bool(*)(); class Decide { public: Decide(DecisionFn dec) : _dec{dec} {} private: DecisionFn _dec; }; int main() { int x = 5; Decide greaterThanThree{ [x](){ return x > 3; } }; return 0; }

Cuando intento compilar esto , aparece el siguiente error de compilación:

In function ''int main()'': 17:31: error: the value of ''x'' is not usable in a constant expression 16:9: note: ''int x'' is not const 17:53: error: no matching function for call to ''Decide::Decide(<brace-enclosed initializer list>)'' 17:53: note: candidates are: 9:5: note: Decide::Decide(DecisionFn) 9:5: note: no known conversion for argument 1 from ''main()::<lambda()>'' to ''DecisionFn {aka bool (*)()}'' 6:7: note: constexpr Decide::Decide(const Decide&) 6:7: note: no known conversion for argument 1 from ''main()::<lambda()>'' to ''const Decide&'' 6:7: note: constexpr Decide::Decide(Decide&&) 6:7: note: no known conversion for argument 1 from ''main()::<lambda()>'' to ''Decide&&''

Ese es un gran mensaje de error para digerir, pero creo que lo que constexpr es que el lambda no se puede tratar como un constexpr por lo que no puedo pasarlo como un puntero de función. También he intentado hacer x const, pero eso no parece ayudar.


La captura de lambdas no se puede convertir en punteros de función, como señaló esta respuesta .

Sin embargo, a menudo es bastante difícil proporcionar un puntero de función a una API que solo acepta uno. El método más citado para hacerlo es proporcionar una función y llamar a un objeto estático con ella.

static Callable callable; static bool wrapper() { return callable(); }

Esto es tedioso Llevamos esta idea más allá y automatizamos el proceso de creación de wrapper y hacemos la vida mucho más fácil.

#include<type_traits> #include<utility> template<typename Callable> union storage { storage() {} std::decay_t<Callable> callable; }; template<int, typename Callable, typename Ret, typename... Args> auto fnptr_(Callable&& c, Ret (*)(Args...)) { static bool used = false; static storage<Callable> s; using type = decltype(s.callable); if(used) s.callable.~type(); new (&s.callable) type(std::forward<Callable>(c)); used = true; return [](Args... args) -> Ret { return Ret(s.callable(std::forward<Args>(args)...)); }; } template<typename Fn, int N = 0, typename Callable> Fn* fnptr(Callable&& c) { return fnptr_<N>(std::forward<Callable>(c), (Fn*)nullptr); }

Y úsalo como

void foo(void (*fn)()) { fn(); } int main() { int i = 42; auto fn = fnptr<void()>([i]{std::cout << i;}); foo(fn); // compiles! }

Live

Esto es esencialmente declarar una función anónima en cada aparición de fnptr .

Tenga en cuenta que las invocaciones de fnptr sobrescriben los callable invocables del mismo tipo previamente escritos. Remediamos esto, hasta cierto punto, con el parámetro int N

std::function<void()> func1, func2; auto fn1 = fnptr<void(), 1>(func1); auto fn2 = fnptr<void(), 2>(func2); // different function


Sé que esto es un poco viejo ...

Pero quería agregar:

La expresión lambda (incluso las capturadas) se puede manejar como un puntero de función (función de puntero a miembro).

Es complicado porque una expresión Lambda no es una función simple. En realidad es un objeto con un operador ().

Cuando eres creativo, ¡puedes usar esto! Piense en una clase de "función" en el estilo de std :: function. Si guarda el objeto!

También puede usar el puntero de función.

Para usar el puntero de función, puede usar lo siguiente:

int first = 5; auto lambda = [=](int x, int z) { return x + z + first; }; int(decltype(lambda)::*ptr)(int, int)const = &decltype(lambda)::operator(); std::cout << "test = " << (lambda.*ptr)(2, 3) << std::endl;

Para construir una clase que pueda comenzar a funcionar como una "std :: function", solo haré un breve ejemplo. Primero necesita una clase / estructura que pueda almacenar el puntero de objeto y función, también necesita un operador () para ejecutarlo:

// OT => Object Type // RT => Return Type // A ... => Arguments template<typename OT, typename RT, typename ... A> struct lambda_expression { OT _object; RT(OT::*_function)(A...)const; lambda_expression(const OT & object) : _object(object), _function(&decltype(_object)::operator()) {} RT operator() (A ... args) const { return (_object.*_function)(args...); } };

Con esto, ahora puede ejecutar lambdas capturadas y no capturadas, al igual que está utilizando el original:

auto capture_lambda() { int first = 5; auto lambda = [=](int x, int z) { return x + z + first; }; return lambda_expression<decltype(lambda), int, int, int>(lambda); } auto noncapture_lambda() { auto lambda = [](int x, int z) { return x + z; }; return lambda_expression<decltype(lambda), int, int, int>(lambda); } void refcapture_lambda() { int test; auto lambda = [&](int x, int z) { test = x + z; }; lambda_expression<decltype(lambda), void, int, int>f(lambda); f(2, 3); std::cout << "test value = " << test << std::endl; } int main(int argc, char **argv) { auto f_capture = capture_lambda(); auto f_noncapture = noncapture_lambda(); std::cout << "main test = " << f_capture(2, 3) << std::endl; std::cout << "main test = " << f_noncapture(2, 3) << std::endl; refcapture_lambda(); system("PAUSE"); return 0; }

Este código funciona con VS2015 Espero que ayude :)

Saludos!

Editar: plantilla de agujas eliminadas FP, parámetro de puntero de función eliminado, renombrado a lambda_expression

Actualización 04.07.17:

template <typename CT, typename ... A> struct function : public function<decltype(&CT::operator())(A...)> {}; template <typename C> struct function<C> { private: C mObject; public: function(const C & obj) : mObject(obj) {} template<typename... Args> typename std::result_of<C(Args...)>::type operator()(Args... a) { return this->mObject.operator()(a...); } template<typename... Args> typename std::result_of<const C(Args...)>::type operator()(Args... a) const { return this->mObject.operator()(a...); } }; namespace make { template<typename C> auto function(const C & obj) { return ::function<C>(obj); } } int main(int argc, char ** argv) { auto func = make::function([](int y, int x) { return x*y; }); std::cout << func(2, 4) << std::endl; system("PAUSE"); return 0; }


Si bien el enfoque de la plantilla es inteligente por varias razones, es importante recordar el ciclo de vida de la lambda y las variables capturadas. Si se va a utilizar cualquier forma de puntero lambda y la lambda no es una continuación hacia abajo, solo se debe usar una copia [=] lambda. Es decir, incluso entonces, capturar un puntero a una variable en la pila no es SEGURO si la vida útil de esos punteros capturados (desbobinado de la pila) es menor que la vida útil de la lambda.

Una solución más simple para capturar una lambda como puntero es:

auto pLamdba = new std::function<...fn-sig...>([=](...fn-sig...){...});

por ejemplo, new std::function<void()>([=]() -> void {...}

Solo recuerde delete pLamdba más tarde delete pLamdba así que asegúrese de no perder la memoria lambda. El secreto para darse cuenta aquí es que las lambdas pueden capturar lambdas (pregúntese cómo funciona) y también que para que la std::function genéricamente, la implementación de lambda necesita contener suficiente información interna para proporcionar acceso al tamaño de la lambda (y datos capturados (por lo que la delete debería funcionar [ejecutando destructores de tipos capturados]).


Un atajo para usar una lambda con un puntero de función C es este:

"auto fun = +[](){}"

Usar Curl como ejemplo ( información de depuración de curl )

auto callback = +[](CURL* handle, curl_infotype type, char* data, size_t size, void*){ //add code here :-) }; curl_easy_setopt(curlHande, CURLOPT_VERBOSE, 1L); curl_easy_setopt(curlHande,CURLOPT_DEBUGFUNCTION,callback);


Una lambda solo se puede convertir a un puntero de función si no captura, del borrador de la sección estándar C ++ 11 5.1.2 [expr.prim.lambda] dice ( énfasis mío ):

El tipo de cierre para una expresión lambda sin captura lambda tiene una función de conversión const pública no virtual no explícita a puntero a función que tiene el mismo parámetro y tipos de retorno que el operador de llamada de función del tipo de cierre. El valor devuelto por esta función de conversión será la dirección de una función que, cuando se invoca, tiene el mismo efecto que invocar al operador de llamada de función del tipo de cierre.

Tenga en cuenta que cppreference también cubre esto en su sección sobre las funciones de Lambda .

Entonces las siguientes alternativas funcionarían:

typedef bool(*DecisionFn)(int); Decide greaterThanThree{ []( int x ){ return x > 3; } };

y esto también:

typedef bool(*DecisionFn)(); Decide greaterThanThree{ [](){ return true ; } };

y como señala 5gon12eder , también puede usar std::function , pero tenga en cuenta que std::function es pesado , por lo que no es una compensación sin costo.


La respuesta de Shafik Yaghmour explica correctamente por qué la lambda no se puede pasar como un puntero de función si tiene una captura. Me gustaría mostrar dos soluciones simples para el problema.

  1. Utilice std::function lugar de punteros de función sin formato.

    Esta es una solución muy limpia. Sin embargo, tenga en cuenta que incluye una sobrecarga adicional para el borrado de tipo (probablemente una llamada de función virtual).

    #include <functional> #include <utility> struct Decide { using DecisionFn = std::function<bool()>; Decide(DecisionFn dec) : dec_ {std::move(dec)} {} DecisionFn dec_; }; int main() { int x = 5; Decide greaterThanThree { [x](){ return x > 3; } }; }

  2. Use una expresión lambda que no capture nada.

    Dado que su predicado es realmente solo una constante booleana, lo siguiente solucionaría rápidamente el problema actual. Consulte esta respuesta para obtener una buena explicación de por qué y cómo funciona.

    // Your ''Decide'' class as in your post. int main() { int x = 5; Decide greaterThanThree { (x > 3) ? [](){ return true; } : [](){ return false; } }; }