Como obter o endereço de uma função lambda C++ dentro da própria lambda?




c++11 c++14 (4)

Estou tentando descobrir como obter o endereço de uma função lambda dentro de si. Aqui está um código de exemplo:

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

Eu sei que posso capturar o lambda em uma variável e imprimir o endereço, mas quero fazê-lo no lugar quando esta função anônima estiver em execução.

Existe uma maneira mais simples de fazer isso?


É possível, mas depende muito da otimização da plataforma e do compilador.

Na maioria das arquiteturas que conheço, existe um registro chamado ponteiro de instruções. O objetivo desta solução é extraí-la quando estivermos dentro da função.

Em amd64, o código a seguir deve fornecer endereços próximos à função

#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;
}

Mas, por exemplo, no gcc https://godbolt.org/z/dQXmHm com a função de nível de otimização -O3 pode estar embutido.


Aqui está uma abordagem de versão promissora:

#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();
}

Não é diretamente possível.

No entanto, capturas lambda são classes e o endereço de um objeto coincide com o endereço de seu primeiro membro. Portanto, se você capturar um objeto por valor como a primeira captura, o endereço da primeira captura corresponde ao endereço do objeto lambda:

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

Saídas:

0x7ffe8b80d820
0x7ffe8b80d820

Como alternativa, você pode criar um padrão de design decorador lambda que transmita a referência para a captura lambda em seu operador de chamada:

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();
}

Não há como obter diretamente o endereço de um objeto lambda dentro de um lambda.

Agora, como acontece, isso geralmente é útil. O uso mais comum é para se recuperar.

O y_combinator vem de idiomas nos quais você não poderia falar sobre si mesmo até onde foi definido. Ele pode ser implementado facilmente no 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)... );
  }
};

agora você pode fazer isso:

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

Uma variação disso pode 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)... );
  }
};

onde o self passado pode ser chamado sem passar no self como o primeiro argumento.

O segundo corresponde ao combinador real y (também conhecido como combinador de ponto fixo), acredito. O que você deseja depende do que você quer dizer com 'endereço de lambda'.





c++17