Reputation: 31
The tricky part here is the define of BUILD_DEBUG_CLASS_MEMBER in TestCompiler.cpp, I know I can just simply don't define it to solve the bug (please see the assertion in runtime result below)
My questions are:
1. why, in my case, virtual inline function is being inlined in TestCompiler.obj file
EVEN IF I don't call delete or whatever class methods of TriShape/Shape.
2. why in runtime, the inlined version from TestCompiler.o is called
but not the other version in Class.o. (Not only the rumtime result below but also gdb shows it.)
Anyway, here I am just trying to study why gcc does the way it does now. For example, it makes the .obj very big but it may help to inline as much as it wants…!? I am just guessing and I don't understand exactly. ( Since my bug (my stupid define) is obvious, so I prefer not to say it is gcc's bug. ;) )
BTW, the compiler I have tested is gcc 4.4.7 and also VS2013. ONLY the former cause assertion. And, the original code base is in a library which is not mine and so I cannot easily change the Shape files. (ClassA is kind of factory and the Shape classes are the core of a library.)
Thank you very much for being patient and I am looking forward to your comments/answers.
(ClassA is kind of factory and the Shape classes are the core originated from a library.)
//================================================================//
// TestCompiler.cpp
//
//#include "stdafx.h"
#include <stdio.h>
#include "TestA.h"
#define BUILD_DEBUG_CLASS_MEMBER // :)
#include "TriShape.h" // include it just for testing compiler/linker in our case
int main(int argc, char* argv[])
{
printf("TC: main start \n");
//TestA::gTestAFunc();
gTestAFunc();
// calls to TriShape::testVFunc, etc
//...
printf("TC: main finish \n");
return 0;
}
//================================================================//
//TestA
#pragma once
extern void gTestAFunc();
//================================================================//
//TestA.cpp
#include <stdio.h>
#include "ClassA.h"
void gTestAFunc()
{
ClassA* pA = new ClassA();
pA->createS();
pA->removeS();
delete pA;
}
//================================================================//
//ClassA.h
#pragma once
#include "Shape.h"
class ClassA
{
public:
ClassA()
: m_pShape(NULL)
{
}
void createS();
void removeS();
Shape* m_pShape;
};
//================================================================//
//ClassA.cpp
#include <stdio.h>
#include "ClassA.h"
//#define BUILD_DEBUG_CLASS_MEMBER // don't define it :)
#include "TriShape.h"
void ClassA::createS()
{
m_pShape = new TriShape;
}
void ClassA::removeS()
{
delete m_pShape;
m_pShape = NULL;
}
//================================================================//
//Shape.h
#pragma once
class Shape //:: MemoryObject
{
public:
Shape()
: m_ptr(NULL)
{
printf("Shape ctor: this:%p size:%d \n", this, sizeof(*this));
}
//TODO: inline it! :P
virtual ~Shape()
{
//m_ptr
printf("Shape dtor: this:%p size:%d \n", this, sizeof(*this));
}
inline virtual int testVFunc()
{
return -1;
}
Shape* m_ptr;
};
//test ONLY:
//#include "TriShape.h"
//================================================================//
//TriShape.h
#pragma once
#include <assert.h>
#include "Shape.h"
#define FIX_NUM 0xABCD
class MyList
{
public:
MyList()
: m_dummy(FIX_NUM)
{
}
//TODO: inline it! :P
virtual ~MyList()
{
printf("List dtor: this:%p size:%d dummy:0x%x \n", this, sizeof(*this), m_dummy);
assert(m_dummy==FIX_NUM);
//#pragma message( "List dtor here" )
}
int m_dummy;
};
class TriShape : public Shape
{
public:
TriShape()
//: m_debugMember()
{
printf("TriShape ctor: this:%p size:%d \n", this, sizeof(*this));
}
#if 1
//caseT1
virtual ~TriShape();
#else
//caseT2
virtual ~TriShape()
{
printf("TriShape dtor IN class: this:%p size:%d \n", this, sizeof(*this));
#pragma message( "TriShape dtor here IN class" )
}
#endif
virtual int testVFunc();
#ifdef BUILD_DEBUG_CLASS_MEMBER
MyList m_debugMember;
#endif
};
// inline dtor
#if 1
inline TriShape::~TriShape()
{
printf("TriShape dtor AFTER class: this:%p size:%d \n", this, sizeof(*this));
#pragma message( "TriShape dtor here AFTER class" )
#ifdef BUILD_DEBUG_CLASS_MEMBER
#pragma message("\tit is defined: BUILD_DEBUG_CLASS_MEMBER")
#endif
}
#endif
// inline virutal func
inline int TriShape::testVFunc()
{
printf("TriShape testVFunc AFTER class: this:%p size:%d \n", this, sizeof(*this));
#pragma message( "TriShape testVFunc here AFTER class" )
#ifdef BUILD_DEBUG_CLASS_MEMBER
#pragma message("\tit is defined: BUILD_DEBUG_CLASS_MEMBER")
#endif
return 0;
}
$ ./inlineTest
TC: main start
Shape ctor: this:0x995b018 size:8
TriShape ctor: this:0x995b018 size:8
TriShape dtor AFTER class: this:0x995b018 size:16
List dtor: this:0x995b020 size:8 dummy:0x20fe1
inlineTest: TriShape.h:22: virtual MyList::~MyList(): Assertion
`m_dummy==0xABCD' failed.
Aborted (core dumped)
In TestCompiler.asm:
The destructor is inlned up this section
.section .text._ZN8TriShapeD1Ev,"axG",@progbits,_ZN8TriShapeD1Ev,comdat
(...)
call _ZN6MyListD1Ev
(...)
call _ZN5ShapeD2Ev
In ClassA.asm:
The destructor is inlned up this section
.section .text._ZN8TriShapeD1Ev,"axG",@progbits,_ZN8TriShapeD1Ev,comdat
(...)
call _ZN5ShapeD2Ev
.text._ZN8TriShapeD1Ev
0x0000000000000000 0x0 ClassA.o
.text._ZN8TriShapeD0Ev
0x0000000000000000 0x0 ClassA.o //oops, ALL ZEROS means something??
(...)
.rel.text._ZN8TriShapeD1Ev
0x0000000000000000 0x0 /usr/lib/../lib/crt1.o
.rel.text._ZN8TriShapeD0Ev
0x0000000000000000 0x0 /usr/lib/../lib/crt1.o
(...)
.text._ZN8TriShapeD1Ev
0x0000000008048a80 0xb9 TestCompiler.o
0x0000000008048a80 _ZN8TriShapeD1Ev
*fill* 0x0000000008048b39 0x1 90909090
.text._ZN8TriShapeD0Ev
0x0000000008048b3a 0xb9 TestCompiler.o
0x0000000008048b3a _ZN8TriShapeD0Ev
*fill* 0x0000000008048bf3 0x1 90909090
Upvotes: 0
Views: 181
Reputation: 96241
Unfortunately you are violating the one definition rule for TriShape
so trying to guess why the compiler did something particular isn't likely to yield useful information. The compiler is allowed to assume that the class and functions will be identical in all sources files, so it may pick one and execute that code in all places.
Upvotes: 1