C ++ element function pointers in class and subclass

I have one base class that contains map for function pointers like this

 typedef void (BaseClass::*event_t)(); class BaseClass { protected: std::map<std::string, event_t> events; public: // Example event void onFoo() { // can be added easily to the map } }; 

Handling this works as a prefect, but now I want to make BaseClass abstract base class to extract from this:

  class SpecificClass : public BaseClass { public: void onBar() { // this is gonna be difficult! } }; 

Although I can access the map from SpecificClass , I cannot add onBar because the event_t type event_t defined only for BaseClass ! Is there a possibility (possibly with templates?) Which does not lead to event_t definition for each class that I will use ...

(No need to use templates! Any good / suitable approach would be nice.)

Additional reference information:

All this for textual RPG. My base class could be called Location and specify any place, for example. CivicCenter . Each Location object subscribes to my EventSystem , which notifies all the necessary objects when I fire the event. Therefore, I want to save on the map some pointers to private functions that contain actions with their "name", for example, onSetOnFire (xD) as a key.

+7
source share
4 answers

After some thought and redesign, I was able to achieve what I wanted. Although I am stubborn and still use inheritance, I revised the map. Here's how it works now:

 class Location { // ... protected: std::map<std::string, std::function<void(void)>> m_mEvents; }; 

And now I can handle it as follows:

 class CivicCenter : public Location { public: CivicCenter() { // this is done by a macro which lookes better than this this->m_mEvents["onTriggerSomething"] = std::bind(&CivicCenter::onTriggerSomething, this); } void onTriggerSomething() { // ... } // ... }; 

With the easy use of std::bind I can implement generic function pointers. When using parameters like std::function<void(int, int)> remeber, use either boost _1 and _2 , or lambda expressions like me:

 std::function<void(int,int)> f = [=](int a, int b) { this->anotherFunctionWithParams(a, b); }; 

But this is simply indicated due to the completeness of my decision.

+2
source

This cannot be done with the current map in its current form. Think about what happens if you can put a child’s method on the map. Then you can pull the pointer-to-child element (masquerading as a base) from the map, call it the instance pointer of the base class, and then how it will call the derived class on the base class instance, which obviously could not work.

Will a polymorphic approach work?

+2
source

Yes; stop using item pointers.

A more proper way to do what you want is with the event type and object pointer. Thus, the event is fired on a specific object. The type of event will be a function that is not a member (or a static member). It will be passed by the pointer of the object. And it will call some real member function of this object.

Currently, the event type may be std/boost::function . However, since function parameters must remain the same type for all events, this does not actually fix your problem. You cannot call SpecificClass::onBar from a BaseClass pointer unless you cast to SpecificClass . And the event call function will not know to do this. Thus, you still cannot put SpecificClass::onBar in a std/boost::function ; you still need a separate feature to make the roll for you.

This is just a terrible use of polymorphism. Why does SpecificClass need to be inferred from BaseClass at all? Can't they be two unrelated classes?

+2
source

You should use static_cast :

 event_t evt = static_cast<event_t>(&SpecificClass::onBar); 

This is because it is slightly dangerous to use for event_t , you may accidentally apply it to an instance of BaseClass .

How it works (for the skeptic):

 class BaseClass { public: typedef void (BaseClass::*callback_t)(); // callback method void doSomething(callback_t callback) { // some code this->*callback(); // more code } void baseCallback(); // an example callback }; class DerivedClass : public BaseClass { public: void derivedCallback(); void doWhatever() { // some code doSomething(&BaseClass::baseCallback); // more code doSomething(static_cast<callback_t>(&DerivedClass::derivedCallback)); // et cetera }; 

Here's what you should avoid, and why it is potentially dangerous:

 void badCodeThatYouShouldNeverWrite() { BaseClass x; // DO NOT DO THIS IT IS BAD x.doSomething(static_cast<callback_t>(&DerivedClass::derivedCallback)); } 

The requirement for static_cast makes it so that you cannot "accidentally" pass the pointers to the DerivedClass method. And if you think this is dangerous, just remember that it is a pointer, and pointers are always dangerous. Of course, there are ways to do this that include creating helper classes, but this requires a lot of extra code (maybe creating a class for each function that you want to pass as a callback). Or you can use closure in C ++ 11 or something from Boost, but I understand that many of us do not have this option.

+1
source

All Articles