람다 함수의 주소를 얻는 방법을 알아 내려고 노력 중입니다. 다음은 샘플 코드입니다.
[]() {
std::cout << "Address of this lambda function is => " << ????
}();
변수에서 람다를 캡처하고 주소를 인쇄 할 수 있다는 것을 알고 있지만이 익명 함수가 실행될 때 수행하고 싶습니다.
더 간단한 방법이 있습니까?
this
.
람다 함수의 주소를 얻는 방법을 알아 내려고 노력 중입니다. 다음은 샘플 코드입니다.
[]() {
std::cout << "Address of this lambda function is => " << ????
}();
변수에서 람다를 캡처하고 주소를 인쇄 할 수 있다는 것을 알고 있지만이 익명 함수가 실행될 때 수행하고 싶습니다.
더 간단한 방법이 있습니까?
this
.
답변:
직접 가능하지 않습니다.
그러나 람다 캡처는 클래스이며 객체의 주소는 첫 번째 멤버의 주소와 일치합니다. 따라서 첫 번째 캡처로 하나의 개체를 값으로 캡처하면 첫 번째 캡처의 주소는 람다 개체의 주소와 일치합니다.
int main() {
int i = 0;
auto f = [i]() { printf("%p\n", &i); };
f();
printf("%p\n", &f);
}
출력 :
0x7ffe8b80d820
0x7ffe8b80d820
또는 람다 캡처에 대한 참조를 호출 연산자로 전달 하는 데코레이터 디자인 패턴 람다를 만들 수 있습니다 .
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();
}
람다 내에서 람다 객체의 주소를 직접 가져올 수있는 방법이 없습니다.
이제 이런 일이 발생하면 매우 유용합니다. 가장 일반적으로 사용되는 것은 재귀입니다.
가 y_combinator
정의 된 곳 당신이 때까지 자신에 대해 말할 수없는 언어에서 비롯됩니다. 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)... );
}
};
이제 당신은 이것을 할 수 있습니다 :
y_combinator{ [](auto& self) {
std::cout<<"Address of this lambda function is => "<< &self;
} }();
이에 대한 변형은 다음과 같습니다.
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)... );
}
};
여기서 self
전달 된 self
인수는 첫 번째 인수로 전달되지 않고 호출 될 수 있습니다 .
두 번째는 실제 y 콤비 네이터 (일명 고정 소수점 콤비 네이터)와 일치합니다. 당신이 원하는 것은 '람다의 주소'가 무엇을 의미하는지에 달려 있습니다.
F
표준 레이아웃이 아닌 경우 표준 레이아웃 y_combinator
이 아니므로 적절한 보증이 제공되지 않습니다.
이 문제를 해결하는 한 가지 방법은 람다를 손으로 쓴 functor 클래스로 바꾸는 것입니다. 또한 람다는 본질적으로 후드 아래에 있습니다.
그러면 this
functor를 변수에 할당하지 않아도 주소를 통해 주소를 얻을 수 있습니다 .
#include <iostream>
class Functor
{
public:
void operator()() {
std::cout << "Address of this functor is => " << this;
}
};
int main()
{
Functor()();
return 0;
}
산출:
Address of this functor is => 0x7ffd4cd3a4df
이것은 이것이 100 % 휴대 가능하고 추론하고 이해하기가 매우 쉽다는 장점이 있습니다.
struct { void operator()() { std::cout << "Address of this functor is => " << this << '\n'; } } f;
람다를 포착하십시오.
std::function<void ()> fn = [&fn]() {
std::cout << "My lambda is " << &fn << std::endl;
}
std::function
필요하지 않으며 상당한 비용이 듭니다. 또한 해당 객체를 복사 / 이동하면 객체가 손상됩니다.
가능하지만 플랫폼 및 컴파일러 최적화에 크게 의존합니다.
내가 아는 대부분의 아키텍처에는 명령 포인터라는 레지스터가 있습니다. 이 솔루션의 핵심은 함수 내부에있을 때 추출하는 것입니다.
amd64에서 다음 코드는 함수 1에 가까운 주소를 제공해야합니다.
#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;
}
그러나 GCC에 예를 들어 https://godbolt.org/z/dQXmHm 에 -O3
최적화 수준의 기능 인라인 될 수 있습니다.
thread_local
저장 시간 이있는 객체의 주소를 파생시킬 수 없습니다 . 여기서 얻으려는 것은 객체가 아니라 함수의 반환 주소입니다. 그러나 컴파일러 생성 함수 프롤로그가 스택으로 푸시하고 로컬 변수를위한 공간을 만들기 위해 스택 포인터를 조정하기 때문에 작동하지 않습니다.