programing

C 콜백 함수로서의 C++ 클래스멤버 함수 사용

prostudy 2022. 6. 5. 17:48
반응형

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*)’.

질문:

  1. 우선, C++ 클래스 멤버의 함수를 등록하는 것은 가능한 것입니까.등록하는 방법은 무엇입니까?(http://www.parashift.com/c++-faq-lite/mixing-c-and-cpp.html에서 32.8을 읽었습니다.하지만 제 생각에는 문제가 해결되지 않습니다.)
  2. 이 문제를 해결할 다른/더 나은 방법이 있습니까?

멤버 함수가 스태틱한 경우 이를 수행할 수 있습니다.

에는 타입 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

반응형