Search code examples
c++pointerssfml

Trying to make a callback for a member


I have the class stickyNotes, and in it I have the function addNote which is a public non-static function. In the code there is a new type defined: typedef void(*fptr)

I also have the class Button which in its constructor takes a variable of type fptr, I have a function makeButton() that returns a Button object.

stickyNotes also has another member called rendermainWindow which renders the main window and adds a button, I am trying to create a new variable of type fptr that is set to the address of stickyNotes::addNote and I'm getting the error:

'&': illegal operation on bound member function expression

stickyNotes::rendermainWindow:

void stickyNotes::rendermainWindow() {
    /*
    Renders the main window
    */
    this->buttonList.empty(); // buttonList is a list of all buttons
    mainWindow->clear(sf::Color(29, 29, 27, 255)); // clearing the window
    sf::Vector2u windowDimensions = mainWindow->getSize(); // getting window dimensions
    fptr cb = &this->addNote; <-------- ERROR HERE 
    Button plusB = makeButton((int)(windowDimensions.x * 0.75),
                              (int)(windowDimensions.y * 0.15),
                              (int)(windowDimensions.x * 0.85),
                              (int)(windowDimensions.y * 0.25),
                              mainWindow,
                              cb);
    // first button to add stuff
    std::vector<Button> vect;
    vect.push_back(plusB);
    this->buttonList.push_back(vect);
    renderNotes();

}

What I've tried:

Replacing this->addNote with stickyNotes::addNote.

PS:

I'm not looking to make addNote static. and I want to keep it public, how can I make a button with the callback function of addNote? if there is a workaround, I'll be glad to hear it.


Solution

  • You are trying to pass a callback to the button. If you require the callback to be a simple function pointer (that does not receive any arguments) then your callback cannot retain any state.

    However, all non-static member functions require at least one piece of state in order to be called: They need to know which object to work in/with.

    These two are fundamentally at odds: If your button takes a simple function pointer, it cannot be used to call member functions (leaving horrible hacks such as global state aside).

    One way around this is to use member function pointers: These specify which class they operate on (i.e. are member functions of) and must be called on an instance of that class. Since you presumably want the button callback to be usable with more than one class, you can use polymorphy - this is the interface solution SHR suggested.

    Another option for the button interface is to take (and store) a std::function<void()> instead of your function pointer. std::function can store state and is very conveniently used with lambda functions:

    Button plusB = makeButton(/* ... */, [this]() { addNote(); });
    

    Lastly, you could allow users to pass some "custom data" to the button interface, possibly as void* or std::any which is passed along to the callback. The user can then store e.g. the stickyNotes instance in that custom data. That is compatible with a simple function pointer. However, this approach circumvents type safety and is very low-level. It is used in some libraries as it is very general, but it's much more annoying to work with than the std::function approach.