我正在使用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成员函数转换为所需的指针。
有人可以帮忙吗?
答案 0 :(得分:22)
您不能将非静态成员函数指针作为普通函数指针传递。它们不是一回事,甚至可能都不一样。
然而,你可以(通常)通过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"
的自由函数而不是静态成员来进行转发(必要时声明为friend
),但实际上我从来没有使用这种方法将C ++代码与GObject代码接口有任何问题,这些代码是C回调密码。
答案 1 :(得分: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;
使用它可以存储任何可调用的,甚至是非静态成员函数(使用std::bind
)并使用Callback::callback
函数转换为c指针。 E.g:
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());
}
答案 2 :(得分:10)
如果我没记错,只能通过函数语法的“普通”C指针访问类的静态方法。所以尽量让它变得静止。指向类方法的指针需要额外的信息,例如“对象”(this),这对于纯C方法没有意义。
显示的常见问题解答here有很好的解释,并且可能(丑陋)解决您的问题。
答案 3 :(得分:4)
@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);
答案 4 :(得分:1)
我有一个想法(不完全符合标准,因为extern "C"
缺失):
class MainWindow;
static MainWindow* instance;
class MainWindow
{
public:
MainWindow()
{
instance = this;
registerCallback([](int* arg){instance->...});
}
};
如果MainWindow
的多个实例被实例化,您将遇到问题。
答案 5 :(得分:1)
简短的回答是:您可以使用std::mem_fn将成员函数指针转换为普通的C函数指针。
这是给出的问题的答案,但这个问题似乎有一个混乱的前提,因为提问者希望C代码能够在没有MainWindow *的情况下调用MainWindow的实例方法,这根本不可能。
如果使用mem_fn将MainWindow::on_btn_clicked
转换为C函数指针,那么您仍然是一个以MainWindow*
作为其第一个参数的函数。
void (*window_callback)(MainWindow*,int*) = std::mem_fn(&MainWindow::on_btn_clicked);
这是给出的问题的答案,但它与界面不匹配。您必须编写一个C函数来将调用包装到特定实例(毕竟,您的C API代码对MainWindow或其任何特定实例一无所知):
void window_button_click_wrapper(int* arg)
{
MainWindow::inst()->on_btn_clicked(arg);
}
这被认为是OO反模式,但由于C API对您的对象一无所知,所以这是唯一的方法。
答案 6 :(得分:1)
#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);