callbacks?

Some time ago I wrote a class that has the ability to use a callback. I remeber testing it and thinking, "Cool it works" and that was that.

Now, years later I find I need to use such functionality and I've forgotten how to do the callback thing.

// ***************************************************************
// Base class for an object that can be drawn on the screen.
// Possibly clicked on.
// ***************************************************************

class drawObj : public rect, public dblLinkListObj {

	public:
  				drawObj();
  				drawObj(rect* inRect,bool inClicks=false);
  				drawObj(int inLocX, int inLocY, int inWidth,int inHeight,bool inClicks=false);
	virtual	~drawObj();
    
   virtual	bool	wantRefresh(void);
   virtual	void	setNeedRefresh(void);
	virtual	void	setLocation(int x,int y);
   virtual	void  draw(void);                    		// Call this one. Don't inherit this one.
   virtual 	void  drawSelf(void);                		// Inherit this one and make it yours.
	virtual	void	setFocus(bool setLoose);				// We are either getting or loosing focus.
  		    	void	clickable(bool inWantsClicks);
   virtual 	bool	acceptClick(point where);
   virtual 	void  clickOver(void);
   virtual	void  doAction(void);      					// Override me for action!
          	void	setCallback(void(*funct)(void));		// Or use a callback?
  
protected:
  bool	needRefresh;
  bool	focus;
  bool	wantsClicks;
  bool	clicked;
  void	(*callback)(void);
};

Its the last method on the list..

The trouble is how to use it?

  closeBtn* aCloseBtn = new closeBtn(5,293);
  aCloseBtn->begin();
  aCloseBtn->setCallback(&breakout::closeCallback());  //<<- What is the syntax for this?
  addObj(aCloseBtn);

Somewhere down the list I have..

void breakout::closeCallback(void) { needClose = true; }

I'm getting a headache trying to understand the stack overflow explanatoins.

Thanks!

-jim lee

  aCloseBtn->setCallback(breakout::closeCallback);

breakout has to be a class and closeCallback has to be a static method. (untested)

Breakout is just the main class that is a breakout game. The callback is when someone clicks the "X" go away button and it needs to call the method to set the bool "needToClose" No interrupts or anything fancy like that. Both just member functions of different objects.

I've been playing around with ideas for a common set of buttons to do different functions. One of the main ones is to close the widow/process. Do I do it nicely and let them install a callback? Or just go the the base class and call its close() method. Basically pulling the plug on the poor thing.

The callback just seems too complicated. I'm leaning toward calling the base class's close() and be done with it.

-jim lee

You are using virtual methods and multiple inheritance. The next logical step is to use interfaces which would make what you are trying to do quite easy.

What do you mean by these.. "interfaces"?

-jim lee

#include <iostream>

class MyClass;

struct Callbacks { // This is an abstract class that provides pure virtual callback methods (interface in Java)
    virtual void callbackA(MyClass &instance) = 0;
    virtual void callbackB(MyClass &instance) = 0;
    virtual ~Callbacks() = default; // Always use a virtual destructor if you're going to inherit from a class, because you might be deleting an instance using a base class pointer
};

class MyClass {
    private:
        const int id;
        Callbacks *callbacks = nullptr;

    public:
        MyClass(int id) : id{id} {}
        int getID() const { return id; }
        void doActionA() {
            if (callbacks)
                callbacks->callbackA(*this);
        }
        void doActionB() {
            if (callbacks)
                callbacks->callbackB(*this);
        }
        void setCallbacks(Callbacks *callbacks) { this->callbacks = callbacks; }
        void setCallbacks(Callbacks &callbacks) { setCallbacks(&callbacks); }
};

struct MyCallbacks : public Callbacks { // This is an implementation of the callbacks
    void callbackA(MyClass &instance) override {
        std::cout << "Callback A called from instance #" << instance.getID() << std::endl;
    }
    void callbackB(MyClass &instance) override {
        std::cout << "Callback B called from instance #" << instance.getID() << std::endl;
    }
};

int main() {
    MyCallbacks cb;
    MyClass instance1 = 1;
    MyClass instance2 = 2;
    instance1.setCallbacks(cb);
    instance2.setCallbacks(cb);
    instance1.doActionA();
    instance1.doActionB();
    instance2.doActionB();
    instance2.doActionA();
}

This prints:

Callback A called from instance #1
Callback B called from instance #1
Callback B called from instance #2
Callback A called from instance #2

Do note that the Callbacks object must outlive the MyClass instances!
A possible solution is to make a bidirectional relationship, i.e. the callbacks have a pointer to their MyClass instance. Then in the constructor of Callbacks, set the MyClass callbacks to null.
Alternatively, keep all MyClass instances in a linked list, and in the Callbacks destructor, traverse the list, and clear the callbacks of all MyClass instances that have this Callbacks instance as their callbacks.

For example:

#include <iostream>
#include <memory>

class MyClass;

struct Callbacks { // This is an abstract class that provides virtual callback functions (interface in Java)
    virtual void callback(MyClass &instance) = 0;
    virtual ~Callbacks();
};

class MyClass {
    private:
        const int id;
        Callbacks *callbacks = nullptr;
        MyClass *next;
        static MyClass *head;

    public:
        MyClass(int id) : id{id} {
            // prepend this to linked list
            this->next = head;
            head = this;
        }
        ~MyClass() {
            // remove this from linked list
            for (MyClass **p = &head; *p != nullptr; p = &(*p)->next)
                if (*p == this) {
                    *p = this->next;
                    break;
                }
        }

        int getID() const { return id; }
        void doAction() {
            if (callbacks)
                callbacks->callback(*this);
            else
                std::cout << "Instance #" << getID() << " has no callbacks" << std::endl;
        }
        void setCallbacks(Callbacks *callbacks) { this->callbacks = callbacks; }
        void setCallbacks(Callbacks &callbacks) { setCallbacks(&callbacks); }

        static void removeCallbacksFromAll(Callbacks *callbacks) {
            for (MyClass *p = head; p != nullptr; p = p->next)
                if (p->callbacks == callbacks)
                    p->callbacks = nullptr;
        }
};

MyClass *MyClass::head = nullptr;

Callbacks::~Callbacks() {
    MyClass::removeCallbacksFromAll(this);
}

struct MyCallbacks : public Callbacks { // This is an implementation of the callbacks
    void callback(MyClass &instance) override {
        std::cout << "Callback called from instance #" << instance.getID() << std::endl;
    }
};

int main() {
    auto cb1 = std::make_unique<MyCallbacks>();
    auto cb2 = std::make_unique<MyCallbacks>();
    MyClass instance1 = 1;
    MyClass instance2 = 2;
    instance1.setCallbacks(cb1.get());
    instance2.setCallbacks(cb2.get());
    instance1.doAction();
    instance2.doAction();
    std::cout << "Deleting callback #1" << std::endl;
    cb1.reset(); // Deleting the first callback should remove it from instance1 as well
    instance1.doAction();
    instance2.doAction();
}

Prints:

Callback called from instance #1
Callback called from instance #2
Deleting callback #1
Instance #1 has no callbacks
Callback called from instance #2

Pieter