I have some MVC code, it uses the observer mode, such as below:
void Model::ChangeMethod1()
{
m_A = m_A + 1;
...
Notify();
}
void Model::ChangeMethod2()
{
m_A = m_A + 2;
...
Notify();
}
void Model::ChangeMethod3()
{
ChangeMethod1();
ChangeMethod2();
Notify();
}
void Model::ChangeMethod4()
{
ChangeMethod1();
ChangeMethod2();
ChangeMethod3();
Notify();
}
There are many functions like ChangeMethodX which will make changes to the Model, and notify the viewers, and when the viewers receive the events, they will refresh/update themselves.
You see, each function ChangeMethodX has a Notify() function, which internally send an event to the observer.
But I don't want the observer receive too many events in the each function, because there will too many events, I would like each top level function call whether it has any internal function calls only send one update event to the viewer.
I think this is a very common issue that happens in many situations, such as the MVC mode, as a model will notify viewers to get updated. But we have to avoid the flooded events if the model changes several times inside a top level function call.
I thought of 2 possible approaches:
If the subject is completely under your control and this solution is not too invasive, you could add an optional parameter specifying whether the called ChangeMethodX is a top level function, like this:
void Model::ChangeMethod1(bool topLevel = true)
{
m_A = m_A + 1;
...
NotifyIfTopLevel(topLevel);
}
void Model::ChangeMethod2(bool topLevel = true)
{
m_A = m_A + 2;
...
NotifyIfTopLevel(topLevel);
}
void Model::ChangeMethod3(bool topLevel = true)
{
ChangeMethod1(false);
ChangeMethod2(false);
NotifyIfTopLevel(topLevel);
}
void Model::ChangeMethod4(bool topLevel = true)
{
ChangeMethod1(false);
ChangeMethod2(false);
ChangeMethod3(false);
NotifyIfTopLevel(topLevel);
}
void Model::NotifyIfTopLevel(bool topLevel)
{
if (topLevel)
Notify();
}
However, it is ugly most of the time and it could dirty your interface.
The second approach you could choose is on the other hand risky if you have to deal with concurrency. Moreover if you catch an exception and you handle it, you must remember to bring the object back in a correct state (is_changing-- if not called yet), otherwise the observers are not going to receive notifications anymore.
int is_changing = 0;
void Model::ChangeMethod1()
{
m_A = m_A + 1;
...
NotifyIfNotChanging();
}
void Model::ChangeMethod2()
{
m_A = m_A + 2;
...
NotifyIfNotChanging();
}
void Model::ChangeMethod3()
{
is_changing++;
ChangeMethod1();
ChangeMethod2();
is_changing--;
NotifyIfNotChanging();
}
void Model::ChangeMethod4()
{
is_changing++;
ChangeMethod1();
ChangeMethod2();
ChangeMethod3();
is_changing--;
NotifyIfNotChanging();
}
void Model::NotifyIfNotChanging()
{
if (is_changing == 0)
Notify();
}
If you have that many ChangeMethodX methods, maybe consider using an Aspect Oriented framework to separate the concern of notifying observers. Especially if you need to repeat is_changing++/-- or trivially the Notify call, moving them in the appropriate aspect class would definitely be more readable.
EDIT
As for the RAII approach, it is in my opinion kind of overused here since you do not have resources to release, creating and disposing objects every time is quite overkilling for your needs.
By the way, if you want to follow this path, then I recommend you to fix some code smells.
You are not encapsulating appropriately the SetTopLevelCall. It is not supposed to be public because the user of your class must not mess with it.
There is a new public class DeferredEventSender that is tightly coupled to your Model class. The worst part is that it is responsible for the Notify method, that should be called by the Model itself. Moreover, you are ruling out the possibility to need to access the Model private fields and functions.
Here is how I would face these issues, even though it is not perfect yet.
class Model
{
public:
Model()
{
}
~Model()
{
}
void ChangeMethod1();
void ChangeMethod2();
void ChangeMethod3();
void ChangeMethod4();
void Notify();
protected:
class DeferredEventSender
{
public:
DeferredEventSender(Model* m)
{
_m = m;
doCallNotify = _m->topLevel;
_m->topLevel = false;
}
~DeferredEventSender()
{
if (doCallNotify)
{
_m->Notify();
_m->topLevel = true;
}
}
Model* _m;
bool doCallNotify;
};
bool topLevel = true;
int m_A;
int m_B;
};
void Model::ChangeMethod1()
{
Model::DeferredEventSender sender(this);
m_A = m_A + 1;
}
...
I just follow Marco Luzzara's second approach, and create a simple demo C++ code, see below:
Revision 1:
#include <iostream>
using namespace std;
class Model
{
public:
Model()
: m_TopLevelCallScope(false)
{
}
~Model()
{
}
void ChangeMethod1();
void ChangeMethod2();
void ChangeMethod3();
void ChangeMethod4();
void Notify();
bool IsTopLevelCall()
{
return m_TopLevelCallScope;
}
void SetTopLevelCall(bool topLevel)
{
m_TopLevelCallScope = topLevel;
}
private:
// if this variable is true, it means a top level call scope is entered
// then all the inner call should not send event, the final event could
// send when the top level sender get destructed
bool m_TopLevelCallScope;
// other members
int m_A;
int m_B;
};
// this is a deferred notification
// each function should create a local object
// but only the top level object can finally send a notification
class DeferredEventSender
{
public:
DeferredEventSender(Model* model)
: m_Model(model)
{
if(m_Model->IsTopLevelCall() == false)
{
m_Model->SetTopLevelCall(true);
m_TopLevelCallScope = true;
}
else
{
m_TopLevelCallScope = false;
}
}
~DeferredEventSender()
{
if (m_TopLevelCallScope == true)
{
// we are exiting the top level call, so restore it to false
// it's time to send the notification now
m_Model->SetTopLevelCall(false);
m_Model->Notify();
}
// do nothing if m_TopLevelCallScope == false
// because this means we are in a inner function call
}
bool m_TopLevelCallScope;
Model* m_Model;
};
void Model::ChangeMethod1()
{
DeferredEventSender sender(this);
m_A = m_A + 1;
}
void Model::ChangeMethod2()
{
DeferredEventSender sender(this);
m_A = m_A + 2;
}
void Model::ChangeMethod3()
{
DeferredEventSender sender(this);
ChangeMethod1();
ChangeMethod2();
}
void Model::ChangeMethod4()
{
DeferredEventSender sender(this);
ChangeMethod1();
ChangeMethod2();
ChangeMethod3();
}
void Model::Notify()
{
cout << "Send event!" << endl;
}
int main()
{
Model m;
m.ChangeMethod1();
m.ChangeMethod2();
m.ChangeMethod3();
m.ChangeMethod4();
return 0;
}
And here is the output of the demo C++ code:
Send event!
Send event!
Send event!
Send event!
You see that in the main() function, I have only 4 function calls, and only 4 events is send.
The method I use is that I put a DeferredEventSender local object in each ChangeMethodX method, and if it is a top level function call, this object will have its member variable m_TopLevelCallScope set as true, if it is a inner function call, m_TopLevelCallScope is set as false.
When the DeferredEventSender local object leaves the scope, it will check to see if it is the top level object, if true, it will send the event, so all the inner function calls won't send events.
The demo code can be extended, so that events can be accumulated and stored in a std::queue<Event> in the DeferredEventSender object or Model object, and when the top DeferredEventSender object get destroyed, we can run a filter in the std::queue<Event>, and remove the duplicated events, and send the events we actually needed.
As suggested by Marco Luzzara, this is the modified version, thanks Marco Luzzara!
Revision 2:
#include <iostream>
using namespace std;
class Model
{
public:
Model()
{
}
~Model()
{
}
void ChangeMethod1();
void ChangeMethod2();
void ChangeMethod3();
void ChangeMethod4();
void Notify();
protected:
class DeferredEventSender
{
public:
DeferredEventSender(Model* m)
{
m_Model = m;
// the first instance of the DeferredEventSender will copy the status of m_TopLevel
// and all the later(inner) instances will have false m_TopLevel
m_DoCallNotify = m_Model->m_TopLevel;
m_Model->m_TopLevel = false;
}
~DeferredEventSender()
{
// we only call Notify on the top level DeferredEventSender
if (m_DoCallNotify)
{
m_Model->Notify();
m_Model->m_TopLevel = true;
}
}
Model* m_Model;
bool m_DoCallNotify;
};
bool m_TopLevel = true;
int m_A;
int m_B;
};
void Model::ChangeMethod1()
{
Model::DeferredEventSender sender(this);
m_A = m_A + 1;
}
void Model::ChangeMethod2()
{
Model::DeferredEventSender sender(this);
m_A = m_A + 2;
}
void Model::ChangeMethod3()
{
Model::DeferredEventSender sender(this);
ChangeMethod1();
ChangeMethod2();
}
void Model::ChangeMethod4()
{
Model::DeferredEventSender sender(this);
ChangeMethod1();
ChangeMethod2();
ChangeMethod3();
}
void Model::Notify()
{
cout << "Send event!" << endl;
}
int main()
{
Model m;
m.ChangeMethod1();
m.ChangeMethod2();
m.ChangeMethod3();
m.ChangeMethod4();
return 0;
}
Related
I have this code (its a smol version of the code that replicates the error) and it gives a some kind of error with memory. idk just pls help me fix it. It deletes the object so there remains just the nullptr. Idk why but it doesn't want to remove the pointer from the list.
#include <iostream>
#include <list>
// casual include
here i create a class thats the base for all of my other classes
class Object // a virtual class
{
public:
bool needs_delete = false;
virtual void tick() {}
virtual void render() {}
};
a player class that inherits from the Object class i created earlier
class Player : public Object
{
public:
float x, y; // <-- just look at da code dont read dis
Player(float x, float y) : // i initialize the "x" & "y" with the x & y the user has set in the constructor
x(x), y(y)
{}
void tick() override // just look at the code
{
x++;
if (x > 10000)
{
needs_delete = true;
}
}
void render() override // just look at the code
{
// nothing...
}
};
just the main function. at this point im just writing text because stackoverflow wont let me post this piece of constant depression. pls help :)
int main()
{
std::list<Object*>* myObjs = new std::list<Object*>; // a list that will contain the objects
for (int i = 0; i < 1000; i++) // i create 1k player just for testing
{
myObjs->push_back(new Player(i, 0));
}
while (true)
{
if (myObjs->size() == 0) // if there are no objects i just break out of the loop
break;
for (Object* obj : *myObjs) // update the objects
{
obj->tick();
obj->render();
// some other stuff
}
// DA PART I HAVE NO IDEA HOW TO DO
// pls help cuz i suck
for (Object* obj : *myObjs) // help pls :)
{
// baisicly here i want to delete the object and remove it from the list
if (obj->needs_delete)
{
std::cout << "deleted object\n";
delete obj;
myObjs->remove(obj);
}
}
}
}
What about:
myObjs->remove_if([](auto& pObj)
{
if ( pObj->needs_delete )
{
delete pObj;
return true;
}
else
return false;
});
I am new to QT started to try out some things with QMap as it seems like a useful tool. I already read some other forum threads but I wasnt able to answer my question. The user is supposed to add and edit different shapes via the GUI. For the shapes I first wrote an abstract base class:
#ifndef BASE_H
#define BASE_H
#include <QListWidget>
#include <QMap>
class Base
{
protected:
QVariantMap m_properties;
QString m_key;
void setKey(const QString& key){
this->m_key=key;
}
public:
Base(){}
virtual ~Base(){}
QString key(){
return m_key;
}
void setProperty(QString key, QVariant variant){
m_properties[key]=variant;
}
virtual void toRTC()=0;
};
#endif // BASE_H
one example of a subclass is an ellipse with the following cpp file:
#include "ellipse.h"
Ellipse::Ellipse(int Start_x, int Start_y, int Rad_x, int Rad_y): rad_x(Rad_x), rad_y(Rad_y), start_x(Start_x), start_y(Start_y)
{
this->setKey("ellipse");
this->setProperty("radX", rad_x);
this->setProperty("radY", rad_y);
this->setProperty("startX", start_x);
this->setProperty("startY", start_y);
}
void Ellipse::set_rad_x(int rad_x)
{
Base::setProperty("radX", rad_x);
}
void Ellipse::set_rad_y(int rad_y)
{
Base::setProperty("radY", rad_y);
}
void Ellipse::set_start_x(int start_x)
{
Base::setProperty("startX", start_x);
}
void Ellipse::set_start_y(int start_y)
{
Base::setProperty("startY", start_y);
}
int Ellipse::get_rad_x()
{
return m_properties["radX"].toInt();
}
int Ellipse::get_rad_y()
{
return m_properties["radY"].toInt();
}
int Ellipse::get_start_x()
{
return m_properties["startX"].toInt();
}
int Ellipse::get_start_y()
{
return m_properties["startY"].toInt();
}
First off, is this a correct approach for the cpp file? I feel my approach is vary laborious.
In my main window file I thought about storing my data in a simple Vector QVector<Base *> data_storage;
Ellipse *e_values = new Ellipse(ui->Ellipse_Value_start_x->value(),ui->Ellipse_Value_start_y->value(), ui->Ellipse_Value_rad_x->value(),ui->Ellipse_Value_rad_y->value());
data_storage.append(e_values);
To load the data, I thought it would be a good idea to use the key to check which object of data_storage I want to load, but I don't really know how I can access the data which is connected to my key.
if(data_storage[t]->key()=="ellipse"){
ui->Ellipse_Value_rad_x->setValue(data_storage[t]->) //how can I access the correct data?
}
I feel like I messed up the entire approach, like how to properly use keys, so how would I do that?
You need to declare all the behaviours you need in the base class. I don't see the need to have Base hold the data.
E.g. if you need to be able to read and write the UI, there should be methods to do that.
class Base
{
public:
virtual ~Base() = default;
virtual void toRTC() = 0;
virtual QVariantMap properties() const = 0;
virtual void writeUI(form_t * ui) const = 0;
virtual void readUI(const form_t * ui) = 0;
};
class Ellipse : public Base
{
int start_x;
int start_y;
int rad_x;
int rad_y;
public:
void toRTC() final { /* ??? */ }
QVariantMap properties() const final {
return { { "radX", rad_x }, { "radY", rad_y },
{ "startX", start_x }, { "startY", start_y } };
}
void writeUI(form_t * ui) const final {
ui->Ellipse_Value_rad_x->setValue(rad_x);
ui->Ellipse_Value_rad_y->setValue(rad_y);
ui->Ellipse_Value_start_x->setValue(start_x);
ui->Ellipse_Value_start_y->setValue(start_y);
}
void readUI(const form_t * ui) final {
rad_x = ui->Ellipse_Value_rad_x->value().toInt();
rad_y = ui->Ellipse_Value_rad_y->value().toInt();
start_x = ui->Ellipse_Value_start_x->value().toInt();
start_y = ui->Ellipse_Value_start_y->value().toInt();
}
};
If you don't want to tie your shapes to the UI, you could define a visitor interface, with a visit method for each shape type.
class ShapeVisitor
{
virtual void accept(Ellipse * ellipse) = 0;
/* virtual void accept(Rectangle * rectangle) = 0; // etc.. */
};
class Base
{
public:
virtual ~Base() = default;
virtual void toRTC() = 0;
virtual QVariantMap properties() const = 0;
virtual void visit(ShapeVisitor & visitor) = 0;
};
class Ellipse : public Base
{
public:
int start_x;
int start_y;
int rad_x;
int rad_y;
void toRTC() final { /* ??? */ }
QVariantMap properties() const final {
return { { "radX", rad_x }, { "radY", rad_y },
{ "startX", start_x }, { "startY", start_y } };
}
void visit(ShapeVisitor & visitor) final {
visitor.accept(this); // calls visitor::accept(Ellipse *)
}
};
class UIReadVisitor : public ShapeVisitor
{
form_t * ui
void accept(Ellipse * ellipse) final {
ellipse->rad_x = ui->Ellipse_Value_rad_x->value().toInt();
ellipse->rad_y = ui->Ellipse_Value_rad_y->value().toInt();
ellipse->start_x = ui->Ellipse_Value_start_x->value().toInt();
ellipse->start_y = ui->Ellipse_Value_start_y->value().toInt();
}
}
class UIWriteVisitor : public ShapeVisitor
{
form_t * ui;
void accept(Ellipse * ellipse) final {
ui->Ellipse_Value_rad_x->setValue(ellipse->rad_x);
ui->Ellipse_Value_rad_y->setValue(ellipse->rad_y);
ui->Ellipse_Value_start_x->setValue(ellipse->start_x);
ui->Ellipse_Value_start_y->setValue(ellipse->start_y);
}
}
I have a class called StateMachine, which controls all possible Entity States.
I then have a class called State which is a base class for unique Entity classes, e.g Attack State, Flee State.
When a new unique State is created (within the StateMachine), it passes in a StateMachine pointer, which will then be stored in the State base class, so that each unique State created can access its State Machine.
When I attempt to access the pointers members (using -> operator) it simple doesn't come up with any public methods, and I don't know why.
If anyone has any clue it would be greatly appreciated.
StateMachine.h
using STATE_PTR = std::shared_ptr<State>;
// Class to implement a finite state machine using the state desing pattern
class StateMachine
{
public:
StateMachine();
~StateMachine();
void OnEnter(STATE_NAME sn);
void OnExit();
void OnEvent(STATE_SYMBOL & ss);
void OnTick(float st);
void ChangeState(STATE_NAME const & sn);
void RegisterState(ENTITY_CLASS const & ec);
typedef std::map<STATE_NAME, STATE_PTR> STATE_REGISTRY;
private:
STATE_REGISTRY state_registry;
STATE_NAME current_state;
};
StateMachine.cpp
using namespace::std;
StateMachine::StateMachine()
: state_registry()
{
current_state = STATE_NAME::UNKNOWN;
}
StateMachine::~StateMachine()
{
state_registry.clear();
}
void StateMachine::OnEnter(STATE_NAME sn)
{
current_state = sn;
if (state_registry[current_state] != nullptr)
{
state_registry[current_state]->OnEnter();
}
}
void StateMachine::OnExit()
{
if (state_registry[current_state] != nullptr)
{
state_registry[current_state]->OnExit();
}
}
void StateMachine::OnTick(float st)
{
}
void StateMachine::OnEvent(STATE_SYMBOL & ss)
{
state_registry[current_state]->OnEvent(ss);
}
void StateMachine::RegisterState(ENTITY_CLASS const & ec)
{
switch (ec)
{
case ENTITY_CLASS::PLAYER_TANK :
state_registry.insert(std::make_pair(STATE_NAME::STATE_1, std::make_shared<PlayerTankState1>(this)));
state_registry.insert(std::make_pair(STATE_NAME::STATE_2, std::make_shared<PlayerTankState2>(this)));
break;
case ENTITY_CLASS::ENEMY_TANK :
state_registry.insert(std::make_pair(STATE_NAME::STATE_3, std::make_shared<EnemyTankState1>(this)));
state_registry.insert(std::make_pair(STATE_NAME::STATE_4, std::make_shared<EnemyTankState2>(this)));
state_registry.insert(std::make_pair(STATE_NAME::STATE_5, std::make_shared<EnemyTankState3>(this)));
break;
default:
break;
}
}
void StateMachine::ChangeState(STATE_NAME const & sn)
{
state_registry[current_state]->OnExit();
current_state = sn;
state_registry[current_state]->OnEnter();
}
State.h
class StateMachine; // Forward decloration of the StateMachine class
// Base class for all states of the game system
class State
{
protected:
State(StateMachine * p)
: mp_Owner(p)
{}
public:
virtual ~State() {}
virtual void OnEnter() = 0;
virtual void OnExit() = 0;
virtual void OnTick(float) = 0;
virtual void OnEvent(STATE_SYMBOL) = 0;
StateMachine * mp_Owner;
};
EnemyTankState.cpp (Unique State)
EnemyTankState1::EnemyTankState1(StateMachine * p)
: State(p)
{
}
EnemyTankState1::~EnemyTankState1()
{
}
void EnemyTankState1::OnEnter()
{
cout << "Hi From Enemy Tank: Partolling State" << endl;
}
void EnemyTankState1::OnExit()
{
cout << "Bye From Enemy Enemy Tank: Partolling State" << endl;
}
void EnemyTankState1::OnTick(float dt)
{
}
void EnemyTankState1::OnEvent(STATE_SYMBOL ss)
{
switch (ss)
{
// Takes Enemy Tank to Attacking State
case STATE_SYMBOL::SYMBOL_2 :
mp_Owner->
break;
}
}
Within the code sample above, the line mp_Owner-> is what is giving me grief, as it is not opening up a list of public methods as you would expect when using a class pointer.
Any help would be much appreciated. Sorry for the long chunks of code, I couldn't think of any other way of getting my problem across.
What is a good way to make a polymorph Task interface?
I want to design an interface for a processing library, which takes different data objects.
All data objects inherit from a IDataObject class.
The objects should be passed to the task throug a setInput(IDataObject* input) function.
I want to be able to define which kind of data object is needed. Maybe through a function setRequieredInput(DataObjectBase* input).
It should be possible to find Tasks which are able to process a data object maybe like this vector<Task*> getTasks(DataObjectBase* input). Like a Microkernel (Dependency Container)?
All suggestions are welcome.
I listed an first shoot below:
class IDataObject {
// Visitor style implementation
virtual void multiDispatch(Task* task) = 0;
}
class ISingleAccessDataObject : IDataObject {
// Visitor style implementation
// would this work orcan't I use the definition derived
// virtual function for the "interface" and class?
// If I'm right this is the diamond problem (inheriting the same
// function twice).
virtual void multiDispatch(Task* task) = 0;
double getData(int i) = 0;
}
class DataObjectBase : IDataObject {
// Visitor style implementation
virtual void multiDispatch(Task* task) {
task->execute(this);
}
// ...
}
class DataObjectBase {
// Visitor style implementation
virtual void multiDispatch(Task* task) {
task->execute(this);
}
// ...
}
class DerivedDataObject : DataObjectBase, ISingleAccessDataObject {
// Visitor style style Implementation
virtual void multiDispatch(Task* task) {
task->execute(this);
}
// ...
}
class Task {
void setInput(DataObjectBase* input) {
if (checkCanHandle(input) {
m_inputs.push_back(input);
}
}
void execute() {
for (int i = 0; i < m_Inputs.size(); i++) {
IDataObject* dataObject = m_Inputs.at(i);
if (dataObject == NULL) {
continue;
}
//-----------------------
// Visitor style implementation
dataObject.multiDispatch(this);
//-----------------------
// or
//-----------------------
if (Derived1DataObject* derived1DataObject = dynamic_cast<Derived1DataObject>(dataObject )) {
// ...
} else if (ISingleAccessDataObject* singleAccessDataObject = dynamic_cast<SingleAccessDataObject>(dataObject)) {
// ...
} else { //Log error }
//-----------------------
}
}
// Visitor style implementation
//-----------------------
// This would require to know all DataObjects on defining the
// Task "Interface". What is not really what I want.
void execute(Derived1DataObject* input) {
// ..
}
void execute(Derived2DataObject* input) {
// ..
}
void execute(ISingleIndexAccessDataObject* input) {
// ..
}
//-----------------------
bool checkCanHandle(DataObjectBase* input) {
for (int i = 0; i < m_HandleDataObjects.size(); i++) {
// Question: Is there a way to let this be true if
// m_HandleDataObjects.at(i) is a super class of input?
// Question: Is there a way to allow only save something like
// the typeid. Maybe name, but I'm not sure if name is the best
// option.
if (typeid(m_HandleDataObjects.at(i)) == typeid(input)) {
return true;
}
}
}
vector<DataObjectBase*> m_HandleDataObjects;
vector<DataObjectBase*> m_Inputs;
}
class TaskRegister {
vector<Task*> getTasks(IDataObject* input) {
vector<Task*> ret;
for (int i = 0; i < m_Tasks.size(); i++) {
// Question: Is there a better way using a HashMap or something else
if (m_Tasks.at(i).checkCanHandle(input)) {
ret.push_back(m_Tasks.at(i));
}
}
return ret;
}
vector<Task*> m_Tasks;
}
I am trying to implement a simple GUI component in C++. The 'Button' class exists in a library that is completely de-coupled from the main program. I want to be able to pass a function-pointer to a button that can be ran upon clicking.
I had some success until I moved the 'button' from being a struct to class for readability and expandability. I reckon that it only worked by pure chance as now I get very random problems.
I basically have something like this:
typedef void(BaseMenu::*ClickAreaCallback)(Button*);
struct Message{
ClickAreaCallback func;
Button* clickArea;
BaseMenu* funObj;
};
The from my classes that subclass BaseMenu, I do something like this:
cb = (ButtonContainer::ClickAreaCallback)&TileSelectorScreen::setTileMode;
and set:
ClickAreaCallback to &cb (as well as funObj)
I then run it upon 'click' by doing:
m->funObj->*m->func)(m->clickArea);
This is obviously wrong as I've read there are problems passing non-static member functions and expecting them to run.
So, is what I am doing impossible? Is what I want possible by using plain C++ without boost or using -std=c++11. I'm limiting myself to the very basics so I can still compile for many platforms.
In short: I want a simple method of calling functions from a class that knows nothing of the class it's calling.
thanks in advance.
In principle there is nothing wrong with pointers to members.
See, e.g., the following code:
#include <iostream>
/** Some API */
struct Button {
virtual void OnClick() = 0;
};
struct BaseMenu {
void f1(Button* b) {
std::cout << "f1(Button*)\n";
b->OnClick();
}
void f2(Button* b) {
std::cout << "f2(Button*)\n";
b->OnClick();
}
void Update() {
}
};
typedef void(BaseMenu::*ClickAreaCallback)(Button*);
struct Message{
ClickAreaCallback func;
Button* clickArea;
BaseMenu* funObj;
};
/** Usage */
class OKButton : public Button {
void OnClick() {
std::cout << "OKButton::OnClick()\n";
}
};
int main(int nArg, char* args[]) {
// Fill message:
BaseMenu menu;
OKButton ok;
Message m1, m2;
m1.func = &BaseMenu::f1;
m1.funObj = &menu;
m1.clickArea = dynamic_cast<Button*>(&ok);
m2.func = &BaseMenu::f2;
m2.funObj = &menu;
m2.clickArea = dynamic_cast<Button*>(&ok);
(m1.funObj ->* m1.func)(m1.clickArea);
(m2.funObj ->* m2.func)(m2.clickArea);
}
But it looks like a conceptional error. You should not need the callback. The buttons should be derived from a base class and have virtual member functions that do the specific stuff.
There follows an example demonstrating the usage of inheritance instead of callbacks.
Note, that ButtonToggle is an example for storing the information inside the button and ButtonNotify is an example for the button notifying the menu.
#include <iostream>
#include <vector>
/** Some API */
struct Button {
double _area[4]; // rectangle x1,y1,x2,y2
Button(std::initializer_list<double> area) {
std::copy(area.begin(),area.begin()+4,_area);
}
virtual void OnClick() = 0;
};
class BaseMenu {
protected:
std::vector<Button*> _buttons;
public:
void Register(Button* btn) {
_buttons.push_back(btn);
}
void OnClick(double pt[2]) {
for(auto iBtn = _buttons.begin(); iBtn!=_buttons.end(); iBtn++) {
if( (*iBtn)->_area[0] <= pt[0] && pt[0] <= (*iBtn)->_area[2]
&& (*iBtn)->_area[1] <= pt[1] && pt[1] <= (*iBtn)->_area[3] ) {
(*iBtn)->OnClick();
}
}
}
};
struct MyMenu : public BaseMenu {
struct ButtonToggle: public Button {
bool _val;
ButtonToggle() :
Button( {0.0,0.0,1.0,1.0} )
{
_val = false;
}
void OnClick()
{
std::cout << "ButtonToggle::OnClick()\n";
_val = not(_val);
}
} buttonToggle;
void DoSomething() {
std::cout << "DoSomething()\n";
}
struct ButtonNotify: public Button {
MyMenu& _myMenu;
ButtonNotify(MyMenu& myMenu) :
Button( {2.0,0.0,3.0,1.0} ),
_myMenu(myMenu)
{}
void OnClick() {
_myMenu.DoSomething();
}
} buttonNotify;
MyMenu() :
buttonNotify(*this)
{
Register(&buttonToggle);
Register(&buttonNotify);
}
};
int main(int nArg, char* args[]) {
MyMenu menu;
double pt[2];
while(( std::cout << "\nCoordinates (end: -1 -1):",
std::cin >> pt[0] >> pt[1],
not( pt[0] == -1.0 and pt[1] == -1.0 ) )) {
menu.OnClick(pt);
}
}
/*
Local Variables:
compile-command: "g++ -g -std=c++11 test1.cc"
End:
*/