C++ Crashing Template Bug

  • Thread starter Thread starter 314ccc
  • Start date Start date
3

314ccc

I have stumbled upon a very nasty compiler bug. I have boiled it down
to the following code. Granted, this code looks nonsensical, trust
that the original source did much more and solves a specific problem
nicely. The real question here, is why does the compiler compile this
without any problems, yet generates code that crashes. This code
should do nothing...absolutely nothing. There are some comments in
this code that identify places where simple changes can be made to
resolve the crash. I know I can make the crash go away. My question
is what is it doing that is crashing?

File 1: One.h
#pragma once

class Two; // crash
//#include "Two.h" // no crash

class One
{
public:
One(){}
virtual ~One(){}

protected:
Two* m_pTwo;
};

File2: Two.h
#pragma once

#include "Three.h"

class One;

class Two
{
public:
Two();
virtual ~Two();

private:
Three<One> m_cThree;
};

File2 (source): Two.cpp
#include "Two.h"

Two::Two()
{
}

Two::~Two()
{
}

File 3: Three.h
#pragma once

class One;

template<class T> class Three
{
public:
Three();
virtual ~Three();

private:
void (One::*m_pOneMethod)();
};

template<class T> Three<T>::Three()
: m_pOneMethod(0)
{
}

template<class T> Three<T>::~Three()
{
}

File4 (test location):

#include "One.h" // comment out this line fixes crash!?
#include "Two.h"

void CTestDlg::OnBnClickedButton1()
{
Two cTest;
}
 
I have stumbled upon a very nasty compiler bug. I have boiled it down
to the following code. Granted, this code looks nonsensical, trust
that the original source did much more and solves a specific problem
nicely. The real question here, is why does the compiler compile this
without any problems, yet generates code that crashes. This code
should do nothing...absolutely nothing. There are some comments in
this code that identify places where simple changes can be made to
resolve the crash. I know I can make the crash go away. My question
is what is it doing that is crashing?

VC++ implements a (strictly speaking non-conforming) optimization which uses
4 different representations for pointers to members. When you declare a
pointer to a member of an incomplete class, the compiler is forced to choose
the most general representation (16 bytes), but when you declare a pointer
to member of a complete class, the compiler will choose the most efficient
representation (in this case, 4 bytes).

As a result, in this code, when you simply forward declare ClassTwo, you end
up with two different representations for pointers to members of ClassTwo,
which ultimately leads to disaster.

You need to compile this code with /vmg, or use #pragma pointers_to_members.
Look them up in MSDN.

-cd
 
Thank you for this response! Adding this option takes care of the
problem without any of the mystery that was worrying me.

After reading in the help about the pragma, I am unsure of the scope of
it. If I only want to use it in this instance would I add inside the
class definition?

template<class T> class Three
{
public:
Three();
virtual ~Three();

private:
#pragma pointers_to_members(full_generality)
void (One::*m_pOneMethod)();

};
 
Thank you for this response! Adding this option takes care of the
problem without any of the mystery that was worrying me.

After reading in the help about the pragma, I am unsure of the scope
of it. If I only want to use it in this instance would I add inside
the class definition?

template<class T> class Three
{
public:
Three();
virtual ~Three();

private:
#pragma pointers_to_members(full_generality)
void (One::*m_pOneMethod)();

};

No, it applies to a class, not a member, so you'd do

#pragma pointers_to_members(full_generality)
class One;

Once given, the #pragma applies to all subsequent class definitions that
aren't already known to use a different representation, or aren't marked
with one of the inheritance keywords.

In your case, the inheritance keywords (which I neglected to mention in my
earlier reply) are probably the way to go:

// One.h

class __single_inheritance Two;

-cd
 
Back
Top