none
How to pass address of member function as parameter RRS feed

  • Question

  • Help please,

    I have created this bit of code to illustrate my problem. I wish to pass the address of a member function as a parameter in the constructor of another class. In my example, I don't want X to have any knowledge of A or B. Is there anyway to pass the address without getting compile errors. The code is unmanaged (no clr).

    typedef void(*DISP)(char*);
    
    class X
    {
    public:
    	DISP disp;
    
    	X(DISP pdisp) {
    		disp = pdisp;
    	}
    
    	X() {}
    };
    
    class A {
    public:
    	X *ax;
    
    	A() {
    		ax = new X(&display);
    	};
    
    	void display(char * msg){ }
    };
    
    class B {
    public:
    	X *bx;
    
    	B() {
    		bx = new X(&display);
    	};
    
    	void display(char * msg){ }
    };
    
    int main() {
    	A a;
    	B b;
    };

    Wednesday, July 2, 2014 1:43 AM

Answers

  • Hello Dave,

    1. In order to pass the address of a member function of a class, you need the declaration of the member function itself, e.,g. :

    typedef void (A::*A_display)(char* );
    typedef void (B::*B_display)(char* );
    

    2. You would then need to instantiate the A_display and B_display types to the specific member functions of the A and B classes, e.g. :

    A_display my_a_display = &(A::display);
    B_display my_b_display = &(B::display);
    

    3. You then need to call these member function pointers in relation to specific instances of A and B, e.g. :

    (a.*my_a_display)("Hello world");
    (b.*my_b_display)("Hello world");

    4. I do not know what you exactly mean by : X class to have "no knowledge of A and B" but if you mean that X must not know which specific class' display method is being called, I suggest that you declare a base class for both classes A and B, e.g. :

    class Base
    {
    public:
    	typedef void (Base::*DISP)(char*);
    };
    

    5. Then derive A and B from Base, e.g. :

    class A : public Base {
    public:
    	A() {
    	};
    
    	void display(char * msg){ }
    };
    
    class B : public Base {
    public:
    	B() {
    	};
    
    	void display(char * msg){ }
    };
    

    6. The X class can be declared with a constructor that takes a parameter to the Base::DSIP member function pointer type :

    class X
    {
    public:
    	Base::DISP m_base_display;
    
    	X(Base::DISP base_display)
    	{
    		m_base_display = base_display;
    	}
    	
    	void CallDisp(Base base)
    	{
    		(base.*m_base_display)("Hello World");
    	}
    };

    7. You would instantiate one or more X's in the following way :

    X x1((Base::DISP)&(A::display));
    X x2((Base::DISP)&(B::display));

    8. And then you can use CallDisp() to invoke an A or a B display() method :

    A a;
    B b;
      
    x1.CallDisp(a);
    x2.CallDisp(b);

    9. Hope this helps.

    - Bio.


    Please visit my blog : http://limbioliong.wordpress.com/

    • Marked as answer by Dave Mullard Thursday, July 3, 2014 12:22 AM
    Wednesday, July 2, 2014 3:01 AM

