c++ c++11 lambda c++14 c++17

¿Cómo obtener la dirección de una función lambda de C++ dentro de la propia lambda?



c++11 c++14 (6)

Estoy tratando de descubrir cómo obtener la dirección de una función lambda dentro de sí misma. Aquí hay un código de muestra:

[]() { std::cout << "Address of this lambda function is => " << ???? }();

Sé que puedo capturar el lambda en una variable e imprimir la dirección, pero quiero hacerlo en el lugar cuando se ejecuta esta función anónima.

¿Hay una manera más simple de hacerlo?


Aquí hay un enfoque de versión prometedor:

#include <future> #include <cstdio> int main() { std::promise<void *> p; auto f = [ready_future = p.get_future()]() mutable { printf("%p/n", ready_future.get()); }; p.set_value(&f); f(); }


Captura la lambda:

std::function<void ()> fn = [&fn]() { std::cout << "My lambda is " << &fn << std::endl; }


Es posible, pero depende en gran medida de la plataforma y la optimización del compilador.

En la mayoría de las arquitecturas que conozco, hay un registro llamado puntero de instrucciones. El objetivo de esta solución es extraerlo cuando estamos dentro de la función.

En amd64 El siguiente código debería proporcionarle direcciones cercanas a la función uno.

#include <iostream> void* foo() { void* n; asm volatile("lea 0(%%rip), %%rax" : "=a" (n)); return n; } auto boo = [](){ void* n; asm volatile("lea 0(%%rip), %%rax" : "=a" (n)); return n; }; int main() { std::cout<<"foo"<<''/n''<<((void*)&foo)<<''/n''<<foo()<<std::endl; std::cout<<"boo"<<''/n''<<((void*)&boo)<<''/n''<<boo()<<std::endl; }

Pero, por ejemplo, en gcc https://godbolt.org/z/dQXmHm con la función de nivel de optimización -O3 podría estar en línea.


No es directamente posible.

Sin embargo, las capturas lambda son clases y la dirección de un objeto coincide con la dirección de su primer miembro. Por lo tanto, si captura un objeto por valor como la primera captura, la dirección de la primera captura corresponde a la dirección del objeto lambda:

int main() { int i = 0; auto f = [i]() { printf("%p/n", &i); }; f(); printf("%p/n", &f); }

Salidas:

0x7ffe8b80d820 0x7ffe8b80d820

Alternativamente, puede crear un patrón de diseño de decorador lambda que pase la referencia a la captura lambda a su operador de llamada:

template<class F> auto decorate(F f) { return [f](auto&&... args) mutable { f(f, std::forward<decltype(args)>(args)...); }; } int main() { auto f = decorate([](auto& that) { printf("%p/n", &that); }); f(); }


No hay forma de obtener directamente la dirección de un objeto lambda dentro de una lambda.

Ahora, como sucede, esto es bastante útil. El uso más común es para recurrir.

El y_combinator proviene de idiomas en los que no podría hablar sobre usted hasta que se lo definiera. Se puede implementar con bastante facilidad en c ++ :

template<class F> struct y_combinator { F f; template<class...Args> decltype(auto) operator()(Args&&...args) const { return f( f, std::forward<Args>(args)... ); } template<class...Args> decltype(auto) operator()(Args&&...args) { return f( f, std::forward<Args>(args)... ); } };

ahora puedes hacer esto:

y_combinator{ [](auto& self) { std::cout<<"Address of this lambda function is => "<< &self; } }();

Una variación de esto puede incluir:

template<class F> struct y_combinator { F f; template<class...Args> decltype(auto) operator()(Args&&...args) const { return f( *this, std::forward<Args>(args)... ); } template<class...Args> decltype(auto) operator()(Args&&...args) { return f( *this, std::forward<Args>(args)... ); } };

donde el self pasado puede ser llamado sin pasar en self como primer argumento.

El segundo coincide con el combinador y real (también conocido como el combinador de punto fijo), creo. Lo que desea depende de lo que quiere decir con "dirección de lambda".


Una forma de resolver esto sería reemplazar la lambda con una clase de functor escrita a mano. También es lo que la lambda esencialmente está debajo del capó.

Entonces puede obtener la dirección a través de this , incluso sin asignar el functor a una variable:

#include <iostream> class Functor { public: void operator()() { std::cout << "Address of this functor is => " << this; } }; int main() { Functor()(); return 0; }

Salida:

Address of this functor is => 0x7ffd4cd3a4df

Esto tiene la ventaja de que es 100% portátil y extremadamente fácil de razonar y entender.