Member function pointers?

Started by
18 comments, last by CapThunderNutz 8 years, 5 months ago

So I was wondering is how could I make the following possible? In my current attempts I get a compiler error one way or another sad.png

I was wondering if it was possible to have a member function of a class. Then get the pointer to that member function.

Something like:



//Header
class TestClass
{
   public:
     void Init(AnotherObject *anotherObject);
     void MyFunction();
};


//Imp
void TestClass::Init(AnotherObject *anotherObject)
{
    //Callback is a void (*AnotherObjectCallback::callback)()
    //This code is broken. How can I do this?
    anotherObject->callback = &this->MyFunction 
}

void TestClass::MyFunction()
{
    //Do stuff!
}


Advertisement

I was wondering if it was possible to have a member function of a class. Then get the pointer to that member function.

Yes, it is possible, however its a little more difficult. I didn't see the declaration of "anotherObject->callback", but I assume it is something like this


using CallbackFunction = void(*)();

Right? Now, a member function pointer unfortunately needs a declaration of the actual class with it:


using CallbackFunction = void(TestClass::*)();

And you obviously need to store a pointer to an instance of TestClass and call the member function pointer on that.

There are two ways around it:

- Use std::function with type erasure. I have no experience in that so I cannot help you with it, but just so I mentioned it

- If you do not need to access data members of TestClass when the function pointer is called, make the member function "static", then you can just assign it to the function pointer normally:


static TestClass
{
    static void MyFunction();
}

void TestClass::Init(AnotherObject *anotherObject)
{
anotherObject->callback = &TestClass::MyFunction; // only if static 
}

You need two bits of information:

  1. The address of the member function within the class. You get this directly using the class definitions.
  2. The instance of the object whose member function you want to invoke.

A fully working example is shown below, copy and paste it to experiment.


#include <iostream>

class A
{
public:
  float Multiply(float x, float y)
  {
    return x * y;
  }
};

int main()
{
  // 1. Get the address of the member function inside the class.
  float (A::*functPtr)(float, float) = &A::Multiply;

  // 2. The instance of the object whose member function you want to invoke.
  A * objPtr = new A();

  // 3. Invoke the function pointer on the object instance.
  std::cout << "2 x 3 = " << (objPtr->*functPtr)(2.0f, 3.0f) << std::endl;

  return 0;
}

If you use C++11 onward, you should use std::function and combine it with std::bind.


class AnotherObject
{
public:
   std::function<void(void)> callback;
};

class TestObject
{
public:
   void MyFunction() {};
};

int main()
{
   AnotherObject ao;
   TestObject to;
   ao.callback = std::bind(&TestObject::MyFunction, &to);
   ao.callback();
}

Edit: Good catch by CapThunderNutz below.

devstropo.blogspot.com - Random stuff about my gamedev hobby

Streway, you need to change the line:


ao.callback = std::bind(&TestObject::MyFunction, &ao); 

to


ao.callback = std::bind(&TestObject::MyFunction, new TestObject());

(or to another instance of the TestObject).

make the member function "static", then you can just assign it to the function pointer normally


If the function is not static am I still allowed to assign it?
I see that I get a "a pointer to a bound function may only be called" syntax error when trying
I'm not really sure if I'm on the right track with the first section you talked about

//In header file
void (TestClass::*MyFunctionPtr)(AnotherObject *anotherObject);
void MyFunction(AnotherObject *anotherObject);

//In imp file
MyFunctionPtr = &TestClass::MyFunction;
anotherObject->callback = this->*MyFunctionPtr;

If you use C++11 onward, you should use std::function and combine it with std::bind.


I went ahead and gave this ago, but I get tripped up at the std::bind
My issue there is that I am trying to use a 'this' in the second param since I want to keep it inside the TestClass::Init function

Besides the fact that I get an error
Is doing something like this not going to happen? Because of the 'this':
//In the header file
std::function<void(AnotehrObject *anotherObject)> MyFunctionFunc;
void TestClass::MyFunction();


