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