Search code examples
c++kernel

Assign function pointer inside class


I am writing simple experiment code to test out assigning member function of one class to the function pointer who is a member of another class. My sample code did not compile. The compiler gave an error saying: '&': illegal operation on bound member function expression.

I see some people do (&ClassB::printFun) when passing a pointer to a member function. It didn't work for me because printFun is not a static member function. I have also seen people using std::bind. But std library is not a option for me since I will be using this to write kernel mode code. I have being searching for the right way to do this for a while, can someone help me out? Thanks!

using namespace std;

class ClassA
{
public:
    ClassA(void(*callBack)(int));
    void (*funPtr)(int);
};

ClassA::ClassA(void(*callBack)(int))
{
    funPtr = callBack;
}

class ClassB
{
public:
    void printFun(int a)
    {
        cout << a << endl;
    }
};

int main()
{
    ClassB classB;
    ClassA classA(&classB->printFun);
    classA.funPtr(5);
    return 0;
}

Solution

  • Pointers to function and pointers to member functions are different things. You need an object to call a member function.

    A pointer to a meber function can be declared like this:

    void (ClassB::*ptrPtrintFunc)(int) = &ClassB::printFun;
    

    And you can call it like this:

    ClassB classB;
    (classB.*ptrPtrintFunc)(1);
    

    or this:

    ClassB *ptrClassB = &classB;
    (ptrClassB->*ptrPtrintFunc)(2);
    

    What you can do ist to use std::function instead a of the old school function pointer. You can pass a lambda function to the std::function, in which you call the method of the class:

    #include <iostream>
    #include <functional>   // std::function
    
    struct ClassA
    {
        using TFunc = std::function<void( int )>; // <-- std::function<void( int )> insted of void(*)(int)
        ClassA( TFunc func) : funPtr( func ) {}
        TFunc funPtr;
    };
    
    struct ClassB
    {
        void printFun( int a ) { std::cout << a << std::endl; }
    };
    
    int main()
    {
        ClassB classB;
        ClassA classA( [&classB]( int a ) { classB.printFun(a); } ); // <-- lambda function
        classA.funPtr(5);
        return 0;
    }
    

    Instead of the lambda function, you can also use std::bind with a std::placholder for the parameter:

    ClassB classB;
    ClassA classA( std::bind( &ClassB::printFun, &classB, std::placeholders::_1 ) );
    classA.funPtr(5);
    

    Of course you can also pass a simple function to std::function

    void printFunc( int a ) { std::cout << a << std::endl; }
    
    ClassA classA( printFunc ); 
    

    Extension to the answer : Solution without using STL

    Thanks for the information. I am using std library to print in the experimenting code. But the actual code I am writing is in kernel mode, so I will not have access to std library. I wonder if there is another way to do this.

    If you don't want to use STL anyway you can think about a solution with an abstract base class. You need a base class with an abstract callback method and you need 2 derived classes which overrides the abstract callback method. The implementations of the callback methods delegate the callback either to a function or to class method.

    Abstract class with the abstract methode CallBack:

    struct CCallBack
    {
        virtual void CallBack( int ) = 0;
    };
    

    Implementation for a function pointer. The overridden method CallBack delegates the callback to a function pointer:

    struct CCallBackFunc : public CCallBack
    {
        typedef void(*TFuncPtr)(int);
        TFuncPtr _funcPtr;
        CCallBackFunc( TFuncPtr funcPtr ) : _funcPtr( funcPtr ) {}
        virtual void CallBack( int a ) override { (*_funcPtr)( a ); }
    };
    

    Implementation for a method function pointer. The overridden method CallBack delegates the callback to an object and a method function pointer:

    template < class T >
    struct CCallBackMethod : public CCallBack
    {
        typedef void(T::*TMethodPtr)(int);
        T &_obj;
        TMethodPtr _methodPtr;
        CCallBackMethod( T &obj, TMethodPtr methodePtr ) : _obj( obj ), _methodPtr( methodePtr ) {}
        virtual void CallBack( int a ) override { (_obj.*_methodPtr)( a ); }
    };
    

    Example for the use of both cases:

    struct ClassA
    {
        ClassA( CCallBack *cb ) : _cb( cb ) {}
        virtual ~ClassA() { delete _cb; }
        CCallBack *_cb;
    };
    
    struct ClassB { void printFun( int a ) { std::cout << a << std::endl; } };
    void printFun( int a ) { std::cout << a << std::endl; }
    
    int main()
    {
        ClassB classB;
        ClassA classA0( new CCallBackFunc( &printFun ) );
        ClassA classA1( new CCallBackMethod<ClassB>( classB, &ClassB::printFun ) );
        classA0._cb->CallBack( 6 );
        classA1._cb->CallBack( 7 );
        return 0;
    }