27

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?

logoff
  • 3,118
  • 4
  • 36
  • 51
RRR
  • 3,421
  • 13
  • 48
  • 68
  • functions inside a class are not recommended for accessing. – Aniket Inge Nov 06 '13 at 09:21
  • possible duplicate of [Function pointer to class member function problems](http://stackoverflow.com/questions/439540/function-pointer-to-class-member-function-problems) – Andrey Nov 06 '13 at 09:24
  • 6
    Your question makes no sense. C++ function pointers and C function pointers are the same thing (except for some minor linkage details). But you must understand that `MainWindow::f` is not a function and `&MainWindow::f` is not a function pointer -- it's a **member** function (or respectively a pointer to a member function). – Kerrek SB Nov 06 '13 at 09:27
  • For a start - in C++ a member function to be used as a function pointer would have to be static. Bringing C into the equation wouldn't remove that requirement. – Grimm The Opiner Nov 06 '13 at 09:28
  • To be 100% standards-compliant (which could be relevant on exotic hardware), you should use an [`extern "C"` function as the callback](http://stackoverflow.com/a/19423544/1782465). – Angew is no longer proud of SO Nov 06 '13 at 09:29
  • Here is a library that will solve all your problems. Its brilliant and I have used it on production systems across multiple platforms. http://www.codeproject.com/Articles/7150/Member-Function-Pointers-and-the-Fastest-Possible – GlGuru Sep 17 '14 at 12:00

7 Answers7

24

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.

Tristan Brindle
  • 15,036
  • 2
  • 31
  • 79
18

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());
}
Snps
  • 13,516
  • 6
  • 49
  • 72
  • Nice. Small bug: `static void callback` should be `static Ret callback`. – Anne van Rossum Apr 22 '15 at 16:21
  • 1
    This approach only works if you want to convert one member function to plain c function pointer right? If you have two or three, you need two or three function wrapper class, because each wrapper class stores function as static member variable. – DXM Apr 13 '16 at 23:06
  • How would one make this work for multithreading? I've posted a new question here : http://stackoverflow.com/questions/41198854/using-a-c-class-member-function-as-a-c-callback-function-thread-safe-version – Victor.dMdB Dec 17 '16 at 13:58
  • This answer is **exactly** what I needed to interface to a C library from my C++ code. Unlike other answers which suggest passing class context to the callback this answer allows you to use C libraries as is without modification of callback semantics. – K-Sid Aug 01 '19 at 05:38
13

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.

ElGavilan
  • 5,747
  • 16
  • 24
  • 35
Ferenc Deak
  • 30,889
  • 14
  • 82
  • 151
  • This https://stackoverflow.com/questions/1000663/using-a-c-class-member-function-as-a-c-callback-function/56943930#56943930 is a nicer solution. – cibercitizen1 Jul 09 '19 at 06:54
8

@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);
Thomas Fankhauser
  • 4,761
  • 1
  • 27
  • 31
3

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.

stands2reason
  • 616
  • 1
  • 7
  • 18
3

@Snps answer is perfect! But as @DXM mentioned it can hold only one callback. I've improved it a little, now it can keep many callbacks of the same type. It's a little bit strange, but works perfect:

 #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

Now you can just do something like this:

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);
  • update: for use in multiple files you need to pass TypeName of caller, because __COUNTER__ macro is only per-file and doesn't generate unique numbers per project. – Evgeniy Alexeev Oct 28 '16 at 04:52
  • Is there also a solution for multiple objects? I tried this sample and it does not work properly if I have multiple instances of "tf" – Soccertrash Aug 16 '17 at 15:15
1

I've got an idea (not entirely standard-compliant, as extern "C" is missing):

class MainWindow;

static MainWindow* instance;

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

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

You will have problems if multiple instances of MainWindow are instantiated.

user1095108
  • 12,675
  • 6
  • 43
  • 96
  • What does the `+` in `registerCallback(+[](int* arg){instance->...});` mean? Never seen that before. – Snps Nov 06 '13 at 14:34
  • It converts the lambda object to a function pointer. The lambda helps prevent namespace pollution (one function definition & declaration less). – user1095108 Nov 06 '13 at 14:41
  • Isn't that unnecessary? I thought a non-capturing lambda is implicitly convertible to a suitable function pointer. – Snps Nov 06 '13 at 14:57
  • @Snps There's no need to capture a global variable. Why would it? As far as conversion is concerned, you may be right, but the `+` does no harm AFAIK. – user1095108 Nov 06 '13 at 14:59
  • I didn't see that it was a global at first. – Snps Nov 06 '13 at 15:04