//In the imp file (inside the init function of TestClass)
MyFunctionFunc = std::bind(TestClass::MyFunction, this); //causes error on type conversion

You can use a typedef to have a clean code of what you wanted to do from your first post :


typedef void (*TFunction)();

class AnotherObject
{
public:
  TFunction Callback;
};

class TestClass
{
public:
  TestClass(AnotherObject* Object)
  {
    Object->Callback = MyFunction;
  }

  static void MyFunction()
  {
    // Do something...
  }
};

//In the imp file (inside the init function of TestClass)
MyFunctionFunc = std::bind(TestClass::MyFunction, this); //causes error on type conversion

You get a type conversion error because you're passing an object of the wrong type and it can;t convert it. Try this.


MyFunctionFunc = std::bind(&TestClass::MyFunction, this); //causes error on type conversion

Stephen M. Webb
Professional Free Software Developer

It's all about how functions are stored. There is a lot of information on the internet about this, so I wont repeat it here, i.e. see: http://stackoverflow.com/questions/15572411/where-are-member-functions-stored-for-an-object But the short and sweet of it, that if you want to store a pointer to a member function you need two bits of information. The pointer to the function that will be invoked, and the instance (this pointer) of the object that will be passed to the member function as a transparent parameter.

Non-Member functions and Static functions don't need a transparent parameter to be passed since the live in a different world where they are unique so you only need to know the pointer to the function.

The STL functions that others have posted basically wrap all this up for you. For the sake of learning I've used raw C++ (with the exception of "iostream" to give the function something testable to do) only I've implemented your original example. (I changed AnotherObject to AnotherClass, because Objects are instances of Classes ... a little bit pedantic, sorry tongue.png). It is implemented using three code files, copy, paste and experiment.

File: AnotherClass.hpp


#ifndef ANOTHER_CLASS
#define ANOTHER_CLASS

#include "TestClass.hpp"

class TestClass;

class AnotherClass
{
public:
    // The pointer to the TestClass object on which the pointer must be
    // initialised.
    TestClass * fTestObjPtr;

    // The function pointer to the TestClass member function that will be
    // invoked.
    void (TestClass::*fMyFunctionPtr)();

    // This function invokes the external function.
    void InvokeFuncPtr()
    {
        (fTestObjPtr->*fMyFunctionPtr)();
    }
};

#endif // ANOTHER_CLASS

File: TestClass.hpp


#ifndef TEST_CLASS
#define TEST_CLASS

#include "AnotherClass.hpp"

class TestClass
{
public:
    void Init(AnotherClass * anotherObject)
    {
        // Set the object to invoke on.
        anotherObject->fTestObjPtr = (TestClass*)(this);

        // Set the function pointer to invoke.
        anotherObject->fMyFunctionPtr = &TestClass::MyFunction;
    }

    // The function to be invoked by Another Class.
    void MyFunction()
    {
        std::cout << "Hello there!" << std::endl;
    }
};

#endif // TEST_CLASS

File: Main.cpp


#include <iostream>
#include "TestClass.hpp"
#include "AnotherClass.hpp"

int main()
{
    // Create the test object.
    TestClass testObject;

    // Create the another object.
    AnotherClass anotherObject;


    // Initialise the test object.
    testObject.Init(&anotherObject);

    // Execute the function.
    anotherObject.InvokeFuncPtr();

    return 0;
}

Also may I ask (I'm new here and not to clear on the procedure regarding down votes) why post #5 was voted down? I actually compiled the code in post #4 and could not get it working without changing the second parameter to an instance of TestObject. I can only think that it was because I put it as a new instance inside the function call where it most certainly will create a memory leak, but since none of the examples are technically complete with a lot of implied error / exception checking and assumed functionality, I fail see why this would be a particular problem for any C++ programmer.

This topic is closed to new replies.

Advertisement