Convert C++ function pointer to c function pointer
我正在使用C库开发C ++应用程序。 我必须发送一个指向C库的函数的指针。
这是我的课:
1 2 3 4 5 6 7 8 9 10 11 | 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函数:
1 2 3 4 5 6 7 | 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中注册回调时,您还需要传递"用户数据"指针,该指针将传递回您的注册函数。因此,您可以执行以下操作:
1 2 3 4 5 6 7 8 9 10 | class MyClass { void non_static_func(/* args */); public: static void static_func(MyClass *ptr, /* other args */) { ptr->non_static_func(/* other args */); } }; |
然后将您的回调注册为
1 | c_library_function(MyClass::static_func, this); |
即将实例指针传递给静态方法,并将其用作转发函数。
严格来说,要实现总体可移植性,您需要使用声明为
您不能将函数指针传递给非静态成员函数。您可以做的是创建一个使用实例参数进行调用的静态或全局函数。
这是一个有用的示例,该示例使用具有两个成员的帮助器类:函数包装器和调用包装器的回调函数。
1 2 3 4 5 6 7 8 9 10 11 12 13 | 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; |
使用此功能,您可以存储任何可调用的甚至是非静态的成员函数(使用
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | 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指针访问类的静态方法。因此,请尝试使其变为静态。指向类方法的指针需要额外的信息,例如"对象"(this),对于纯C方法没有任何意义。
此处显示的FAQ很好地解释了您的问题,并提出了可能的(丑陋的)解决方案。
@Snps的答案很好。我使用创建回调的maker函数对其进行了扩展,因为我始终使用不带参数的
1 2 3 4 5 6 7 | 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回调,并将成员称为:
1 2 | voidCCallback callback = makeCCallback(&Foo::print, this); plainOldCFunction(callback); |
@Snps的答案是完美的!但是正如@DXM所提到的,它只能容纳一个回调。我对其进行了一些改进,现在它可以保留许多相同类型的回调。有点奇怪,但是效果很好:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 | #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 |
现在,您可以执行以下操作:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | 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代码能够在没有MainWindow *的情况下调用MainWindow的实例方法,这是根本不可能的。
如果使用mem_fn将
1 | void (*window_callback)(MainWindow*,int*) = std::mem_fn(&MainWindow::on_btn_clicked); |
这是给定问题的答案,但与界面不匹配。您将必须编写一个C函数以将调用包装到特定实例(毕竟,您的C API代码对MainWindow或其任何特定实例一无所知):
1 2 3 4 | void window_button_click_wrapper(int* arg) { MainWindow::inst()->on_btn_clicked(arg); } |
这被认为是OO反模式,但是由于C API对您的对象一无所知,因此这是唯一的方法。
我有一个主意(由于缺少
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | class MainWindow; static MainWindow* instance; class MainWindow { public: MainWindow() { instance = this; registerCallback([](int* arg){instance->...}); } }; |
如果实例化