1

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.

Jarod42
  • 203,559
  • 14
  • 181
  • 302
dsavfcd
  • 13
  • 4
  • 2
    A pointer to a non-static member function (which I assume that `&stickyNotes::addNote` is) is *not* the same as a pointer to a non-member function (which is what the type `fptr` is). A non-static member function needs an object to be called on, which non-member functions won't have. I suggest you start learning about [`std::function`](http://en.cppreference.com/w/cpp/utility/functional/function), [`std::bind`](http://en.cppreference.com/w/cpp/utility/functional/bind), and [lambda expressions](http://en.cppreference.com/w/cpp/language/lambda), as they can help you solve your problem. – Some programmer dude Jul 12 '18 at 12:03
  • You are trying to bind a member function to a non-member pointer. https://stackoverflow.com/questions/14189440/c-class-member-callback-simple-examples/14189561#14189561 is an answer how you can accomplish what you are trying. – divinas Jul 12 '18 at 12:03
  • Do you have a finite set of instances? Or does button allow putting a void* tag or similar to reference the callee? Apart from that, you're limited to thunking, which is nasty. – Pete Kirkham Jul 12 '18 at 12:05
  • Yesterday administrator closed very same question because of dublicate – V. Kravchenko Jul 12 '18 at 12:10
  • You can also implement a signal-slot system (similar to Qt) using e.g. [Boost signal2](https://www.boost.org/doc/libs/1_67_0/doc/html/signals2.html). – Some programmer dude Jul 12 '18 at 12:17

2 Answers2

1

The problem:

Member functions are just an offset in the class namespace, so you can't give an offset as absulute address.

Solution 1:

Easy way is to create an Interface, implement it in your class, and return or assign this as the Interface.

Solution 2:

Other option is to create pointer to member function. for example: void (Class::*pfunc)() will declare pfunc pointer which can point to member function of class Class like void Class::Member()

Solution 3:

As suggested by @GoswinvonBrederlow in the comment, use lambda and return or assign it as std::function:

std::function<void ()> Class::f()
{
    return []() { /* do something...*/ };
}
Community
  • 1
  • 1
SHR
  • 7,940
  • 9
  • 38
  • 57
1

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.

Max Langhof
  • 23,383
  • 5
  • 39
  • 72