-1

I'm working on a game project that features scratch-built controls rendered into an opengl context; things like buttons, scrollbars, listboxes, etc. Many of these controls are nested; for example, my listbox has a scrollbar, a scrollbar has 3 buttons, etc.

When a scrollbar changes value, I'd like it to call 'some' function (typically in it's parent object) that responds to the change. For example, if the listbox has a slider, it should instantiate the slider, then tell the new slider that it should call the listboxes 'onScroll(float)' function. All of the controls share a common base class, so I could have a 'base* parent' parent pointer, then do 'parent->onScroll(val)'. The problem though is what happens when the parent doesn't inheirit from base; there'd be no virtual onScroll() to follow through, so the top-level parent would have to periodically check to see if any of the child controls had changed value. This would also clutter up other controls, since they may not even have children, or may require different event types like when a list entry object is selected, etc.

A better solution would be to have the child object maintain a generic function pointer (like a callback), which can be set by the parent, and called by the child as necessary. Something like this:

typedef (*ptFuncF)(float);

class glBase {
public:
  //position,isVisible,virtual mouseDown(x,y),etc
};

class glDerivedChild : public glBase {
public:
  glDerivedChild();
  ~glDerivedChild();

  void changeValue(float fIn) {
    Value = fIn; //ignore these forward declaration errors
    (*callBack)(fIn);
  }

  void setCallBack(ptFuncF pIn) {callBack = pIn;}

  ptFuncF callBack;
  float Value;
};

class glDerivedParent : public glBase {
public:
  glDerivedParent() {
    child = new glDerivedChild();
    child->setCallBack(&onScroll);
  }
  ~glDerivedParent() {delete child;}

  void onScroll(float fIn) {
    //do something
  }

  glDerivedChild* child;
};

class someFoo {
public:
  someFoo() {
    child->setCallBack(&setValue);
  }

  void setValue(float fIn) {
    //do something else
  }

  glDerivedChild child;
};

I'm kinda new to function pointers, so I know I'm (obviously) doing many things wrong. I suspect it might involve something like "typedef (glBase::*ptFuncF)(float);" with the 'onScroll(f)' being an overridden virtual function, perhaps with a generic name like 'virtual void childCallBack(float)'. I'd prefer to keep the solution as close to vanilla as possible, so I want to avoid external libraries like boost. I've been scratching my head over this one for the better part of 8 hours, and I'm hoping someone can help. Thanks!

Ghost2
  • 536
  • 3
  • 13

4 Answers4

1

I think, what you want is some kind of events or signals mechanism.

You can study, how event processing is organized on Windows, for example. In short, your scrollbar generates new event in the system and then system propagates it to all elements, registered in the system.

More convenient mechanism is signal/slot mechanism. Boost or Qt provides such tools. I'll recomend this solution.

But if you still want to use just callbacks, I'll recommend using std::function (boost::function) (combined with std::bind (boost::bind), when required) instead of raw function pointers.

Lol4t0
  • 12,444
  • 4
  • 29
  • 65
  • I'm currently using qt, but I plan to eventually strip out most of the qt stuff due to the overhead and the extra dlls; for example, I plan to replace the QGLWidget I'm using now with GLUT later. Since these controls are mostly display related, I can probably get away with using a base class pointer to the parent, then call a suitable virtual function. As for interacting with the main widget, the base class should be small enough that I can just use multiple inheritance and fudge it through that way. I just wish function pointers didn't have to be so type specific... – Ghost2 Jul 08 '12 at 11:59
1

Use boost::function (or std::function if available). Like this (using your notation):

typedef std::function<void (float)> ptFuncF; 
//...
void setCallBack(const ptFuncF &pIn);
//...
child->setCallBack(std::bind(&glDerivedParent::onScroll, this, _1)); 
//...
child->setCallBack(std::bind(&someFoo::setValue, this, _1)); 
Igor R.
  • 14,716
  • 2
  • 49
  • 83
1

A function pointer to a member function of a class has such a type:

<return type> (<class name>::*)(<arguments>)

For example:

typedef void (glBase::*ptFuncF)(float);
        ^^^^
      by the way, you have forgot the `void` in your `typedef`

ptFuncF func = &glDerivedChild::onScroll;

And you use it like this:

glDerivedChild c;
(c.*func)(1.2);

In your particular example, the function is a member of the derived class itself, therefore you should call it like this:

(c.*c.callback)(1.2);

the inner c.callback is the function pointer. The rest is exactly as above, which is:

(class_instance.*function_pointer)(arguments);

You might want to take a look at this question also.

Community
  • 1
  • 1
Shahbaz
  • 46,337
  • 19
  • 116
  • 182
-1

Ok, the workaround I came up with has some extra overhead and branching, but is otherwise reasonable.

Basically, each callback function is implemented as a virtual member function that recieves the needed parameters including a void* pointer to the object that made the call. Each derived object also has a base-class pointer that refers to the object that should recieve any events that it emits (typically its parent, but could be any object that inheirits from the base class). In case the control has multiple children, the callback function uses the void* pointer to distinguish between them. Here's an example:

class glBase {
public:
  virtual onChildCallback(float fIn, void* caller);

  glBase* parent;
};

class glSlider : public glBase {
public:
  glSlider(glBase* parentIn);

  void changeValue(float fIn) {
    Value = fIn;
    parent->onChildCallback(fIn, this);
  }

  float Value;
};

class glButton : public glBase {
public:
  glButton(glBase* parentIn);

  void onClick() {
    parent->onChildCallback(0, this);
  }
};

class glParent : public glBase {
public:
  glParent(glBase* parentIn) : parent(parentIn) {
    childA = new glSlider(this);
    childB = new glButton(this);
  }

  void onChildCallback(float fIn, void* caller) {
    if (caller == childA) {
      //slider specific actions
    } else if (caller == childB) {
      //button specific actions
    } else {
      //generic actions
    }
  }

  glSlider* childA;
  glButton* childB;
};

Besides a reasonably small amount of overhead, the scheme is flexible enough that derived classes can ignore certain components or omit them altogether. I may go back to the function pointer idea later (thanks shahbaz), but half the infrastructure is the same for both schemes anyway and the extra overhead is minimal, especially since the number and variety of controls will be rather small. Having the callback function use a nested response is actually a little better since you don't need a separate function for each child object (eg onUpButton, onDownButton, etc).

Ghost2
  • 536
  • 3
  • 13