A RAII problem: parent object getting events before fully constructed

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. :)

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
 
M

Martin Shobe

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. :)
[snip]

I'd tell the List_view not to send a notification for the add_item
that's causing problems. Another, admittedly heavier weight, possibility
is to register the parent as a listener after it's ready to take events.
(This might not work for you since I think you need the parent in the
List_view for things you didn't show.)

Martin Shobe
 
G

Geoff

On Fri, 22 Nov 2013 22:24:47 +0100, "Alf P. Steinbach"

[snip]
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. :)

[snip]

Perhaps you should investigate the warning issued by the compiler:

http://msdn.microsoft.com/query/dev10.query?appId=Dev10IDEF1&l=EN-US&k=k(C4355);k(VS.OUTPUT)&rd=true

HAND
 
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. :)
[snip]

I'd tell the List_view not to send a notification for the add_item
that's causing problems. Another, admittedly heavier weight, possibility
is to register the parent as a listener after it's ready to take events.
(This might not work for you since I think you need the parent in the
List_view for things you didn't show.)

Thanks!

My thinking here is that one can either somehow avoid identifying the
notification sender in the main window object, or do it in a way that
doesn't involve a member.

The latter is what my "no abstraction" code did, but it's risky because
the main window object (or what would have been it with more abstraction
in place) really isn't fully initialized at that point.

Regarding avoiding the identification, in addition to the two schemes
you list (not send the notification at early stage, not receive it at
early stage) there is the not uncommon strategy of merely reflecting the
notification back to the origin. It carries enough information for that.
Then the sending object can handle it, which is more object oriented,
but which for the general case merely moves the problem around. If one
is lucky, however, then the troublesome notification will not be one
that one is interested in handling in the main object.

I think your first suggestion, trying to do the troublesome add_item
without it sending a notification, is perhaps the most promising.


Cheers,

- Alf
 
M

Martin Shobe

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. :)
[snip]

I'd tell the List_view not to send a notification for the add_item
that's causing problems. Another, admittedly heavier weight, possibility
is to register the parent as a listener after it's ready to take events.
(This might not work for you since I think you need the parent in the
List_view for things you didn't show.)

Thanks!

My thinking here is that one can either somehow avoid identifying the
notification sender in the main window object, or do it in a way that
doesn't involve a member.

The latter is what my "no abstraction" code did, but it's risky because
the main window object (or what would have been it with more abstraction
in place) really isn't fully initialized at that point.

I know what you mean, but as far as C++ is concerned, it's the other way
around. It's your "abstraction" code that is using an object that isn't
fully initialized.

[snip]

Martin Shobe
 
A

Alf P. Steinbach

I know what you mean, but as far as C++ is concerned, it's the other way
around. It's your "abstraction" code that is using an object that isn't
fully initialized.

Yeah, that's a good reason to use abstraction and to not zero-initialize
things. It often catches such problems. At the "direct coding" level
things may be OK as far as the language support machinery is concerned,
leaving the programmer with just wrong results later on.

By the way, the API in question is really badly designed. It's
C-oriented and for example, there's no way to find the required buffer
size in order to retrieve an item text back from a list view. The
software provider's own code works around that by just trying steadily
larger buffer sizes in succession, each time it retrieves a string.

So, abstraction is almost /required/, and brings up such problems as above.

Cheers, & thanks,

- Alf
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,962
Messages
2,570,134
Members
46,692
Latest member
JenniferTi

Latest Threads

Top