delorie.com/archives/browse.cgi   search  
Mail Archives: cygwin/1999/03/04/09:35:10

Mailing-List: contact cygwin-help AT sourceware DOT cygnus DOT com; run by ezmlm
Sender: cygwin-owner AT sourceware DOT cygnus DOT com
Delivered-To: mailing list cygwin AT sourceware DOT cygnus DOT com
Message-ID: <050b01be664b$915527d0$e63d2509@jonpryor.raleigh.ibm.com>
From: "Jonathan Pryor" <jonpryor AT vt DOT edu>
To: "Cygwin Mailing List" <cygwin AT sourceware DOT cygnus DOT com>
Subject: egcs-1.1.1: bugs with template classes
Date: Thu, 4 Mar 1999 09:30:05 -0500
MIME-Version: 1.0
X-Priority: 3
X-MSMail-Priority: Normal
X-Mailer: Microsoft Outlook Express 4.72.3110.5
X-MimeOLE: Produced By Microsoft MimeOLE V4.72.3110.3

------=_NextPart_000_0508_01BE6621.96380440
Content-Type: text/plain;
	charset="iso-8859-1"
Content-Transfer-Encoding: 7bit

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

------=_NextPart_000_0508_01BE6621.96380440
Content-Type: application/octet-stream;
	name="def2.cpp"
Content-Transfer-Encoding: quoted-printable
Content-Disposition: attachment;
	filename="def2.cpp"

// lots of "interesting uses of templates"...  Is it a bug?

#include <stdio.h>

struct IFoo {
  virtual void Bar () =3D 0;
};

struct IDitto : public IFoo {
  virtual void Oops () =3D 0;
};


template <class Delegatee>
class CNonDelegator : public IFoo {
  Delegatee* m_pThis;

public:
  CNonDelegator ( Delegatee* pThis ) : m_pThis(pThis) {
    printf ("CNonDelegator::CNonDelegator(): value of this: 0x%x\n", =
this);
    printf ("CNonDelegator::CNonDelegator(): value of m_pThis: 0x%x\n",=20
      m_pThis );
  }
  void Bar () {
    printf ("CNonDelegator::Bar(): value of m_pThis: 0x%x\n", m_pThis );
    m_pThis->InternalBar ();
  }
};


template <class base, class Deferer>
class CDelegator : public base {
  CNonDelegator<Deferer>  m_nd;
public:
  CDelegator ( IFoo* pOuter, Deferer* pThis ) : m_nd(pThis) {
    if ( pOuter ) {
      printf ("CDelegator::CDelegator(): assigning m_pUnkOuter to =
pOuter.\n");
#ifdef SET_FOO
      SetFoo ( pOuter );
#else
      m_pUnkOuter =3D pOuter;
#endif
    }
    else {
      printf ("CDelegator::CDelegator(): assigning m_pUnkOuter to =
&m_nd.\n");
#if SET_FOO
      SetFoo ( &m_nd );
#else
      m_pUnkOuter =3D &m_nd;
#endif
    }
    printf ("CDelegator::CDelegator(): value of m_pUnkOuter: 0x%x\n",
      m_pUnkOuter );
  }

  void Bar () {
    printf ("CDelegator::Bar(): value of m_pUnkOuter: 0x%x\n", =
m_pUnkOuter);
    m_pUnkOuter->Bar ();
  }
};


template <class base >
class CUser : public CDelegator <base, CUser<base> > {
  typedef CDelegator<base, CUser<base> > base_class;
public:
  CUser (IFoo* pOuter ) : base_class (pOuter, this) {
    printf ("CUser::CUser(): value of m_pUnkOuter: 0x%x\n", m_pUnkOuter =
);
  }
  void InternalBar () {
    printf ("CUser::InternalBar() called!\n");
  }
};


#if FIRST_BUG
class FakeAutoCS {
public:
  void Lock() {}
  void Unlock () {}
};

class DefTM {
public:
  static unsigned long Increment ( long* pn ) { return ++(*pn); }
  static unsigned long Decrement ( long* pn ) { return --(*pn); }
  typedef FakeAutoCS CriticalSection;
  typedef FakeAutoCS AutoCriticalSection;
};

template <class tm>
class CRootObject {
  typedef typename tm::CriticalSection CriticalSection;
  typedef typename tm::AutoCriticalSection AutoCriticalSection;

  AutoCriticalSection m_cs;
public:
  void Lock() { m_cs.Lock(); }
  void Unlock() { m_cs.Unlock(); }
};
#endif  // FIRST_BUG


template <class T>
class CoClass=20
#if FIRST_BUG
 : public CRootObject<DefTM>
#endif // FIRST_BUG

{
protected:
  IFoo* m_pUnkOuter;

public:
  CoClass () :  m_pUnkOuter(0) {
    printf ("CoClass::CoClass(): value of this: 0x%x\n", this );
    printf ("CoClass::CoClass(): value of m_pUnkOuter: 0x%x\n", =
m_pUnkOuter);
  }

  IFoo* GetFoo() {
    printf ("CoClass::GetFoo() called: m_pUnkOuter =3D =
0x%x\n",m_pUnkOuter);
    return m_pUnkOuter;
  }

#if SET_FOO
  void SetFoo ( IFoo* pFoo ) {
    m_pUnkOuter =3D pFoo;
  }
#endif  // SET_FOO
};

class Baz :=20
  public IDitto,=20
  public CoClass<Baz>=20
{
public:
  Baz () {
    printf ("Baz::Baz(): value of this: 0x%x\n", this );
    printf ("Baz::Baz(): value of m_pUnkOuter: 0x%x\n", m_pUnkOuter );
  }
  void Oops () {
    printf ("Baz::Oops().\n");
  }
};


int main (int, char **) {
  printf ("main: Creating CUser object.  After successful creation, =
m_pUnkOuter"
    " should be a non-null value.\n");
  CUser<Baz> b(0);
  IFoo* pFoo =3D &b;

  printf ("main: Calling IFoo::Bar().  This will end up going through "
    "m_pUnkOuter.\n");
  pFoo->Bar ();

  printf ("main: If we're here, obviously m_pUnkOuter is =
non-null...\n");
 =20
  pFoo =3D b.GetFoo ();

  printf ("main: value returned by CoClass::GetFoo(): 0x%x\n", pFoo );

  return 0;
}


------=_NextPart_000_0508_01BE6621.96380440
Content-Type: text/plain; charset=

--
Want to unsubscribe from this list?
Send a message to cygwin-unsubscribe AT sourceware DOT cygnus DOT com
------=_NextPart_000_0508_01BE6621.96380440--

- Raw text -


  webmaster     delorie software   privacy  
  Copyright © 2019   by DJ Delorie     Updated Jul 2019