A
Alf P. Steinbach
The following code, although lengthy, is a greatly reduced standard C++
analog of The Real Problem code, which is several orders of magnitude
longer. To avoid irrelevant clutter this reduced code has little use of
const, it has no cleanup, etc. It does exhibit the problem of interest.
By the way, in the real code the NO_ABSTRACTION_PLEASE part would be the
most complex with the most code: this code does not represent the API
complexity, but instead reduces the API level to the relevant stuff.
In the part following the NO_ABSTRACTION_PLEASE part, the Main_window
object receives a notification before the Item_list child has finished
construction, and so before the Main_window has stored its pointer in a
member variable. In the notification handling accessing the pointer data
member is therefore UB, and crashes in e.g. a Visual C++ debug build.
The NO_ABSTRACTION_PLEASE part shows one pretty application-specific way
to code around this (fixed id's), and I know of several more but they
all feel ungood, so I'm asking for suggestions, ideas, just about
anything that might get me out of box thinking here.
Cheers,
- Alf
analog of The Real Problem code, which is several orders of magnitude
longer. To avoid irrelevant clutter this reduced code has little use of
const, it has no cleanup, etc. It does exhibit the problem of interest.
By the way, in the real code the NO_ABSTRACTION_PLEASE part would be the
most complex with the most code: this code does not represent the API
complexity, but instead reduces the API level to the relevant stuff.
In the part following the NO_ABSTRACTION_PLEASE part, the Main_window
object receives a notification before the Item_list child has finished
construction, and so before the Main_window has stored its pointer in a
member variable. In the notification handling accessing the pointer data
member is therefore UB, and crashes in e.g. a Visual C++ debug build.
The NO_ABSTRACTION_PLEASE part shows one pretty application-specific way
to code around this (fixed id's), and I know of several more but they
all feel ungood, so I'm asking for suggestions, ideas, just about
anything that might get me out of box thinking here.
Code:
namespace api {
class Window
{
private:
void operator=( Window ); // No such.
protected:
virtual ~Window() {}
public:
int const id;
virtual void on_child_event( Window* origin, int event_id )
{ (void) origin; (void) event_id; }
Window( int an_id = -1 ): id( an_id ) {}
// Much other stuff.
};
class List_view: public Window
{
protected:
virtual ~List_view() {}
public:
Window* const parent;
void add_item( char const* ) { parent->on_child_event( this,
666 ); }
List_view( Window* a_parent, int an_id = -1 )
: Window( an_id )
, parent( a_parent )
{}
};
} // namespace api
#ifdef NO_ABSTRACTION_PLEASE
enum { itemlist_id = 101 };
class Main_window
: public api::Window
{
protected:
void on_itemlist_event( int event_id )
{
if( event_id == 42 ) { } // E.g., selection changed.
}
virtual void on_child_event( api::Window* origin, int event_id )
{
if( origin->id == itemlist_id )
{
on_itemlist_event( event_id );
}
}
public:
Main_window(): api::Window() {}
};
auto main() -> int
{
auto w = new api::Window();
auto v = new api::List_view( w, itemlist_id ); //
Child of w
v->add_item( "This is a fixed (always there) item" );
}
#else
class Event_handler
{
protected:
virtual void on_api_child_event( api::Window* origin, int id )
{ (void) origin; (void) id; }
static void receive_api_child_event( Event_handler& eh,
api::Window* origin, int id )
{ eh.on_api_child_event( origin, id ); }
};
class Wrapped_api_window
: public Event_handler
{
private:
api::Window* api_window_;
class Forwarding_api_window
: public api::Window
, private Event_handler
{
private:
Event_handler* event_handler_;
protected:
virtual void on_child_event( api::Window* origin, int id )
{ Event_handler::receive_api_child_event( *event_handler_,
origin, id ); }
public:
Forwarding_api_window( Event_handler* handler )
: event_handler_( handler )
{}
};
public:
auto api_window() const -> api::Window* { return api_window_; }
Wrapped_api_window()
: api_window_( new Forwarding_api_window( this ) )
{}
};
class Item_list
{
private:
api::List_view* display_;
public:
auto api_window() -> api::List_view* { return display_; }
Item_list( Wrapped_api_window* parent )
: display_( new api::List_view( parent->api_window() ) )
{ display_->add_item( "This is a fixed (always there) item" ); }
};
class Main_window
: public Wrapped_api_window
{
private:
Item_list* itemlist_;
protected:
void on_itemlist_event( int id )
{
if( id == 42 ) { } // E.g., selection changed.
}
virtual void on_api_child_event( api::Window* origin, int id )
{
if( origin == itemlist_->api_window() ) // Ooops!
{
on_itemlist_event( id );
}
}
public:
Main_window()
: Wrapped_api_window()
, itemlist_( new Item_list( this ) )
{}
};
auto main() -> int
{
auto w = new Main_window();
(void) w;
}
#endif
Cheers,
- Alf