C 콜백 함수로서의 C++ 클래스멤버 함수 사용
C라이브러리에 콜백 함수를 등록하여 처리를 커스터마이즈해야 합니다.은 '콜백'입니다.int a(int *, int *)
.
저는 다음과 같은 C++ 코드를 쓰고 있으며 C++ 클래스 함수를 콜백 함수로 등록하려고 합니다.
class A {
public:
A();
~A();
int e(int *k, int *j);
};
A::A()
{
register_with_library(e)
}
int
A::e(int *k, int *e)
{
return 0;
}
A::~A()
{
}
컴파일러가 다음 오류를 발생시킨다.
In constructor 'A::A()',
error:
argument of type ‘int (A::)(int*, int*)’ does not match ‘int (*)(int*, int*)’.
질문:
- 우선, C++ 클래스 멤버의 함수를 등록하는 것은 가능한 것입니까.등록하는 방법은 무엇입니까?(http://www.parashift.com/c++-faq-lite/mixing-c-and-cpp.html에서 32.8을 읽었습니다.하지만 제 생각에는 문제가 해결되지 않습니다.)
- 이 문제를 해결할 다른/더 나은 방법이 있습니까?
멤버 함수가 스태틱한 경우 이를 수행할 수 있습니다.
에는 타입 A의 인 첫 .class A*
이 포인터에 대응합니다.그렇기 때문에 콜백의 시그니처에 첫 번째 파라미터가 포함되어 있는 경우에만 콜백을 등록할 수 있었습니다.class A*
discloss.discloss.
멤버 함수가 스태틱하지 않지만 좀 더 많은 작업이 필요한 경우에도 이 작업을 수행할 수 있습니다(C++ 함수 포인터를 c 함수 포인터로 변환 참조).
#include <stdio.h>
#include <functional>
template <typename T>
struct Callback;
template <typename Ret, typename... Params>
struct Callback<Ret(Params...)> {
template <typename... Args>
static Ret callback(Args... args) {
return func(args...);
}
static std::function<Ret(Params...)> func;
};
template <typename Ret, typename... Params>
std::function<Ret(Params...)> Callback<Ret(Params...)>::func;
void register_with_library(int (*func)(int *k, int *e)) {
int x = 0, y = 1;
int o = func(&x, &y);
printf("Value: %i\n", o);
}
class A {
public:
A();
~A();
int e(int *k, int *j);
};
typedef int (*callback_t)(int*,int*);
A::A() {
Callback<int(int*,int*)>::func = std::bind(&A::e, this, std::placeholders::_1, std::placeholders::_2);
callback_t func = static_cast<callback_t>(Callback<int(int*,int*)>::callback);
register_with_library(func);
}
int A::e(int *k, int *j) {
return *k - *j;
}
A::~A() { }
int main() {
A a;
}
이 예는 컴파일이라는 점에서 완전합니다.
g++ test.cpp -std=c++11 -o test
때 필요한 것이 있습니다.c++11
에 깃발이 . 코드를 보면register_with_library(func)
.func
'Member Function'에 스태틱함수입니다.e
문제는!=기능입니다.컴파일러는 다음과 같은 방법으로 메서드를 변환합니다.
int e( A *this, int *k, int *j );
클래스 인스턴스를 인수로 전달할 수 없기 때문에 통과할 수 없습니다.회피책의 한 가지 방법은 메서드를 스태틱하게 하는 것입니다.이렇게 하면 좋은 타입이 됩니다.단, 클래스 인스턴스는 없으며 비정적 클래스 멤버에 대한 액세스도 할 수 없습니다.
다른 방법은 A에 대한 정적 포인터를 사용하여 함수를 선언하는 것입니다.이 함수는 콜을 클래스로 리다이렉트만 합니다.
int callback( int *j, int *k )
{
static A *obj = new A();
a->(j, k);
}
그런 다음 콜백 함수를 등록할 수 있습니다.
Win32 플랫폼에는 항상 지저분한 퉁킹 방식이 있습니다.
Win32의 Thunking: 비정적 멤버 함수에 대한 콜백 단순화
그것은 해결책이지만 나는 그것을 사용하는 것을 추천드립니다.
설명이 잘 되어 있고, 그것이 존재한다는 것을 알게 되어 기쁩니다.
이 솔루션에서는 "c 함수"에 콜백으로 제공되는 정적 메서드를 사용하는 템플릿클래스가 있습니다.이 클래스는 최종적으로 호출되는 callback()이라는 이름의 멤버 함수를 가진 "일반" 개체를 보유하고 있습니다.
클래스(여기서 A)를 정의하면 쉽게 사용할 수 있습니다.
int main() {
Holder<A> o ( A(23, 23) );
std::cout << o().getN() << "\n";
callACFunctionPtr( fun );
callACFunctionPtr( o.callback );
} // ()
완전한 예:
#include <iostream>
// ----------------------------------------------------------
// library class: Holder
// ----------------------------------------------------------
template< typename HeldObjectType >
class Holder {
public:
static inline HeldObjectType object;
static void callback( ) {
object.callback();
} // ()
HeldObjectType & operator() ( ) {
return object;
}
Holder( HeldObjectType && obj )
{
object = obj;
}
Holder() = delete;
}; // class
// ----------------------------------------------------------
// "old" C function receivin a ptr to function as a callback
// ----------------------------------------------------------
using Callback = void (*) (void);
// ..........................................................
// ..........................................................
void callACFunctionPtr( Callback f ) {
f();
} // ()
// ----------------------------------------------------------
// ----------------------------------------------------------
void fun() {
std::cout << "I'm fun\n";
} //
// ----------------------------------------------------------
//
// Common class where we want to write the
// callback to be called from callACFunctionPtr.
// Name this function: callback
//
// ----------------------------------------------------------
class A {
private:
int n;
public:
A( ) : n( 0 ) { }
A( int a, int b ) : n( a+b ) { }
void callback( ) {
std::cout << "A's callback(): " << n << "\n";
}
int getN() {
return n;
}
}; // class
// ----------------------------------------------------------
// ----------------------------------------------------------
int main() {
Holder<A> o ( A(23, 23) );
std::cout << o().getN() << "\n";
callACFunctionPtr( fun );
callACFunctionPtr( o.callback );
} // ()
멤버 함수를 사용할 때의 문제는 동작할 오브젝트가 필요하고 C는 오브젝트에 대해 알지 못한다는 것입니다.
가장 쉬운 방법은 다음과 같습니다.
//In a header file:
extern "C" int e(int * k, int * e);
//In your implementation:
int e(int * k, int * e) { return 0; }
2022년에 그 문제에 관해 오는 누구에게나, 나는 최초의 요구에 응한 새로운 접근법을 제안할 것이다.이 솔루션에 대한 피드백을 환영합니다.또, 이 토픽에 액세스 하는 모든 사람에게 도움이 되었으면 합니다.
먼저 이미 강조했듯이 핵심 문제는 (오브젝트의 멤버 등에 액세스할 수 있는) 비정적 방식이라는 점을 이해해야 합니다.)는 객체의 인스턴스 포인터인 'this'에 액세스해야 합니다.단, 이 함수는 콜백으로 하고 싶기 때문에 호출 방식을 변경할 수 없습니다.그렇기 때문에 오브젝트의 'this' 포인터에 액세스할 수 있는 함수가 필요합니다.
저의 솔루션은 실행 시 더미 함수 클론의 코드를 수정하고 콜백 함수로 주소를 전달하는 것입니다.콜백 함수는 한번 호출되면 할당된 오브젝트 포인터를 해결할 수 있습니다.이 더미는 래퍼에 템플릿되어 있기 때문에 원하는 시그니처에 맞출 수 있습니다.
먼저 향후 코드가 업데이트될 경우를 대비한 저장소 링크입니다(https://github.com/Ezarkei/BindFunctorToC https://gitlab.com/Ezarkei/BindFunctorToC)
사용방법은 다음과 같습니다.
Object instance{}; //Create an instance
BindFunctorToC<Object> binder{instance}; //Create a binder on that instance
void(*fPtr)(void){binder()}; //Get the C-style function pointer from the binder, here the signature is void(*)(void)
fPtr(); //Call the C-style function pointer
다음으로 좀 더 자세한 예를 제시하겠습니다.
#include "BindFunctorToC.hpp"
#include <iostream>
struct Foo {
int operator()(std::string const &other) const noexcept { //This is our functor, the "entry point" to our object from the C-style function pointer call
return Bar(other); //Here this functor simply forwards to a method
}
int Bar(std::string const &other) const noexcept { //This method is non-static and will use an object's member: _str
std::cout << _str << ' ' << other << std::endl; //Beeing able to access _str here clearly shows that it's not a trick, we have a direct access to 'this'
return 0;
}
std::string const _str{"default"};
};
static void CallBack(int(*callback)(std::string const &)) noexcept { //This is the kind of use case we want to be able to accomplish, a simple C-style function pointer is passed as parameter but it will effectively call a non-static method on an object
callback("world"); //Here we will call foo1 instance's operator(), hence foo1's 'Bar' method
}
int main(void) {
Foo foo1{"hello"}, foo2{"foo"}; //First we declare 2 instances of Foo, with 2 different member values so we can distinguish them well
BindFunctorToC<Foo> binder1{foo1}, binder2{foo2}; //For every instance a binder is needed
int(*ptr)(std::string const &){binder1()}; //We then construct a C-style function pointer with Foo's operator() signature and initialize it to binder1 function by calling binder1's operator()
CallBack(ptr); //Here we will pass our C-style function pointer to the C api which may need it as a callback
return binder2()("bar"); //Proof that we work on instances, first the operator() will get the C-style function pointer, then we call it and return its value to show the signatures deduction works
}
마지막으로 저장소에서 사용 가능한 바인더 코드(BindFunctorToC.hpp의 내용):
//******************************************************************************
//* Copyright (c) 2022 Ezarkei *
//* *
//* This document is under the MIT License *
//******************************************************************************
#ifndef BINDFUNCTORTOC_HPP_
#define BINDFUNCTORTOC_HPP_
#if ((defined(__i386__) || defined(__x86_64__) || defined(__arm__)) && (defined(__linux__) || defined(__linux) || defined(linux) || defined(__unix__) || defined(__unix))) || (defined(WIN32) || defined(_WIN32) || defined(__WIN32__))
#if defined(WIN32) || defined(_WIN32) || defined(__WIN32__)
#if defined(_DEBUG) || defined(DEBUG)
#error Requires release compilation (windows)
#endif
#define __win32__
#endif
#ifdef __win32__
#define __attribute__(__)
#include <windows.h>
#else
#include <sys/mman.h>
#include <unistd.h>
#include <cstring>
#endif
#include <type_traits>
#include <stdexcept>
#include <string>
#ifdef __win32__
#define __DCL__(_) ((typename decltype(_))(_))
#else
#define __DCL__(_) (_)
#endif
#define __FLG__ 0x21626e636967616d
template<typename R> struct __TTRf__ {
explicit __TTRf__(void) noexcept = delete;
using _R = R &;
};
template<typename> struct __BndFcntrTC__;
template<typename R, typename T, typename ...A> struct __BndFcntrTC__<R(T::*)(A...)> {
public:
explicit __BndFcntrTC__(T &);
~__BndFcntrTC__(void) noexcept;
R(*operator()(void) const noexcept)(A...);
R(&_mppr)(__BndFcntrTC__<R(T::*)(A...)> &, typename __TTRf__<A>::_R...) noexcept = &__MdmMppr__<>;
private:
void __MplcDdrss__(void const *const);
template<typename O = R> static typename std::enable_if<std::is_same<O, void>::value, void>::type __MdmMppr__(__BndFcntrTC__<R(T::*)(A...)> &, typename __TTRf__<A>::_R...) noexcept;
template<typename O = R> static typename std::enable_if<!std::is_same<O, void>::value, O>::type __MdmMppr__(__BndFcntrTC__<R(T::*)(A...)> &, typename __TTRf__<A>::_R...) noexcept;
static std::size_t __PgSzClcltr__(void) noexcept;
static std::size_t __RwTmpltSzClcltr__(void) noexcept;
static std::size_t const _flg, _pgSz, _rwTmpltSz, _sgmntSz;
T &_trgt;
void *_sgmnt;
};
template<typename> struct __CnstNxcptBstrct__;
template<typename R, typename T, typename ...A> struct __CnstNxcptBstrct__<R(T::*)(A...)> {
explicit __CnstNxcptBstrct__(void) noexcept = delete;
using _S = R(T::*)(A...);
};
template<typename R, typename T, typename ...A> struct __CnstNxcptBstrct__<R(T::*)(A...) const> {
explicit __CnstNxcptBstrct__(void) noexcept = delete;
using _S = typename __CnstNxcptBstrct__<R(T::*)(A...)>::_S;
};
#if __cplusplus > 201402L
template<typename R, typename T, typename ...A> struct __CnstNxcptBstrct__<R(T::*)(A...) noexcept> {
explicit __CnstNxcptBstrct__(void) noexcept = delete;
using _S = typename __CnstNxcptBstrct__<R(T::*)(A...)>::_S;
};
template<typename R, typename T, typename ...A> struct __CnstNxcptBstrct__<R(T::*)(A...) const noexcept> {
explicit __CnstNxcptBstrct__(void) noexcept = delete;
using _S = typename __CnstNxcptBstrct__<R(T::*)(A...)>::_S;
};
#endif
template<typename T> class BindFunctorToC : public __BndFcntrTC__<typename __CnstNxcptBstrct__<decltype(&T::operator())>::_S> {
public:
explicit BindFunctorToC(T &);
};
template<typename R, typename T, typename ...A> __attribute__((noinline, unused)) void __SzClcltrE__(void) noexcept;
template<typename R, typename T, typename ...A> __attribute__((noinline, optimize(3))) typename std::enable_if<std::is_same<R, void>::value, void>::type __RwTmplt__(A...) noexcept;
template<typename R, typename T, typename ...A> __attribute__((noinline, optimize(3))) typename std::enable_if<!std::is_same<R, void>::value, R>::type __RwTmplt__(A...) noexcept;
template<typename R, typename T, typename ...A> __BndFcntrTC__<R(T::*)(A...)>::__BndFcntrTC__(T &trgt) : _trgt{trgt} {
#ifdef __win32__
(void const *const)(_rwTmpltSz + _pgSz);
_sgmnt = VirtualAlloc(NULL, _sgmntSz, MEM_COMMIT, PAGE_EXECUTE_READWRITE);
if (!_sgmnt)
throw std::runtime_error{std::string{"VirtualAlloc error :: "} + std::to_string(GetLastError())};
#else
_sgmnt = mmap(nullptr, _sgmntSz, PROT_EXEC | PROT_WRITE | PROT_READ, MAP_ANONYMOUS | MAP_PRIVATE, -1, 0);
if (MAP_FAILED == _sgmnt)
throw std::runtime_error{std::string{"Mmap error :: "} + strerror(errno)};
#endif
void const *const sgmnt{(void const *)__DCL__((&__RwTmplt__<R, T, A...>))};
std::memcpy(_sgmnt, sgmnt, _rwTmpltSz);
__MplcDdrss__(this);
#ifdef __win32__
unsigned long dscrd;
if (!VirtualProtect(_sgmnt, _sgmntSz, PAGE_EXECUTE_READ, &dscrd))
throw std::runtime_error{std::string{"VirtualProtect error :: "} + std::to_string(GetLastError())};
#else
if (mprotect(_sgmnt, _sgmntSz, PROT_EXEC | PROT_READ))
throw std::runtime_error{std::string{"Mprotect error :: "} + strerror(errno)};
__builtin___clear_cache(_sgmnt, (uint8_t*)_sgmnt + _rwTmpltSz);
#endif
}
template<typename R, typename T, typename ...A> __BndFcntrTC__<R(T::*)(A...)>::~__BndFcntrTC__(void) noexcept {
#ifdef __win32__
if (!VirtualFree(_sgmnt, 0, MEM_RELEASE))
#else
if (munmap(_sgmnt, _sgmntSz))
#endif
abort();
}
template<typename R, typename T, typename ...A> R(*__BndFcntrTC__<R(T::*)(A...)>::operator()(void) const noexcept)(A...) {
return (R(*)(A...))_sgmnt;
}
template<typename R, typename T, typename ...A> void __BndFcntrTC__<R(T::*)(A...)>::__MplcDdrss__(void const *const ddrss) {
std::size_t const tht{(std::size_t const)ddrss};
uint8_t *ffst{nullptr}, m{0};
for (std::size_t i{0}, j{0}, k{0}; !ffst && _rwTmpltSz > i; ++i)
if (j[(uint8_t*)&_flg] == i[(uint8_t*)_sgmnt]) {
if (!j++)
k = i;
else if (sizeof(void *volatile const) <= j)
ffst = (uint8_t*)_sgmnt + k;
} else if (j)
j = 0;
if (ffst)
std::memcpy(ffst, &tht, sizeof(void *volatile const));
else {
for (std::size_t i{0}; !ffst && _rwTmpltSz > i; ++i)
for (uint8_t l{0}; !ffst && 8 > l; l += 4)
for (std::size_t j{0}, k{0}; _rwTmpltSz > i + j + k && 7 > j; 2 == j ? (j += 2, k = l) : ++j)
if (!(j % 4 ? j % 2 ? (uint8_t{(uint8_t)(j[(uint8_t *)_sgmnt + i + k] << 4)} >> 4) == uint8_t{(uint8_t)((j / 4 ? 3 : 1)[(uint8_t *)&_flg] << 4)} >> 4 : (uint8_t{(uint8_t)(j[(uint8_t *)_sgmnt + i + k] << 4)} >> 4) == (j / 4 ? 3 : 1)[(uint8_t *)&_flg] >> 4 : j[(uint8_t *)_sgmnt + i + k] == (j / 2)[(uint8_t *)&_flg]))
j = 7;
else if (6 == j) {
ffst = (uint8_t *)_sgmnt + i;
m = l;
}
if (ffst)
for (std::size_t i{0}, k{0}; 7 > i; 2 == i ? (i += 2, k = m) : ++i)
i % 4 ? ((i[ffst + k] >>= 4) <<= 4) |= i % 2 ? uint8_t{(uint8_t)((i / 4 ? 3 : 1)[(uint8_t *)&tht] << 4)} >> 4 : (i / 4 ? 3 : 1)[(uint8_t *)&tht] >> 4 : i[ffst + k] = (i / 2)[(uint8_t *)&tht];
}
if (!ffst)
throw std::runtime_error{"Failed to resolve flag offset"};
}
template<typename R, typename T, typename ...A> template<typename O> typename std::enable_if<std::is_same<O, void>::value, void>::type __BndFcntrTC__<R(T::*)(A...)>::__MdmMppr__(__BndFcntrTC__<R(T::*)(A...)> &tht, typename __TTRf__<A>::_R... __flds__) noexcept {
tht._trgt.operator()(std::forward<A>(__flds__)...);
}
template<typename R, typename T, typename ...A> template<typename O> typename std::enable_if<!std::is_same<O, void>::value, O>::type __BndFcntrTC__<R(T::*)(A...)>::__MdmMppr__(__BndFcntrTC__<R(T::*)(A...)> &tht, typename __TTRf__<A>::_R... __flds__) noexcept {
return tht._trgt.operator()(std::forward<A>(__flds__)...);
}
template<typename R, typename T, typename ...A> void __SzClcltrE__(void) noexcept {
__SzClcltrE__<R, T, A...>();
}
template<typename R, typename T, typename ...A> typename std::enable_if<std::is_same<R, void>::value, void>::type __RwTmplt__(A... __flds__) noexcept {
void *volatile const __RwTmpltRmPtr__{(void *)__FLG__};
__BndFcntrTC__<R(T::*)(A...)> &tht{*((__BndFcntrTC__<R(T::*)(A...)> *const)__RwTmpltRmPtr__)};
tht._mppr(tht, __flds__...);
}
template<typename R, typename T, typename ...A> typename std::enable_if<!std::is_same<R, void>::value, R>::type __RwTmplt__(A... __flds__) noexcept {
void *volatile const __RwTmpltRmPtr__{(void *)__FLG__};
__BndFcntrTC__<R(T::*)(A...)> &tht{*((__BndFcntrTC__<R(T::*)(A...)> *const)__RwTmpltRmPtr__)};
return tht._mppr(tht, __flds__...);
}
template<typename R, typename T, typename ...A> std::size_t __BndFcntrTC__<R(T::*)(A...)>::__PgSzClcltr__(void) noexcept {
#ifdef __win32__
SYSTEM_INFO nf{};
GetSystemInfo(&nf);
return nf.dwPageSize;
#else
return (std::size_t)sysconf(_SC_PAGESIZE);
#endif
}
template<typename R, typename T, typename ...A> std::size_t __BndFcntrTC__<R(T::*)(A...)>::__RwTmpltSzClcltr__(void) noexcept {
if ((std::size_t)__DCL__((&__RwTmplt__<R, T, A...>)) > (std::size_t)&__SzClcltrE__<R, T, A...>)
abort();
return (std::size_t)&__SzClcltrE__<R, T, A...> - (std::size_t)__DCL__((&__RwTmplt__<R, T, A...>));
}
template<typename R, typename T, typename ...A> std::size_t const __BndFcntrTC__<R(T::*)(A...)>::_flg{(std::size_t)__FLG__};
template<typename R, typename T, typename ...A> std::size_t const __BndFcntrTC__<R(T::*)(A...)>::_pgSz{__PgSzClcltr__()};
template<typename R, typename T, typename ...A> std::size_t const __BndFcntrTC__<R(T::*)(A...)>::_rwTmpltSz{__RwTmpltSzClcltr__()};
template<typename R, typename T, typename ...A> std::size_t const __BndFcntrTC__<R(T::*)(A...)>::_sgmntSz{(_rwTmpltSz / _pgSz + 1) * _pgSz};
template<typename T> BindFunctorToC<T>::BindFunctorToC(T &trgt) : __BndFcntrTC__<typename __CnstNxcptBstrct__<decltype(&T::operator())>::_S>(trgt) {
}
#ifdef __win32__
#undef __win32__
#undef __attribute__
#endif
#undef __DCL__
#undef __FLG__
#else
#error Unknown system ; supports unix(-like) (x86_64, i386, arm) and windows (x64, x32)
#endif
#endif
언급URL : https://stackoverflow.com/questions/1000663/using-a-c-class-member-function-as-a-c-callback-function
'programing' 카테고리의 다른 글
Makefile과 CMake를 사용한 코드 컴파일 간의 차이점 (0) | 2022.06.06 |
---|---|
Vuejs 2 이미지 Src 속성 (0) | 2022.06.05 |
Vue2: 경고:프롭을 직접 뮤트하지 않음 (0) | 2022.06.05 |
Java에서의 함수 포인터 (0) | 2022.06.05 |
스토어에서 Vue 플러그인을 사용하는 방법 (0) | 2022.06.05 |