Binding function calls in tgui::SignalWidgetBase::connect()

Started by Hexade, 09 August 2019, 21:24:46

Hexade

Hi,

I am having trouble understanding how to use std::bind to call a function as part of tgui::SignalWidgetBase::connect(). I was looking at the "Introduction to signals" tutorial for version 0.7 where you show this example (It's probably worth noting that I am using version 0.8.5):

int getNum() { return 5; }

void func1(int i);
void func2(int value);

picture->connect("clicked", func1, std::bind(getNum));
button->connect("pressed", func2, std::bind(&tgui::Slider::getValue, slider));


My need for std::bind is a bit more complex than this example and this is my first time using std::bind so I have been spending the day learning about std::bind and std::function. This is what my function looks like at the moment:

void TitleMenuButton::SetFunctions(bool& button_selected, std::string& current_button, std::function<Button&(std::string)> get_button_function) {

button_image->connect("MouseEntered", &TitleMenuButton::ButtonFocused, this, std::ref(button_selected), std::ref(current_button), std::bind(get_button_function, current_button));
button_image->connect("MouseLeft", &TitleMenuButton::ButtonUnfocused, this, std::ref(button_selected), std::ref(current_button));

}


This function belongs to the TitleMenuButton class that stores a tgui::Picture (button_image), the function simply calls connect() on the tgui::Picture and connects the TitleMenuButton::ButtonFocused() and TitleMenuButton::ButtonUnfocused() functions. The issue lies with my attempt at using std::bind to bind the function ButtonList::GetButton().

This function belongs to a ButtonList class that stores x amount of TitleMenuButtons. It is designed to make creating the buttons much faster and easier, so this TitleMenuButton::SetFunctions() function is called from within the ButtonList class as such:

std::function<Button&(std::string)> get_button_function = std::bind(&ButtonList::GetButton, this, std::placeholders::_1);
button->SetFunctions(is_focusing_button, current_button, get_button_function);


Lastly, the function I am attempting to bind is ButtonList::GetButton() that returns a reference to one of the buttons stored within the ButtonList.

public:
Button& GetButton(std::string button_name) { return *button_list.at(button_name); }

private:
std::unordered_map<std::string, std::unique_ptr<Button>> button_list;


So the idea behind all of this code is that the ButtonList creates and stores instances of TitleMenuButton and keeps track of which button is currently "active" (Being hovered over with the mouse / keyboard). During the creation process of the TitleMenuButtons we need to connect functions to the various signals to give them functionality. Now on to the actual error I am getting.

Visual Studio seems to like the code and doesn't produce any red squiggly lines, however when I try to compile it I receive 2 errors.

'operator __surrogate_func': no matching overloaded function found
Failed to specialize function template 'unknown-type std::Mem_fn<void (__thiscall TitleMenuButton::* )(bool &,std::string &,Button &)>::operator ()(_Types &&...) noexcept(<expr>) const'


Both of these errors occur in signalimpl.hpp at lines 89 and 88 respectively. Unfortunately both of these error messages are lost on me and I can't figure out what I am doing wrong. I have tried a plethora of methods of passing the function from an instance of ButtonList to an instance of TitleMenuButton but none of them have worked. This seems to be my best attempt so far and I don't think I am that far off from getting it working. However the error messages that point me to TGUI files don't help me much.

If you need to see more of the code then just ask although I hope I have covered everything. Thanks.

texus

QuoteMy need for std::bind is a bit more complex than this example
If you had tried a simple case first then maybe it could have saved you some time with trying different things :).
After some debugging, trying to compile the code you described, I decided to just check the most basic code that you mentioned at the top. It didn't work either.

It turns out there is a good reason why it isn't in the 0.8 tutorial, it simply no longer works.
The reason it used to work was more of a side-effect of the way my internal code worked, it was never an intended feature to have (although I did document it in the tutorial after I found out about it). All parameters passed to the connect function were passed to std::bind inside the signal class. The getNum function being called at the moment func1 is called was a consequence of passing an std::bind argument as parameter to an std::bind call. In 0.8 I don't use std::bind anymore however. I've learned that lambdas are both faster and more powerful and saw no more use to rely on std::bind in my implementation.

The quick solution should have been to just surround everything with the std::bind that used to be inside the connect function...
Code (cpp) Select
button_image->connect("MouseEntered", std::bind(&TitleMenuButton::ButtonFocused, this, std::ref(button_selected), std::ref(current_button), std::bind(get_button_function, current_button)));
... but that unfortunately doesn't work. My new signal system in 0.8 doesn't seem to like the argument being a bind expression (which acts really weird). (EDIT: Although I don't recommend doing it like this, I do consider this a bug. So I will try to change the code soon so that the above line does work.)

I would recommend just not using std::bind at all:
Code (cpp) Select
button_image->connect("MouseEntered", [&,get_button_function]{ ButtonFocused(button_selected, current_button, get_button_function(current_button)); });

In the above line, the get_button_function is passed as a copy to the lambda because unlike the other parameters it is a local variable and thus a reference to it won't be valid anymore when the MouseEntered event occurs. The "&" makes sure the other parameters do get passed as a reference.

Hexade

Alright, that clears everything up then. Thanks a lot for the help and clarification.