함수 포인터와 컨텍스트를 예상하는 C-콜백에 C++ 람다를 전달하려면 어떻게 해야 합니까?
표준 함수-포인터+컨텍스트 패러다임을 사용하는 C-API에 콜백을 등록하려고 합니다.api는 다음과 같습니다.
void register_callback(void(*callback)(void *), void * context);
제가 정말 하고 싶은 것은 C++ 람다를 콜백으로 등록하는 것입니다.또한 람다가 변수를 캡처한 것(즉, 직선 상태 비저장으로 변환할 수 없음)을 원합니다.std::function)
람다를 콜백으로 등록하려면 어떤 종류의 어댑터 코드를 작성해야 합니까?
간단한 방법은 람다를 다음과 같은 위치에 고정하는 것입니다.std::function<void()>어딘가에 보관되어 있습니다.잠재적으로 힙에 할당되고 단지 에 의해 참조됩니다.void*콜백을 받는 엔티티에 등록되었습니다.콜백은 단순히 다음과 같은 기능이 됩니다.
extern "C" void invoke_function(void* ptr) {
(*static_cast<std::function<void()>*>(ptr))();
}
참고:std::function<S>상태가 있는 함수 개체를 유지할 수 있습니다(예: 비어 있지 않은 캡처의 람다 함수).다음과 같은 콜백을 등록할 수 있습니다.
register_callback(&invoke_function,
new std::function<void()>([=](){ ... }));
가장 효율적인 방법은voidify람다 직접.
#include <iostream>
#include <tuple>
#include <memory>
template<class...Args>
struct callback {
void(*function)(void*, Args...)=nullptr;
std::unique_ptr<void, void(*)(void*)> state;
};
template<typename... Args, typename Lambda>
callback<Args...> voidify( Lambda&& l ) {
using Func = typename std::decay<Lambda>::type;
std::unique_ptr<void, void(*)(void*)> data(
new Func(std::forward<Lambda>(l)),
+[](void* ptr){ delete (Func*)ptr; }
);
return {
+[](void* v, Args... args)->void {
Func* f = static_cast< Func* >(v);
(*f)(std::forward<Args>(args)...);
},
std::move(data)
};
}
void register_callback( void(*function)(void*), void * p ) {
function(p); // to test
}
void test() {
int x = 0;
auto closure = [&]()->void { ++x; };
auto voidified = voidify(closure);
register_callback( voidified.function, voidified.state.get() );
register_callback( voidified.function, voidified.state.get() );
std::cout << x << "\n";
}
int main() {
test();
}
여기서voidify람다 및 (선택적으로) 인수 목록을 사용하고 전통적인 C 스타일 콜백을 생성합니다.void*짝. 그void*소유자:unique_ptr리소스를 적절하게 정리할 수 있도록 특수 삭제 프로그램을 사용합니다.
이것의 장점은 다음과 같습니다.std::function솔루션은 효율성입니다. 런타임을 한 단계 제거했습니다.콜백이 유효한 수명 또한 명확합니다. 즉, 콜백이 유효한 수명은std::unique_ptr<void, void(*)(void*)>에 의해 반환된.voidify.
unique_ptr<T,D>할 수 있는move에 빠지다.shared_ptr<T>더 복잡한 삶을 원한다면요
위는 수명과 데이터를 혼합하고 소거와 유틸리티를 입력합니다.나눌 수 있습니다.
template<class Lambda, class...Args>
struct callback {
void(*function)(void*, Args...)=nullptr;
Lambda state;
};
template<typename... Args, typename Lambda>
callback<typename std::decay<Lambda>::type, Args...> voidify( Lambda&& l ) {
using Func = typename std::decay<Lambda>::type;
return {
+[](void* v, Args... args)->void {
Func* f = static_cast< Func* >(v);
(*f)(std::forward<Args>(args)...);
},
std::forward<Lambda>(l)
};
}
지금이다voidify할당하지 않습니다.콜백 기간 동안 포인터를 전달하여 voidify를 저장하기만 하면 됩니다.second당신과 마찬가지로void*의 옆에first함수 포인터.
이 구성을 스택 외부에 저장해야 하는 경우 람다를 다음으로 변환std::function도움이 될 수 있습니다.또는 위의 첫 번째 변형을 사용합니다.
void test() {
int x = 0;
auto closure = [&]()->void { ++x; };
auto voidified = voidify(closure);
register_callback( voidified.function, &voidified.state );
register_callback( voidified.function, &voidified.state );
std::cout << x << "\n";
}
람다 함수는 캡처 변수가 없는 한 C-콜백 함수와 호환됩니다.
새로운 방법으로 기존의 것에 새로운 것을 넣으라고 강요하는 것은 말이 되지 않습니다.
옛날 방식을 따르는 것은 어떻습니까?
typedef struct
{
int cap_num;
} Context_t;
int cap_num = 7;
Context_t* param = new Context_t;
param->cap_num = cap_num; // pass capture variable
register_callback([](void* context) -> void {
Context_t* param = (Context_t*)context;
std::cout << "cap_num=" << param->cap_num << std::endl;
}, param);
Lamda 함수를 C 함수 포인터로 사용하는 매우 간단한 방법은 다음과 같습니다.
auto fun=+[](){ //add code here }
예를 들어 이 답변을 참조하십시오.https://stackoverflow.com/a/56145419/513481
언급URL : https://stackoverflow.com/questions/20525977/how-can-i-pass-a-c-lambda-to-a-c-callback-that-expects-a-function-pointer-and
'programing' 카테고리의 다른 글
| 유형 스크립트로 "디버그" 모듈을 사용하는 방법 (0) | 2023.06.21 |
|---|---|
| Oracle SQL - 주별 데이터 합계 및 그룹화 (0) | 2023.06.21 |
| 가져온 모듈에서 전역 변수의 가시성 (0) | 2023.06.21 |
| 현대 각도에서 손자부터 조부모까지 이벤트를 발산하는 방법은 무엇입니까? (0) | 2023.06.21 |
| MongoDB 3.6.2 2008 R2 Plus가 설치되지 않음 (0) | 2023.06.21 |