source

C++ 함수 포인터를 C 함수 포인터로 변환

nicesource 2023. 11. 5. 14:44
반응형

C++ 함수 포인터를 C 함수 포인터로 변환

저는 C 라이브러리를 이용하여 C++ 어플리케이션을 개발하고 있습니다.C 라이브러리에 기능할 포인터를 보내야 합니다.

이것은 내 수업입니다.

 class MainWindow : public QMainWindow {  
     Q_OBJECT  
     public:  
     explicit MainWindow(QWidget *parent = 0);  
     private:  
     Ui::MainWindow *ui;
     void f(int*);

 private slots:
     void on_btn_clicked(); 
};

내 on_btn_clicked 함수입니다.

void MainWindow::on_btn_clicked()
{
    void (MainWindow::* ptfptr) (int*) = &MainWindow::f;

    c_library_function(static_cast<void()(int*)>(ptfptr), NULL);

}

C 함수는 이러한 함수인 void f(int*)에 대한 포인터를 가져와야 합니다.하지만 위의 코드가 작동하지 않아 f멤버 기능을 원하는 포인터로 변환할 수 없습니다.

누가 도와줄 수 있습니까?

비정규 멤버 함수 포인터는 일반 함수 포인터로 전달할 수 없습니다.그것들은 같은 것도 아니고, 아마 같은 크기도 아닐 겁니다.

그러나 일반적으로 C를 통해 정적 멤버 함수에 포인터를 전달할 수 있습니다.일반적으로 C API에 콜백을 등록할 때 "사용자 데이터" 포인터를 전달하여 등록된 기능으로 다시 전달합니다.따라서 다음과 같은 작업을 수행할 수 있습니다.

class MyClass
{

    void non_static_func(/* args */);

public:
    static void static_func(MyClass *ptr, /* other args */) {
        ptr->non_static_func(/* other args */);
    }
};

그러면 콜백을 다음과 같이 등록합니다.

c_library_function(MyClass::static_func, this);

즉, 인스턴스 포인터를 정적 메서드로 전달하고 전달 함수로 사용합니다.

엄밀하게 말하면 무료 기능을 사용해야 합니다.extern "C"전달을 위한 정적 멤버가 아닌(declared를 a로)friend필요한 경우), 하지만 실제로 이 방법을 사용하여 C 콜백이 많은 GObject 코드와 C++ 코드를 인터페이스하는 데 문제가 발생한 적은 없습니다.

비정규 멤버 함수에는 함수 포인터를 전달할 수 없습니다.인스턴스 매개변수를 사용하여 통화를 수행하는 정적 함수 또는 전역 함수를 만드는 것이 가능합니다.

여기 기능 래퍼와 래퍼를 호출하는 콜백 기능의 두 멤버로 구성된 도우미 클래스를 사용하는 유용한 예가 있습니다.

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

// Initialize the static member.
template <typename Ret, typename... Params>
std::function<Ret(Params...)> Callback<Ret(Params...)>::func;

이 기능을 사용하면 호출 가능하거나 정적이지 않은 멤버 함수도 저장할 수 있습니다.std::bind)를 사용하여 c-pointer로 변환합니다.Callback::callback기능.예:

struct Foo {
    void print(int* x) { // Some member function.
        std::cout << *x << std::endl;
    }
};

int main() {
    Foo foo; // Create instance of Foo.

    // Store member function and the instance using std::bind.
    Callback<void(int*)>::func = std::bind(&Foo::print, foo, std::placeholders::_1);

    // Convert callback-function to c-pointer.
    void (*c_func)(int*) = static_cast<decltype(c_func)>(Callback<void(int*)>::callback);

    // Use in any way you wish.
    std::unique_ptr<int> iptr{new int(5)};
    c_func(iptr.get());
}

내가 정확히 기억한다면, 클래스의 정적 메서드만 함수 구문에 "정상" C 포인터를 통해 액세스할 수 있습니다.그러니까 정적으로 만들어 보세요.클래스의 메서드에 대한 포인터는 순수 C 메서드에 대한 의미가 없는 "개체"(이것)와 같은 추가 정보가 필요합니다.

여기에 나와 있는 FAQ에는 문제에 대한 좋은 설명과 가능한 (못생긴) 해결책이 나와 있습니다.

@Snps의 대답은 훌륭합니다.항상 사용하던 콜백을 만드는 메이커 기능으로 확장하였습니다.void매개 변수가 없는 콜백:

