Search code examples
c++pointersstaticcallbackfltk

c++ instance location changed by fltk callback


I am attempting to make a button-like object (Smart_Button) that contains its own callback function. The object is being created by the My_Window constructor, but its memory address when I hit the gui button differes from the address is has inside the My_Window constructer. I want to enable some additional functionality that requires Smart_Buttons to have pointers to each other, but I cannot because the address changes.

Curiously, if I define the callback function inside My_window (as I do for site_parent_cb), I get the behavior I expect. The output of the current program is:

site_parent_cb in constructor: 0xbfc20c6c
site_local_cb in constructor: 0xbfc20c48
(when I hit the "parent" button)
this: 0xbfc20c6c
(when I hit the "local" button)
this: 0xbfc20b90

My code is below. In addition, my include is calling files from Struoustrups' Programming Principles and Practice Using C++ Book. Those files are wrappers for some FLTK functionality and are available here: http://www.stroustrup.com/Programming/PPP2code/

Any help understanding the behavior would be appreciated. My primary question is if this from something about my own code I don't understand, or if it is a peculiarity of the simplifying FLTK wrappers.

#include "GUI.h"

namespace Graph_lib {
    struct Smart_Button : Button {
    Smart_Button(Point tl, int w, int h, Callback cb);
    Smart_Button(Point tl, int w, int h);
    void print_address();
private:
    static void cb_pressed(void*, void*);
};

Smart_Button::Smart_Button(Point tl, int w, int h, Callback cb)
    :Button(tl, w, h, "parent", cb)
{
}

Smart_Button::Smart_Button(Point tl, int w, int h)
    :Button(tl, w, h, "local", cb_pressed)
{
}

void Smart_Button::cb_pressed (void*, void* pw){static_cast<Smart_Button*>(pw)->print_address();}

void Smart_Button::print_address(){
    cout << "this: " << this << endl;
}


struct My_Window : Window {
    My_Window();
    Smart_Button site_local_cb;
    Smart_Button site_parent_cb;
private:
    static void cb_pressed(void*, void*);
};

My_Window::My_Window()
    :Window(Point(100,100),200,200,"Some App"),
    site_parent_cb(Point( 50, 50), 40, 40, cb_pressed),
    site_local_cb(Point(100, 50), 40, 40)
{   
    cout << "site_parent_cb in constructor: " << &site_parent_cb << endl;
    cout << "site_local_cb in constructor: " << &site_local_cb << endl;
    attach(site_parent_cb);
    attach(site_local_cb);
}

void My_Window::cb_pressed (void*, void* pw){static_cast<My_Window>(pw)->site_parent_cb.print_address();}
};


int main(int argc, char **argv) {
    using namespace Graph_lib;
    My_Window game;
    return gui_main();
}

Solution

  • The problem is in the callback

    void My_Window::cb_pressed (void*, void* pw)
    {
        static_cast<My_Window>(pw)->site_parent_cb.print_address();
    }
    

    When the callback is setup, the parameter passed in is the address of the Smart_Button. When the button is pressed, the callback is casting the parameter to a My_Window and taking the offset of site_parent_cb, which happens to have a 4 byte offset from the address of My_Window. This is why there is a discrepancy in the address. The callback should be

    void My_Window::cb_pressed(void*, void* pw)
    {
        static_cast<Smart_Button*>(pw)->print_address()
    }
    

    This is quite a good catch. If the positions of local and parent had been swapped, the discrepancy would never have been spotted.