[omniORB] MSVC 5.0 bug: throw in ctor with virtual base class calls virtual base with wrong 'this'

Randy Shoup rshoup@tumbleweed.com
Fri, 04 Sep 1998 08:19:17 -0700


Dietmar -- 

We have experienced this problem for a while on NT, but never did the
research in the KnowledgeBase to find out if it had been reported :-) 
Thanks.

Our interim solution to this problem has been the following ugliness:

+ publicly derive all implementations from an ExceptionRethrower class
+ catch all exceptions in the outermost constructor, and store the
exception inside the ExceptionRethrower base class.
+ immediately after the constructor call in the calling method, call
object->RethrowCtorException().

This works best, of course, when all or most of your exceptions derive
from a single base class.  ExceptionRethrower is an auto_ptr-like
template which takes the exception type as a template argument, stores a
pointer to the exception, and exposes a method to rethrow the exception
if it exists.

Dietmar May wrote:
> 
> Ran across this bug while debugging a program. Since it deals with
> calling the destructor of a virtual base class, which CORBA uses a
> little bit :-), thought it might be of interest to others.
> 
> Essentially, if an exception is thrown by the constructor of a derived
> class (where apparently there is at least one level of intermediate
> derivation), the virtual base class' destructor is called with a 'this'
> pointer that is offset by 4 or more bytes. A second side effect, at
> least in the test program below, is that the virtual base class'
> destructor is called a second time with the correct this pointer! This,
> of course, is a problem if the virtual base class has any data to clean
> up, because integers may become pointers, and pointers may reference
> other objects...
> 
> Sorry, there are no known work-arounds. Also, see MS Q168936
> Knowledge-base article.
> 
> =============================
> 
> #include <stdio.h>
> 
> class base
> {
> public:
>     base ()
>     {
>         printf("base::base() this = 0x%lx\n", (long)(void*)this);
>         i = 0;
>     }
>     virtual ~base ()
>     {
>         printf("base::~base() this = 0x%lx\n", (long)(void*)this);
>         i = 0;
>     }
> protected:
>     int i;
> };
> 
> class intermed : public virtual base
> {
> public:
>     intermed ()
>     {
>         printf("intermed::intermed () this = 0x%lx\n",
> (long)(void*)this);
>         j = 0;
>         i = 1;
>     }
>     virtual ~intermed ()
>     {
>         printf("intermed::~intermed () this = 0x%lx\n",
> (long)(void*)this);
>         j = 0;
>     }
> protected:
>     int j;
> };
> 
> class derived : public intermed
> {
> public:
>     derived ()
>     {
>         printf("derived::derived() this = 0x%lx\n", (long)(void*)this);
>         k = 0;
>         j = 1;
>         throw int(0);
>         k = 1;
>     }
>     ~derived ()
>     {
>         printf("derived::~derived() this = 0x%lx\n", (long)(void*)this);
>         k = 0;
>     }
> protected:
>     int k;
> };
> 
> int main ()
> {
>     try
>     {
>         printf("creating derived object\n");
>         derived d;
>     }
>     catch(...)
>     {
>         printf("caught exception from ctor\n");
>     }
>     printf("out of catch block\n");
>     return 0;
> }
> 
> =============================
> 
> Running this code results in the following output under NT 4.0 sp3:
> 
> creating derived object
> base::base() this = 0x12ff68
> intermed::intermed () this = 0x12ff5c
> derived::derived() this = 0x12ff5c
> intermed::~intermed () this = 0x12ff5c
> base::~base() this = 0x12ff64
> base::~base() this = 0x12ff68
> caught exception from ctor
> out of catch block
> 
> Regards,
> Dietmar May
> 
> Software Architect
> Object Workshops, Inc.
> http://www.object-workshops.com
> dcmay@object-workshops.com

-- 

-- Randy
_________________________________________________________________  
Randy Shoup                                     (650)569-3682  
Principal Engineer                              rshoup@tumbleweed.com  
Tumbleweed Software Corporation