I need to define a function which will return an object of a class.
Suppose I have a class called dog (class declaration in .h and implementation in .cpp), and when the dog dies, I want to call a function to let the dog manager know that. In dog`s SetFunc I will receive the function to be called:
Dog.h
class Dog
{
public:
typedef void (*OndogDie)(Dog * dog);
OndogDie function;
void SetFunc(OndogDie func);
void Die();
};
Dog.cpp
void Dog::SetFunc(Dog::OndogDie func)
{
function = func;
}
void Dog::Die()
{
function(this);
}
If I put the typedef inside the class Dog, everthing works, execept the Manager class, which can`t pass as parameter a suitable callback for the dog class :
Manager.h
#include "Dog.h"
class Manager
{
public:
Manager();
void CbdogDie(Dog * dog);
};
Manager.cpp
void Manager::CbdogDie(Dog * d)
{
//stuff
}
Manager::Manager()
{
Dog d = Dog();
d.SetFunc(CbdogDie); (Error: argument of type void(Manager::*)(Dog *d) is incompatible with parameter of type "Dog::OndogDie")
}
I already looked in many tutorials, and answered questions, but none of them had helped me find out what is wrong with my code. I have also considered using the delegate type, but it only worked in an clr project.
I`m thinking that the problem might be in the place where I put the typedef function. Is there any other place to define this function ? If not is there any solution to this problem, or at least an alternative to it.
Thanks
Repeating my comment for the convenience of further readers: CbdogDie (in your code) is a function member (non-static) and requires an instance of Manager to be called so its type is incompatible with your callback typedef.
Unrelated to your error but still worth consideration: Shouldn't you move the logic that invokes the callback into Dog's destructor? In C++ (unlike Java, for example) we know when our objects are destructed.
Not sure if you can apply this to your actual problem but here are some examples that work.
#include <iostream>
#include <string>
class Dog
{
public:
typedef void (* on_dog_die_callback_t)(const Dog&);
Dog(const std::string& name, on_dog_die_callback_t callback) :
name_(name), callback_(callback)
{
std::clog << "A new dog named " << this->getName() << " is born.\n";
}
~Dog()
{
std::clog << "A dog named " << this->getName() << " has died.\n";
if (this->callback_) // allow NULL pointer for no-op
this->callback_(*this);
}
std::string
getName() const
{
return this->name_;
}
private:
std::string name_;
on_dog_die_callback_t callback_;
};
// Note: This is a free function, not a class member.
void
friendly_callback(const Dog& dog)
{
std::clog << "We are so sorry that " << dog.getName() << " died.\n";
}
struct Manager
{
// Note: This is a static function.
static void
unfriendly_callback(const Dog& dog)
{
std::clog << "Haha, " << dog.getName() << " died.\n";
}
};
int
main()
{
Dog fido("Fido", friendly_callback);
Dog leika("Leika", Manager::unfriendly_callback);
Dog waldo("Waldo", 0);
}
The output of running this code is:
A new dog named Fido is born.
A new dog named Leika is born.
A new dog named Waldo is born.
A dog named Waldo has died.
A dog named Leika has died.
Haha, Leika died.
A dog named Fido has died.
We are so sorry that Fido died.
Note that I have (without need) changed the signature of your callback to accept a const reference to a Dog rather than a pointer. This will generally be considered better style but if you need a pointer for whatever reason I cannot see, you can do it as well.
Notice: If you decide to invoke the callback from your destructor, be aware that there are some unusual things to watch out for:
Dog class owns resources that are freed in the destuructor before the callback is invoked, you will pass a this pointer that points to a partially destructed object and calling any member function on it will likely invoke undefined behavior.virtual function members of the Dog it is passed. Note that Dog's destructor will only be called after the destructor of a derived class (if any) so it might deal with an already partially destructed object. As long as the callback is only interested in the Dog base, this is fine, but if it tries to access any members of the derived class (via virtual function calls), undefined behaviour will result.The good news: If your class is simple (no virtual functions, no explicit resources released in the destructor), you'll be just fine.
You have defined function a pointer to a (non member) function. You can easily verify this, if you create a dummy function outside of any class:
void gondie(Dog * dog) {}
and if you change your Manager() to:
d.SetFunc(gondie); // compile
But Manager::CbdogDie() is a member function of class Manager. To call it, you need to know which manager to use, which you don't in Dog:Die(): there you know only the function, not the object.
Edit: Additional suggestions:
You've already a very good solution for the callback approach, but for the records, I propose you another one with a slight change in design, and refering in the dog to its manager.
As the Manager is not yet known in the Dog header, we'll use an adapter class, DogManager whose only goal is to offer a dying callback function :
class Dog
{
public:
class DogManager { // adapter
public:
void CbdogDie(Dog *d) {} // empty function to call back. Will be overloaded later
};
private:
DogManager *mymanager; // Who is the master of the dog ?
public:
... // remaining of your dog definition
void SetManager(DogManager *m);
};
The implementation of Dog would change slightly:
void Dog::Die()
{
mymanager->CbdogDie(this); // call directly manager function instead of callback
}
The last thing to do is to let your Manager class to inherit from the DogManager :
class Manager : Dog::DogManager
...
and of course change its implementation to tell the dog by replacing your former d.SetFunc(...) with d.SetManager(this);
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With