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();
}
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.