All replies

  • Hello Dave,

    1. In order to pass the address of a member function of a class, you need the declaration of the member function itself, e.,g. :

    typedef void (A::*A_display)(char* );
    typedef void (B::*B_display)(char* );
    

    2. You would then need to instantiate the A_display and B_display types to the specific member functions of the A and B classes, e.g. :

    A_display my_a_display = &(A::display);
    B_display my_b_display = &(B::display);
    

    3. You then need to call these member function pointers in relation to specific instances of A and B, e.g. :

    (a.*my_a_display)("Hello world");
    (b.*my_b_display)("Hello world");

    4. I do not know what you exactly mean by : X class to have "no knowledge of A and B" but if you mean that X must not know which specific class' display method is being called, I suggest that you declare a base class for both classes A and B, e.g. :

    class Base
    {
    public:
    	typedef void (Base::*DISP)(char*);
    };
    

    5. Then derive A and B from Base, e.g. :

    class A : public Base {
    public:
    	A() {
    	};
    
    	void display(char * msg){ }
    };
    
    class B : public Base {
    public:
    	B() {
    	};
    
    	void display(char * msg){ }
    };
    

    6. The X class can be declared with a constructor that takes a parameter to the Base::DSIP member function pointer type :

    class X
    {
    public:
    	Base::DISP m_base_display;
    
    	X(Base::DISP base_display)
    	{
    		m_base_display = base_display;
    	}
    	
    	void CallDisp(Base base)
    	{
    		(base.*m_base_display)("Hello World");
    	}
    };

    7. You would instantiate one or more X's in the following way :

    X x1((Base::DISP)&(A::display));
    X x2((Base::DISP)&(B::display));

    8. And then you can use CallDisp() to invoke an A or a B display() method :

    A a;
    B b;
      
    x1.CallDisp(a);
    x2.CallDisp(b);

    9. Hope this helps.

    - Bio.


    Please visit my blog : http://limbioliong.wordpress.com/

    • Marked as answer by Dave Mullard Thursday, July 3, 2014 12:22 AM
    Wednesday, July 2, 2014 3:01 AM
  • In addition to Lim Bio Liong's excellent post I would like to give the reason why you need at least some information on the class.

    Member functions are special because of this. It is a pointer that always refers to the instance of the class that it is working on. So, the question is where does this actually come from.

    In the expression a.myMember(); you can actually think that this gets rewritten to myMember(&a); and for a->myMember(), this becomes myMember(a);. So this is obtained from the call, and this is also true for pointer to members.

    So if we think of the member function as a regular function with a hidden parameter for this, what do we actually get, for void A::myMember(); well, maybe void myMember(A* this); and this is where the problem lies, the this pointer is strongly typed too. Using a pointer in the pointer to member must be of a compatible type.

    So how can you help yourself with these pointers. There are two conversions to these function pointers that are allowed. The first is conversion to a base class type. So

    class Base
    {
    public:
    	void MyFun()
    	{
    		wprintf(L"In Base::MyFun\n");
    	}
    };
    class Derived : public Base
    {
    public:
    	void MyFun()
    	{
    		wprintf(L"In Derived::MyFun\n");
    	}
    	void MyFun2()
    	{
    		wprintf(L"In Derived::MyFun2\n");
    	}
    };
    
    typedef void(Base::*MyBaseFunPtr)();
    typedef void(Derived::*MyFunPtr)();
    
    Derived testclass;
    MyFunPtr D2Ptr = &Derived::MyFun2;
    MyBaseFunPtr BPtr = static_cast<MyBaseFunPtr>(D2Ptr);
    ((&testclass)->*BPtr)();

    This is allowed, but just be careful you don't accidentally pass it a pointer to a base class type. The compiler won't really complain.


    This is a signature Any samples given are not meant to have error checking or show best practices. They are meant to just illustrate a point. I may also give inefficient code or introduce some problems to discourage copy/paste coding. This is because the major point of my posts is to aid in the learning process.

    Wednesday, July 2, 2014 4:10 AM
  • In modern C++ you can also consider this:

    typedef std::function<void(char*)> func;


    class X

    {

    public:

           func mFunc;


           X( func f ) : mFunc(f)

           {

           }


           void Test( char * msg )

           {

                  if( mFunc ) mFunc( msg );

           }

    };


    class A

    {

    public:

           X mX;


           A() : mX( [this](char * msg) { this->display(msg); } )

           {

           };


           void display( char * msg )
           {
               puts(
    msg );
           }

    };


    int main()

    {

           A a;

           a.mX.Test( "Test" );

           return 0;

    };


    • Edited by Viorel_MVP Wednesday, July 2, 2014 6:11 AM
    Wednesday, July 2, 2014 6:10 AM
  • Hi Bio,

    Thanks for that. Solves my problem. Why didn't I think of using a base class. I have produced a variant of your code, see below, and it works great.

    Curiously, &(A::display) gives an error, but not &A::display.

    I see from your blog that you have been doing a series of articles on interfacing C# to unmanaged C++. That is precisely what I have been doing. The A class represents the bottom of my interface code and X the top of my unmanaged code. I have got everything working except this one small problem that I thought would be trivial. Calls down are easy and I can pass results back. I can also do calls up from the A level to my C#. What I was stuck on was this last bit. The reason for my X not knowing about A is that the interface code is obviously platform dependant, Windows, but I want my unmanaged code to be platform independent because I wish to also use it on Linux.

    Thanks also to everyone else. All helpful. Did not know about 

    typedef std::function<void(char*)> func; That may be a better way.

    Dave

    typedef void(*DISP)(char*);
    
    class Base
    {
    public:
    	typedef void (Base::*DISP)(char*);
    };
    
    class X
    {
    public:
    	Base::DISP m_base_display;
    
    
    	X(Base::DISP base_display) : m_base_display(base_display)
    	{
    	}
    
    	void CallDisp(Base* base)
    	{
    		(base->*m_base_display)("Hello World");
    	}
    
    	X() {}
    };
    
    
    class A : public Base 
    {
    public:
    	X *ax;
    
    	A() {
    		ax = new X((Base::DISP)&A::display);
    	};
    
    	void display(char * msg){ }
    
    	void test()
    	{
    		ax->CallDisp(this);
    	}
    };
    
    class B : public Base 
    {
    public:
    	X *bx;
    
    	B() {
    		bx = new X((Base::DISP)&B::display);
    	};
    
    	void display(char * msg){ }
    
    	void test()
    	{
    		bx->CallDisp(this);
    	}
    };
    
    int main() {
    	A a;
    	a.test();
    	B b;
    	b.test();
    };
    

     

    Thursday, July 3, 2014 12:49 AM