typedef void (*voidCCallback)();
template<typename T>
voidCCallback makeCCallback(void (T::*method)(),T* r){
  Callback<void()>::func = std::bind(method, r);
  void (*c_function_pointer)() = static_cast<decltype(c_function_pointer)>(Callback<void()>::callback);
  return c_function_pointer;
}

그때부터 클래스 내 또는 다른 곳에서 일반 C 콜백을 생성하고 구성원에게 다음과 같은 호출을 할 수 있습니다.

voidCCallback callback = makeCCallback(&Foo::print, this);
plainOldCFunction(callback);

@Snps의 대답은 완벽합니다!그러나 @DXM이 언급했듯이 콜백은 한 번만 유지할 수 있습니다.조금 개선을 했는데, 지금은 같은 종류의 콜백을 많이 유지할 수 있습니다.조금 이상하지만 완벽하게 작동합니다.

 #include <type_traits>

template<typename T>
struct ActualType {
    typedef T type;
};
template<typename T>
struct ActualType<T*> {
    typedef typename ActualType<T>::type type;
};

template<typename T, unsigned int n,typename CallerType>
struct Callback;

template<typename Ret, typename ... Params, unsigned int n,typename CallerType>
struct Callback<Ret(Params...), n,CallerType> {
    typedef Ret (*ret_cb)(Params...);
    template<typename ... Args>
    static Ret callback(Args ... args) {
        func(args...);
    }

    static ret_cb getCallback(std::function<Ret(Params...)> fn) {
        func = fn;
        return static_cast<ret_cb>(Callback<Ret(Params...), n,CallerType>::callback);
    }

    static std::function<Ret(Params...)> func;

};

template<typename Ret, typename ... Params, unsigned int n,typename CallerType>
std::function<Ret(Params...)> Callback<Ret(Params...), n,CallerType>::func;

#define GETCB(ptrtype,callertype) Callback<ActualType<ptrtype>::type,__COUNTER__,callertype>::getCallback

이제 당신은 다음과 같은 일을 할 수 있습니다.

typedef void (cb_type)(uint8_t, uint8_t);
class testfunc {
public:
    void test(int x) {
        std::cout << "in testfunc.test " <<x<< std::endl;
    }

    void test1(int x) {
        std::cout << "in testfunc.test1 " <<x<< std::endl;
    }

};

cb_type* f = GETCB(cb_type, testfunc)(std::bind(&testfunc::test, tf, std::placeholders::_2));

cb_type* f1 = GETCB(cb_type, testfunc)(
                std::bind(&testfunc::test1, tf, std::placeholders::_2));


f(5, 4);
f1(5, 7);

간단히 말하면 std::mem_fn을 사용하여 멤버 함수 포인터를 일반 C 함수 포인터로 변환할 수 있습니다.

그것은 주어진 질문에 대한 답이지만, 질문자는 C 코드가 메인 윈도우*를 갖지 않고 메인 윈도우의 인스턴스 메서드를 호출할 수 있을 것으로 예상하기 때문에 이 질문은 혼란스러운 전제를 가지고 있는 것 같습니다.

mem_fn을 사용하여 캐스팅할 경우MainWindow::on_btn_clickedC 함수 포인터에, 당신은 여전히 a를 취하는 함수.MainWindow*그 첫 번째 논거로

void (*window_callback)(MainWindow*,int*) = std::mem_fn(&MainWindow::on_btn_clicked);

그것은 주어진 질문에 대한 답이지만 인터페이스와 일치하지 않습니다.호출을 특정 인스턴스에 래핑하려면 C 함수를 작성해야 합니다(결국 C API 코드는 메인 윈도우 또는 메인 윈도우의 특정 인스턴스에 대해 아무것도 알지 못합니다.

void window_button_click_wrapper(int* arg)
{
    MainWindow::inst()->on_btn_clicked(arg);
}

이것은 OO 안티패턴으로 간주되지만, C API는 당신의 객체에 대해 아무것도 모르기 때문에 이것만이 유일한 방법입니다.

아이디어가 있습니다. (완전 표준 준수는 아님)extern "C"누락됨):

class MainWindow;

static MainWindow* instance;

class MainWindow
{
public:
  MainWindow()
  {
    instance = this;

    registerCallback([](int* arg){instance->...});
  }
};

여러 인스턴스의 경우 문제가 발생합니다.MainWindow인스턴스화됩니다.

언급URL : https://stackoverflow.com/questions/19808054/convert-c-function-pointer-to-c-function-pointer

반응형