Search code examples
c++member-functions

cannot convert 'int (Scheduler::*)(int, void*)' to 'int (*)(int, void*)' for argument '2' to 'bool irq_InstallISR(int, int (*)(int, void*), void*)'


I have this function in irq header

/* irq.h */
bool irq_InstallISR(int irq, int (*isr)(int, void*), void* isr_data);

and a class Scheduler

/* Scheduler.cpp */
using namespace x86Duino;
void Scheduler::init(){
...
irq_InstallISR(RTCIRQ, &Scheduler::timerrtc_isr_handler, isrname_rtc);
...
}

int Scheduler::timerrtc_isr_handler(int irq, void* data){
...
}

and I got this error

error: cannot convert 'int (x86Duino::Scheduler::*)(int, void*)' to 'int (*)(int, void*)' for argument '2' to 'bool irq_InstallISR(int, int (*)(int, void*), void*)'

I have tried this in init function

using namespace std::placeholders;
irq_InstallISR(RTCIRQ, std::bind(&Scheduler::timerrtc_isr_handler, this, _1, _2), isrname_rtc);

but I also got a similar error

 error: cannot convert 'std::_Bind_helper<false, int (x86Duino::Scheduler::*)(int, void*), x86Duino::Scheduler*, const std::_Placeholder<1>&, const std::_Placeholder<2>&>::type {aka std::_Bind<int (x86Duino::Scheduler::*(x86Duino::Scheduler*, std::_Placeholder<1>, std::_Placeholder<2>))(int, void*)>}' to 'int (*)(int, void*)' for argument '2' to 'bool irq_InstallISR(int, int (*)(int, void*), void*)'

Please tell me what I'm doing wrong?


Solution

  • You haven't included the declaration of Scheduler::timerrtc_isr_handler, so my first guess would be that you forgot to declare that method as static?

    i.e. In the class definition, it should look like:

    class Scheduler
    {
      static int timerrtc_isr_handler(int irq, void* data);
    };
    

    The irq_InstallISR takes a global function. The error message says it cannot convert this prototype:

    int (Scheduler::*)(int, void*)
    

    Which is a member function, to

    int (*)(int, void*)
    

    Which is a normal C function (or non-member function, e.g. a static method).

    /edit I'm guessing you wanted something along the lines of this pattern:

    class Scheduler
    {
    public:
      Scheduler()
      {
        irq_InstallISR(RTCIRQ, timerrtc_isr_handler, this);
      }
      ~Scheduler()
      {
        // probably want to uninstall the callback here!
      }
    
      int isr_handler(int irq)
      {
        /// do you handling here
      }
    private:
      static int timerrtc_isr_handler(int irq, void* data)
      {
        // cast user data to correct class type
        Scheduler* sch = (Scheduler*)data;
        assert(sch); // just in case we get null for some reason?
    
        // thunk call to member function
        return sch->isr_handler(irq);
      }
    };
    

    /edit 2

    You aren't choosing between declaring the method as static or int, but the choice is between a static function (i.e. does not have access to 'this'), or a member function (which requires 'this'). Consider the following:

    struct Foo
    {
      void func1() { std::cout << "func1\n"; }
      static void func2() { std::cout << "func2\n"; }
    };
    
    void bar()
    {
      // call the static method - does not require an object!
      Foo::func2(); 
    
      // to call func1, we ALWAYS need an object... 
      Foo obj;
      obj.func1(); 
    }
    

    So let's take it another step. Let's imagine I've written a C-API library. C does not have support for C++ classes, so typically in order to inter-operate with C++ class based code, you often resort to a fairly common user data pattern. So I'll try to strip this down to the simplest possible example....

    /// MyLib.h
    
    
    // I want to call this method, and it will inturn call any 
    // callback functions registered with the system. 
    void callMyRegisteredFunction();
    
    // the type of function I want to call in the previous method
    // The C++ way of doing this is to say :
    // 
    //   someObject->func();
    //
    // However in C, without classes, the way you'd call it would be:
    //
    //   func(someObject); 
    // 
    // so the second pointer here is the object to call it on. 
    typedef void (CallbackFunc*)(void*);
    
    // register a callback function, and an associated user-defined object
    void registerFunc(CallbackFunc funcPtr, void* userData);
    
    // reset the internal callback
    void unregisterFunc();
    
    
    /// MyLib.cpp
    #include "MyLib.h"
    
    // the currently registered callback function
    CallbackFunc g_func = NULL;
    
    // the 'object' it is registered against. From 'C' we don't know that 
    // what type of object this is, we just know it's address. 
    void* g_userData = NULL;
    
    void registerFunc(CallbackFunc funcPtr, void* userData)
    {
      g_func = funcPtr;
      g_userData = userData;
    }
    void unregisterFunc()
    {
      g_func = NULL;
      g_userData = NULL;
    }
    
    void callMyRegisteredFunction()
    {
      // don't call invalid method
      if(!g_func) return;
    
      // call the function, and pass it the userData pointer
      // This code does NOT know about C++ code, or the class 
      // type that you registered. 
      g_func(g_userData);
    }
    
    class MyCallbackObject
    {
    public:
      MyCallbackObject()
      {
        registerFunc(C_callback, this); //< NOTE: !!this!!
      }
    
      ~MyCallbackObject()
      {
        unregisterFunc();
      }
    
      // everything else prior exists PURELY to be able to call this C++ 
      // class method, from C code, that has absolutely NO idea about how 
      // your class is defined. 
      // NOTE: I'm making this method virtual so that instead of duplicating
      // the boiler plate code everywhere, you can just inherit from this 
      // class, and override the doThing method. 
      virtual void doThing()
      {
        /// do you handling here
      }
    private:
      static void C_callback(void* userData)
      {
        // cast user data to correct class type
        MyCallbackObject* obj = (MyCallbackObject*)userData;
    
        // now call the method
        obj->doThing();
      }
    };
    

    Honestly, I can't make the above example any simpler. This userData pattern exists simply so that you can call member functions of C++ objects from a C library. Hopefully the above makes sense, if not you'll probably need to read up on static methods, and the limitations of C.