0

I am working on my own GUI framework using C++ and OpenGL. My experience in both is small as this is my first project using them.

In the framework I have created a window, which renders the design of the window, and then any objects that have been added to the window as a child.

I then create the window using a new class called MainMenu that inherits from my window class, I then add a new button to the MainMenu

Using:

button->SetOnClick(&MainMenu::button_Click, this);

I then add a pointer to the function, as well as the instance of the class to the buttons 'SetOnClick' method - Therefore if that button is clicked a method within the MainMenu will run.

This works great at the moment, however my issue now is that I want to add a new window called Settings, which will also inherit from the Window class

Therefore I need to make the function pointer in my button class work for any number of classes (all of type Window)

Here is the code that is currently working - but for only the MainMenu (Please note that I have stripped out a lot of methods that do not relate to the question to make it easier to read)

Window.h:

class Window
{
   private:
void    Initialize(WindowManager* manager, int width, int height);

public:
  Window(WindowManager* manager);
  Window(WindowManager* manager, int width, int height);
  ~Window(void);

  void  Render();
  bool  MouseMove(int x, int y);
  bool  MouseLBDown(int x, int y);


  Children* Child() { return m_children; };

MainMenu.h:

class MainMenu : public ObjWindow
{
  private:
  void          Initialize();
  public:
  MainMenu(WindowManager* manager, int width, int height) : Window(manager, width, height)
  {
    Initialize();
  };
  MainMenu(WindowManager* manager) : Window(manager) 
  {
    Initialize();
  };

  ~MainMenu(void);

  void          button_Click();

MainMenu.cpp:

void MainMenu::Initialize()
{
  Button* button = new Button(Vector2D(100, -400), 100, 50);
  Child()->Add(button);
  button->SetText("Click Me!");

  button->SetOnClick(&MainMenu::button_Click, this);
}

void MainMenu::button_Click()
{
  //Do some button code
}

Button.h:

class Button
{
  typedef void (MainMenu::*Function)();
  Function      m_onClickFunction;

  MainMenu*     m_window;

public:
  Button(Vector2D position, int width, int height);
  ~Button(void);

  void      SetText(std::string);
  std::string   GetText();

  void      Render();
  bool      MouseMove(int x, int y);
  bool      MouseLBDown(int x, int y);


  void      SetOnClick(Function, MainMenu*);

Button.cpp:

Button::Button(Vector2D position, int width, int height)
{
  m_onClickFunction = NULL;
}


Button::~Button(void)
{
}


bool Button::MouseMove(int x, int y)
{

    return true;
}

bool Button::MouseLBDown(int x, int y)
{
        if (m_body->Intercepts(x,y))
        {
            if (m_onClickFunction != NULL)
            {
                (m_window->*m_onClickFunction)();
            }
        }
        return true;
}


void Button::SetOnClick(Function function, MainMenu* window)
{
        m_onClickFunction = function;
        m_window = window;
}

From researching around the internet it seems that I need to use a template so that I can handle the class as a type, I have tried fitting this in to my code, but so far I nothing has worked.

Please note that I am also new to asking question on stackoverflow, so if I am missing anything important please let me know

Ben
  • 514
  • 2
  • 10
  • 1
    @downvoters please remember to explain your downvote, this is super helpful for new people to get their posts as close to question guidelines as possible – Kestami Dec 05 '13 at 18:37
  • 1
    @Shane.C - I didn't downvote this, but it's not actually a question. Ben - what *specific* question do you have when trying to implement your Settings? – admdrew Dec 05 '13 at 18:52
  • @Shane.C No need/requirement to do so. A standard close vote explanation will be visible to the OP. (And as a side note upvotes are not to 'heal' downvotes. Use them only if you want to express a question was very well formed and useful for SO's intended Q&A fomat!). – πάντα ῥεῖ Dec 05 '13 at 19:08
  • @g-makulik downvoting the post doesn't send an explanation to anyhone. And this post isn't quite worth downvoting TBH. – Paweł Stawarz Dec 05 '13 at 19:09
  • @PawełStawarz Close voting does. I'm using both along usually ... – πάντα ῥεῖ Dec 05 '13 at 19:10

3 Answers3

1

You need to either create a template of the Button class - in your case it would look like this:

template <typename myType> 
class Button{
   typedef void (myType::*Function)();
   Function      m_onClickFunction;

   myType*     m_window;

public:
   ...
   void      SetOnClick(Function, myType*);
}

And use it like this:

Button<MainMenu> button;
button->SetOnClick(&MainMenu::button_Click, this);

...or create a delegate class. There are plenty of examples about delegates in C++, if you search for them.

But I'm not so sure if you wan't to stick with passing pointers to functions. You might be better off always calling the same method of derived classes, and using the virtual keyword.

Like this:

class Window{
public:
   virtual void button_Click(){};
};

class someDerivedWindow: public Window{
public:
   void button_Click(){};
};

and inside the button class just calling:

m_window->button_Click();
Community
  • 1
  • 1
Paweł Stawarz
  • 3,952
  • 2
  • 17
  • 26
  • I have tried the first solution using a template, this looked promising at first, however it seems to have caused problems with the methods in my Button.cpp file I now get the error " 45 IntelliSense: argument list for class template "Button" is missing" for every method – Ben Dec 05 '13 at 19:21
  • When you're creating a template class, you should define all the methods inside the header file. Just define them inside the class itself. – Paweł Stawarz Dec 05 '13 at 19:25
  • If I define them in the class then I get the following errors: IntelliSense: identifier "m_window" is undefined IntelliSense: identifier "m_onClickFunction" is undefined – Ben Dec 05 '13 at 20:15
0

You could work and implement your own library to do member function pointers, depending on what the goal of your project is. It is fairly complicated though, and requires a rather deep understanding of C++.

However, you can also use boost::function combined with std::bind or boost:bind, to achieve what you want to do.

If you have

struct X {
  int foo(int);
};

You can use:

boost::function1<int, int> f;
X x;
f = std::bind1st(
      std::mem_fun(&X::foo), &x);
f(5); // Call x.foo(5)

This example is taken directly from the boost::function documentation - http://www.boost.org/doc/libs/1_55_0/doc/html/function.html

divinas
  • 1,787
  • 12
  • 12
0

If using C++11:

#include <iostream>
#include <functional>
#include <vector>
#include <cstdlib>
#include <algorithm>
#include <ctime>

using namespace std;

int foo1()
{
    return 0;
}

int foo2()
{
    return 1;
}
int main()
{
    function<int()> f1 = foo1;
    function<int()> f2 = foo2;

    //do some magic
    srand(time(0));
    function<function<int()>()> generator = [&]()
    {
        if(rand() % 2 == 0)
            return foo1;
        else
            return foo2;
    };

    vector<function<int()>> vec(100);
    generate(vec.begin(),vec.end(),generator);
    for(auto elem: vec)
        cout << elem() << endl;
    return 0;
}
lucas92
  • 464
  • 2
  • 11