egcs-1.1.1: bugs with template classes

Jonathan Pryor jonpryor@vt.edu
Thu Mar 4 06:30:00 GMT 1999


These are some odd bugs...

Attached is a C++ source file with a number of template 
classes.  It is compilable under egcs-2.91.60, 
egcs-2.91.57, mingw32 (using egcs-1.1.1) and MSVC 6.0 
(don't have access to 5.0 at the moment).  

The bugs are only visible in the egcs and mingw32 
compilers, though in the following output, where the 
cygwin egcs compiler would generate 
STATUS_ACCESS_VIOLATION output to the console, mingw32 
output would pop up a dialog box titled 
"a.exe - Application Error.  The instruction at ...
referenced memory at...".

Bug 1:
If I read the value of a class member variable from inside
an inline template class member function, the value of
the variable in that function is the same as the value
the variable was constructed to.

For example, if we compile the attached source file with:

    g++ -DFIRST_BUG def2.cpp

And run the output `a.exe', we get the following output:

    main: Creating CUser object.  After successful creation, 
        m_pUnkOuter should be a non-null value.
    CoClass::CoClass(): value of this: 0x245ff2c
    CoClass::CoClass(): value of m_pUnkOuter: 0x0
    Baz::Baz(): value of this: 0x245ff28
    Baz::Baz(): value of m_pUnkOuter: 0x0
    CNonDelegator::CNonDelegator(): value of this: 0x245ff34
    CNonDelegator::CNonDelegator(): value of m_pThis: 0x245ff28
    CDelegator::CDelegator(): assigning m_pUnkOuter to &m_nd.
    CDelegator::CDelegator(): value of m_pUnkOuter: 0x245ff34
    CUser::CUser(): value of m_pUnkOuter: 0x245ff34
    main: Calling IFoo::Bar().  This will end up going through 
        m_pUnkOuter.
    CDelegator::Bar(): value of m_pUnkOuter: 0x245ff34
    CNonDelegator::Bar(): value of m_pThis: 0x245ff28
    CUser::InternalBar() called!
    main: If we're here, obviously m_pUnkOuter is non-null...
    CoClass::GetFoo() called: m_pUnkOuter = 0x0
    main: value returned by CoClass::GetFoo(): 0x0

Note that `m_pUnkOuter' is constructed to 0, and is set to
0x245f34 in the CDelegator constructor.  Yet, in 
CoClass::GetFoo(), it's value is 0x0!  If I modify the CoClass
constructor so that `m_pUnkOuter' is initialized to a non-null
value (such as 0xfefefefe), the value CoClass::GetFoo() returns
is the same as the constructed value (0xfefefefe).

The work-around is to directly access the member-variable --
which I don't like, but it works.

"Outlining" the class definition (so that all the member 
functions definitions are not in the class declaration) does not 
change the behavior shown.

Bug 2:
If we compile the source with the command:

    g++ def2.cpp

and execute the resulting executable, we get an access violation.

    main: Creating CUser object.  After successful creation, 
        m_pUnkOuter should be a non-null value.
    CoClass::CoClass(): value of this: 0x245ff3c
    CoClass::CoClass(): value of m_pUnkOuter: 0x0
    Baz::Baz(): value of this: 0x245ff38
    Baz::Baz(): value of m_pUnkOuter: 0x0
    CNonDelegator::CNonDelegator(): value of this: 0x245ff40
    CNonDelegator::CNonDelegator(): value of m_pThis: 0x245ff38
    CDelegator::CDelegator(): assigning m_pUnkOuter to &m_nd.
    CDelegator::CDelegator(): value of m_pUnkOuter: 0x245ff40
    CUser::CUser(): value of m_pUnkOuter: 0x4081c8
    main: Calling IFoo::Bar().  This will end up going through 
        m_pUnkOuter.
    CDelegator::Bar(): value of m_pUnkOuter: 0x4081c8
    [main] C:\tmp\a.exe 1000 (0) handle_exceptions: 
        Exception: STATUS_ACCESS_VIOLATION
    [main] a 1000 (0) handle_exceptions: Dumping stack trace 
        to a.exe.core

The access violation appears to occur from `m_pUnkOuter' not
pointing to a valid object.  However, I'm not sure *why* its
pointing to an invalid object...  I got this "bug" trying to 
minimize the source code needed to demonstrate the first bug 
-- and instead got access violations.

What I can point out is that the value of `m_pUnkOuter' changes
for some reason between the CDelegator constructor and the CUser
constructor (it changes from 0x245FF40 to 0x4081c8).  All I know
is that *I'm* not doing this change...

Bug 3:
The third bug is a variation of the second -- it causes an access
violation because the value of `m_pUnkOuter' changes...when I
don't assign to it.  The major difference between this version
and the `Bug 2' version is that instead of assigning directly
to `m_pUnkOuter' in the derived class, I use a base-class member
function to do the assignment -- CoClass::SetFoo().  
To see the bug, compile with:

    g++ -DSET_FOO [-DFIRST_BUG] def2.cpp

The behavior described below is present whether or not 
`-DFIRST_BUG' was used to compile the program.
Run the resulting executable `a.exe' to get:

    main: Creating CUser object.  After successful creation, 
        m_pUnkOuter should be a non-null value.
    CoClass::CoClass(): value of this: 0x245ff3c
    CoClass::CoClass(): value of m_pUnkOuter: 0x0
    Baz::Baz(): value of this: 0x245ff38
    Baz::Baz(): value of m_pUnkOuter: 0x0
    CNonDelegator::CNonDelegator(): value of this: 0x245ff40
    CNonDelegator::CNonDelegator(): value of m_pThis: 0x245ff38
    CDelegator::CDelegator(): assigning m_pUnkOuter to &m_nd.
    CDelegator::CDelegator(): value of m_pUnkOuter: 0x4081a0
    CUser::CUser(): value of m_pUnkOuter: 0x4081c8
    main: Calling IFoo::Bar().  This will end up going through 
        m_pUnkOuter.
    CDelegator::Bar(): value of m_pUnkOuter: 0x4081c8
    [main] C:\tmp\a.exe 1000 (0) handle_exceptions: 
        Exception: STATUS_ACCESS_VIOLATION
    [main] a 1000 (0) handle_exceptions: Dumping stack trace 
        to a.exe.core

This one is really odd.  In the above 2 bugs, CDelegator assigns
`m_pUnkOuter' to the address of `m_nd'.  This can be verified by
looking at the address of `this' in the CNonDelegator constructor.
In Bug 1 and Bug 2, `m_pUnkOuter' in the CDelegator constructor 
is the same value as `this' in the CNonDelegator constructor --
which is correct behavior.  In this version, however, note that
this *isn't* the case -- `m_pUnkOuter' is set...in outer space as
far as I know.  Very odd.  Very wrong.

Again, using the same source, with the same compile defines, 
creates a working executable with "correct" (in my belief)
behavior.  I should note that MSVC does give a warning in the 
compile that egcs doesn't:

    def2.cpp(67) : warning C4355: 'this' : used in base member 
        initializer list def2.cpp(67) : while compiling 
        class-template member function 
        '__thiscall CUser<class Baz>::CUser<class Baz>(struct IFoo *)'

However, in my belief this should be safe in this instance, so 
I'm willing to ignore this particular warning.

Thanks,
 - Jon


More information about the Cygwin mailing list