Convert C++ function pointer to c function pointer

25,934

Solution 1

If I recall it correctly, Only static methods of a class can be accessed via "normal" C pointer to function syntax. So try to make it static. The pointer to a method of a class needs extra information, such as the "object" (this) which has no meaning for a pure C method.

The FAQ shown here has good explanation and a possible (ugly) solution for your problem.

Solution 2

You can't pass a non-static member function pointer as an ordinary function pointer. They're not the same thing, and probably not even the same size.

You can however (usually) pass a pointer to a static member function through C. Usually when registering a callback in a C API, you also get to pass a "user data" pointer which gets passed back to your registered function. So you can do something like:

class MyClass
{

    void non_static_func(/* args */);

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

Then register your callback as

c_library_function(MyClass::static_func, this);

i.e. pass the instance pointer to the static method, and use that as a forwarding function.

Strictly speaking for total portability you need to use a free function declared extern "C" rather than a static member to do your forwarding (declared as a friend if necessary), but practically speaking I've never had any problems using this method to interface C++ code with GObject code, which is C callback-heavy.

Solution 3

You can't pass a function pointer to a non-static member function. What you can do is to create a static or global function that makes the call with an instance parameter.

Here's an example I find useful which uses a helper class with two members: a function wrapper and a callback function that calls the wrapper.

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;

Using this you can store any callable, even non-static member functions (using std::bind) and convert to a c-pointer using the Callback::callback function. 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());
}

Solution 4

@Snps answer is great. I extended it with a maker function that creates a callback, as I always use void callbacks without parameters:

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

From then on, you can create your plain C callback from within the class or anywhere else and have the member called:

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

Solution 5

The short answer is: you can convert a member function pointer to an ordinary C function pointer using std::mem_fn.

That is the answer to the question as given, but this question seems to have a confused premise, as the asker expects C code to be able to call an instance method of MainWindow without having a MainWindow*, which is simply impossible.

If you use mem_fn to cast MainWindow::on_btn_clicked to a C function pointer, then you still a function that takes a MainWindow* as its first argument.

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

That is the answer to the question as given, but it doesn't match the interface. You would have to write a C function to wrap the call to a specific instance (after all, your C API code knows nothing about MainWindow or any specific instance of it):

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

This is considered an OO anti-pattern, but since the C API knows nothing about your object, it's the only way.

Share:
25,934
RRR
Author by

RRR

Updated on July 11, 2022

Comments

  • RRR
    RRR almost 2 years

    I am developing a C++ application using a C library. I have to send a pointer to function to the C library.

    This is my class:

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

    This is my on_btn_clicked function:

    void MainWindow::on_btn_clicked()
    {
        void (MainWindow::* ptfptr) (int*) = &MainWindow::f;
    
        c_library_function(static_cast<void()(int*)>(ptfptr), NULL);
    
    }
    

    The C function should get a pointer to a such function : void f(int*). But the code above doesn't work, I cannot succeed to convert my f member function to the desired pointer.

    Can anybody please help?