Can I pass more than 1 parameter to a FLTK button callback function?

176 views Asked by At

trying to make a chess program with FLTK and I got callbacks working with one parameter passed, a number specifically, but I want the callback function to include "window->hide();". This means I need to pass the callback function the window object. Has to be cast to original datatype in callback function. I get the error - no instance of overloaded function "Fl_Button::callback" matches the argument list. As well as the error - 'Fl_Widget::callback': no overloaded function takes 3 arguments. Both refer to where I write "wK->callback(....);

int Board::setup(int argc, char** argv, std::vector<Piece*> wow, int turn)
{
    Fl_Window* mainWindow = new Fl_Window(640, 640); //create window

    //draw background squares
    for (int x = 0; x < 8; x++)
    {
        for (int y = 0; y < 8; y++)
        {
            if ((x % 2 == 1 && y % 2 == 0) || (x % 2 == 0 && y % 2 == 1)) {
                Fl_Box* box = new Fl_Box(x * 80, y * 80, 80, 80);
                box->box(FL_FLAT_BOX);
                box->color(FL_BLACK);
            }
        }
    }

    //draw pieces
    if (turn % 2 == 1) {
        whiteButtons(wow, argc, argv, (Fl_Window *)mainWindow);
    }
    if (turn % 2 == 0) {
        blackButtons(wow, argc, argv, (Fl_Window *)mainWindow);
    }


    mainWindow->show();//main loop
    return Fl::run();
}
void Board::whiteButtons(std::vector<Piece*> wow, int argc, char** argv, Fl_Window* w){

    Fl_Button* wK = new Fl_Button(wow[0]->yPos * 80, wow[0]->xPos * 80, 80, 80, u8"\u2654");//unicode chess piece
    wK->box(FL_NO_BOX);
    wK->labelsize(48);
    wK->callback(firstPClicked, (void *)0, (void *)w);//0 being its pos in the piece vector
//the word callback is redlined in visual studio
}
//blackButtons omitted for simplicity's sake. exactly the same as whiteButtons.

void firstPClicked(Fl_Widget* f, void* d, void* w) {//cast or smthg
    if (((int*)d) == 0) {
        firstP = 0;
        ((Fl_Window*)w)->hide();
    }
}

Really not sure what I need to change here. Any ideas or people who know what they're doing?

Code aims to call the setup function until one player wins, effectively refreshing the window by closing it. All functions are correctly defined in the .h file. This is the only error I have at the moment.

Many questions ask if you can have multiple buttons callback to the same function, but I want to know if I can pass multiple things to the callback function. regular callback syntax is below.

void balls(int* argc, char** argv){
    Fl_Window *deez = new Fl_Window(300, 300);
    
    Fl_Button *nuts = new Fl_Button(100, 100, 100, 100, "Funny");
    nuts->callback(imSoFunny, void* 0);

}
void imSoFunny(Fl_Widget *w, (void*) d){
    if (((void*)d) == 0){
        std::cout << "ahhh";
    }
}
2

There are 2 answers

0
Some programmer dude On

FLTK is old and not updated for modern C++. Heck, it's barely even designed or implemented with old pre-C++11 standard in mind.

It's using C-style coding habits for many things, including callbacks, which means you will have to work extra hard and write extra code to work around such things.

For your specific problem, the "simplest" workaround is to gather all your arguments in a structure of some kind, and use a pointer to an object of that structure as the only argument to the callback function. The callback function then converts (though proper reinterpret_cast) the pointer to the correct type, and uses the structure to get the different arguments.

The nice thing about such a workaround, is that one of the structure members could be a reference or pointer to another object, and you can use a member function of that object as the actual callback.

1
GandhiGandhi On

To pass multiple values to this callback function, you'll have to:

  1. declare a struct that packages both of the values you want together
  2. Create a new instance of that struct with the values that you want to pass. Using dynamic memory like this isn't considered "good" modern C++, but I think it will work for your example.
  3. Give a pointer to that instance to the callback function.
  4. Call delete to clean up the allocated memory when you're done with it.

The other commenters have pointed this out, but here's specifically adapting your example. If you wanted to pass a char * and an int to your callback, you could do so like this:

/* The two data values you want to pass to your callback packaged together */
struct GotEm {
    char * dragon;
    int dees;
}

void balls(int* argc, char** argv){
    Fl_Window *deez = new Fl_Window(300, 300);
    
    Fl_Button *nuts = new Fl_Button(100, 100, 100, 100, "Funny");
    /* Initialize how you want -- note that the callback data has to by dynamically allocated.
     * That means you'll have to call `delete` on it when the data goes out of scope.
     */
    GotEm *callback_data = new GotEm;
    callback_data->dragon = "foo";
    callback_data->dees = 0;

    nuts->callback(imSoFunny, (void*) &callback_data);

}
void imSoFunny(Fl_Widget *w, void* d){
    /* cast your data type back from the void pointer */
    GotEm * callback_data = (GotEm*) d;
    if (callback_data->dees == 0){
        std::cout << "ahhh";
    }
}