type punning

G

goodfella

I am curious about creating some macro's for keeping track of where
memory has been allocated on the heap. For example it would be nice
to be able to detect memory leaks at the end of program execution. I
would like a system that does not incur to much size and speed
overhead. Here is my design for a way to manage who created what and
where:

#ifndef HEAP_OBJECT_H
#define HEAP_OBJECT_H

struct code_loc
{
code_loc(const char* f, const char* fu, int l):
file(f), func(fu), line(l)
{}

const char* file;
const char* func;
int line;
};

template<class T>
class heap_object
{
public:
heap_object(const T&, const struct code_loc&);

T obj;
struct code_loc loc;
};

template<class T>
heap_object<T>::heap_object(const T& p, const struct code_loc& l):
obj(p), loc(l)
{}

#endif

The macros that implement the system are as follows:
#define track_new(init) (typeof(init)*)new
heap_object<typeof(init)>(init,code_loc(__FILE__,__PRETTY_FUNCTION__,__LINE__))

#define track_delete(pointer) delete
(heap_object<typeof(*pointer)>*)pointer

So basically I have a template class whose first data member is an
object of the type that I want to create. My assumption is that the
offset to this member is zero, so by type casting the pointer to the
heap_object to the type I want; I would have a valid pointer to the
type I want. So my first question is,

"Is it a safe assumption that the first data member of a class always
has an offset of zero?"

I have ran some limited tests with valgrind and it does not seem to
detect memory errors or leaks. I figure as long as I can insure that
track_delete is only called on objects created with track_new the code
is ok. Of course I will need some kind of list to keep track of what
has and has not been deleted, as well as be able to remove objects
from such a list that have been deleted in O(1) time. I think I have
that solved but of course the whole system is dependent on my
assumption above. Any help would be appreciated thanks.
 
G

goodfella

goodfella said:
[..] So my first question is,
"Is it a safe assumption that the first data member of a class always
has an offset of zero?"

Only for classes that do not derive from any other UDT _and_ do not have
any virtual functions.

V

So since my template class does not derive from any other UDT and does
not have any virtual functions, it is safe to assume that its first
data member has an offset of zero and thus a pointer to the data
member points to the same address as a pointer to the object which
contains the data member.
 
G

gpderetta

goodfella said:
goodfella wrote:
[..]  So my first question is,
"Is it a safe assumption that the first data member of a class always
has an offset of zero?"
Only for classes that do not derive from any other UDT _and_ do not have
any virtual functions.
[..]
V
So since my template class does not derive from any other UDT and does
not have any virtual functions, it is safe to assume that its first
data member has an offset of zero and thus a pointer to the data
member points to the same address as a pointer to the object which
contains the data member.

I don't know of any implementation that would put anything else between
the first non-static data member and the beginning of the object.  I am
too lazy to check with the Standard if it requires the addresses to be
the same, though.

AFAIK at least for POD is required.
 
G

goodfella

gpderetta said:
goodfella wrote:
goodfella wrote:
[..] So my first question is,
"Is it a safe assumption that the first data member of a class always
has an offset of zero?"
Only for classes that do not derive from any other UDT _and_ do not have
any virtual functions.
[..]
V
--
Please remove capital 'A's when replying by e-mail
I do not respond to top-posted replies, please don't ask
So since my template class does not derive from any other UDT and does
not have any virtual functions, it is safe to assume that its first
data member has an offset of zero and thus a pointer to the data
member points to the same address as a pointer to the object which
contains the data member.
I don't know of any implementation that would put anything else between
the first non-static data member and the beginning of the object. I am
too lazy to check with the Standard if it requires the addresses to be
the same, though.
AFAIK at least for POD is required.

Can you be/make sure your template is POD?

V

I apologize for my ignorance, but what does POD stand for?
 
G

goodfella

gpderetta said:
goodfella wrote:
goodfella wrote:
[..] So my first question is,
"Is it a safe assumption that the first data member of a class always
has an offset of zero?"
Only for classes that do not derive from any other UDT _and_ do not have
any virtual functions.
[..]
V
--
Please remove capital 'A's when replying by e-mail
I do not respond to top-posted replies, please don't ask
So since my template class does not derive from any other UDT and does
not have any virtual functions, it is safe to assume that its first
data member has an offset of zero and thus a pointer to the data
member points to the same address as a pointer to the object which
contains the data member.
I don't know of any implementation that would put anything else between
the first non-static data member and the beginning of the object. I am
too lazy to check with the Standard if it requires the addresses to be
the same, though.
AFAIK at least for POD is required.
Can you be/make sure your template is POD?

I apologize for my ignorance, but what does POD stand for?

So it seems that I cannot insure that my template class is POD;
however, does the fact that it has a constructor, and a non-POD data
member mean that there will be something else between the first non-
static data member?
 
P

Pascal J. Bourguignon

goodfella said:
I am curious about creating some macro's for keeping track of where
memory has been allocated on the heap. For example it would be nice
to be able to detect memory leaks at the end of program execution. I
would like a system that does not incur to much size and speed
overhead.

Use valgrind.

"Is it a safe assumption that the first data member of a class always
has an offset of zero?"
No.


I have ran some limited tests with valgrind and it does not seem to
detect memory errors or leaks.

Perhaps you didn't use it properly. Here it works very well.


% cat bug.c++

#include <iostream>
using namespace std;

class A{
public:
int x;
virtual ~A(){}
virtual int getX(){return x+1;}
};

int main(void){
A* a=new A;
A* b=a;
A* c=new A;

cout<<"offset of x = "<<((char*)(&(a->x)))-((char*)a)<<endl;
delete a;
delete b;
return(0);
}

/*
-*- mode: compilation; default-directory: "~/src/tests-c++/" -*-
Compilation started at Fri Sep 5 10:15:51

SRC="/home/pjb/src/tests-c++/bug.c++" ; EXE="bug" ; g++ -I$HOME/opt/libanevia-1.0.0-trunk//include/libanevia-1.0/ -L$HOME/opt/libanevia-1.0.0-trunk//lib/libanevia-1.0/ -Lanevia -g3 -ggdb3 -o ${EXE} ${SRC} && valgrind --tool=memcheck ./${EXE} && echo status = $?
==12256== Memcheck, a memory error detector.
==12256== Copyright (C) 2002-2007, and GNU GPL'd, by Julian Seward et al.
==12256== Using LibVEX rev 1854, a library for dynamic binary translation.
==12256== Copyright (C) 2004-2007, and GNU GPL'd, by OpenWorks LLP.
==12256== Using valgrind-3.3.1, a dynamic binary instrumentation framework.
==12256== Copyright (C) 2000-2007, and GNU GPL'd, by Julian Seward et al.
==12256== For more details, rerun with: -v
==12256==
offset of x = 8
==12256== Invalid read of size 8
==12256== at 0x400A9E: main (bug.c++:18)
==12256== Address 0x58e9030 is 0 bytes inside a block of size 16 free'd
==12256== at 0x4C2086C: operator delete(void*) (vg_replace_malloc.c:342)
==12256== by 0x400B0D: A::~A() (bug.c++:7)
==12256== by 0x400A92: main (bug.c++:17)
==12256==
==12256== Invalid write of size 8
==12256== at 0x400AF9: A::~A() (bug.c++:7)
==12256== by 0x400AAD: main (bug.c++:18)
==12256== Address 0x58e9030 is 0 bytes inside a block of size 16 free'd
==12256== at 0x4C2086C: operator delete(void*) (vg_replace_malloc.c:342)
==12256== by 0x400B0D: A::~A() (bug.c++:7)
==12256== by 0x400A92: main (bug.c++:17)
==12256==
==12256== Invalid free() / delete / delete[]
==12256== at 0x4C2086C: operator delete(void*) (vg_replace_malloc.c:342)
==12256== by 0x400B0D: A::~A() (bug.c++:7)
==12256== by 0x400AAD: main (bug.c++:18)
==12256== Address 0x58e9030 is 0 bytes inside a block of size 16 free'd
==12256== at 0x4C2086C: operator delete(void*) (vg_replace_malloc.c:342)
==12256== by 0x400B0D: A::~A() (bug.c++:7)
==12256== by 0x400A92: main (bug.c++:17)
==12256==
==12256== ERROR SUMMARY: 3 errors from 3 contexts (suppressed: 4 from 1)
==12256== malloc/free: in use at exit: 16 bytes in 1 blocks.
==12256== malloc/free: 2 allocs, 2 frees, 32 bytes allocated.
==12256== For counts of detected errors, rerun with: -v
==12256== searching for pointers to 1 not-freed blocks.
==12256== checked 162,136 bytes.
==12256==
==12256== LEAK SUMMARY:
==12256== definitely lost: 16 bytes in 1 blocks.
==12256== possibly lost: 0 bytes in 0 blocks.
==12256== still reachable: 0 bytes in 0 blocks.
==12256== suppressed: 0 bytes in 0 blocks.
==12256== Rerun with --leak-check=full to see details of leaked memory.
status = 0

Compilation finished at Fri Sep 5 10:15:52

*/
 
J

James Kanze

goodfella said:
[..] So my first question is,
"Is it a safe assumption that the first data member of a
class always has an offset of zero?"
Only for classes that do not derive from any other UDT _and_
do not have any virtual functions.

Officially, only for POD's, I think. As soon as there is an
access specifier, at least, the compiler is allowed to reorder
elements with different access specifiers.
 
J

James Kanze

I am curious about creating some macro's for keeping track of
where memory has been allocated on the heap. For example it
would be nice to be able to detect memory leaks at the end of
program execution. I would like a system that does not incur
to much size and speed overhead. Here is my design for a way
to manage who created what and where:
#ifndef HEAP_OBJECT_H
#define HEAP_OBJECT_H
struct code_loc
{
code_loc(const char* f, const char* fu, int l):
file(f), func(fu), line(l)
{}

const char* file;
const char* func;
int line;
};
template<class T>
class heap_object
{
public:
heap_object(const T&, const struct code_loc&);
T obj;
struct code_loc loc;
};
template<class T>
heap_object<T>::heap_object(const T& p, const struct code_loc& l):
obj(p), loc(l)
{}

The macros that implement the system are as follows:
#define track_new(init) (typeof(init)*)new
heap_object<typeof(init)>(init,code_loc(__FILE__,__PRETTY_FUNCTION__,__LINE__))
#define track_delete(pointer) delete
(heap_object<typeof(*pointer)>*)pointer

Since you need to explicitly specify when your tracking system
is being used, in the allocation and the deletion, why not use
the classical solution, with placement new. Something like:

#define track_new new( __FILE__, __LINE__ )

?
 
G

goodfella

goodfella said:
I am curious about creating some macro's for keeping track of where
memory has been allocated on the heap. For example it would be nice
to be able to detect memory leaks at the end of program execution. I
would like a system that does not incur to much size and speed
overhead.

Use valgrind.
"Is it a safe assumption that the first data member of a class always
has an offset of zero?"
No.

I have ran some limited tests with valgrind and it does not seem to
detect memory errors or leaks.

Perhaps you didn't use it properly. Here it works very well.

% cat bug.c++

#include <iostream>
using namespace std;

class A{
public:
int x;
virtual ~A(){}
virtual int getX(){return x+1;}

};

int main(void){
A* a=new A;
A* b=a;
A* c=new A;

cout<<"offset of x = "<<((char*)(&(a->x)))-((char*)a)<<endl;
delete a;
delete b;
return(0);

}

/*
-*- mode: compilation; default-directory: "~/src/tests-c++/" -*-
Compilation started at Fri Sep 5 10:15:51

SRC="/home/pjb/src/tests-c++/bug.c++" ; EXE="bug" ; g++ -I$HOME/opt/libanevia-1.0.0-trunk//include/libanevia-1.0/ -L$HOME/opt/libanevia-1.0.0-trunk//lib/libanevia-1.0/ -Lanevia -g3 -ggdb3 -o ${EXE} ${SRC} && valgrind --tool=memcheck ./${EXE} && echo status = $?
==12256== Memcheck, a memory error detector.
==12256== Copyright (C) 2002-2007, and GNU GPL'd, by Julian Seward et al.
==12256== Using LibVEX rev 1854, a library for dynamic binary translation.
==12256== Copyright (C) 2004-2007, and GNU GPL'd, by OpenWorks LLP.
==12256== Using valgrind-3.3.1, a dynamic binary instrumentation framework.
==12256== Copyright (C) 2000-2007, and GNU GPL'd, by Julian Seward et al.
==12256== For more details, rerun with: -v
==12256==
offset of x = 8
==12256== Invalid read of size 8
==12256== at 0x400A9E: main (bug.c++:18)
==12256== Address 0x58e9030 is 0 bytes inside a block of size 16 free'd
==12256== at 0x4C2086C: operator delete(void*) (vg_replace_malloc.c:342)
==12256== by 0x400B0D: A::~A() (bug.c++:7)
==12256== by 0x400A92: main (bug.c++:17)
==12256==
==12256== Invalid write of size 8
==12256== at 0x400AF9: A::~A() (bug.c++:7)
==12256== by 0x400AAD: main (bug.c++:18)
==12256== Address 0x58e9030 is 0 bytes inside a block of size 16 free'd
==12256== at 0x4C2086C: operator delete(void*) (vg_replace_malloc.c:342)
==12256== by 0x400B0D: A::~A() (bug.c++:7)
==12256== by 0x400A92: main (bug.c++:17)
==12256==
==12256== Invalid free() / delete / delete[]
==12256== at 0x4C2086C: operator delete(void*) (vg_replace_malloc.c:342)
==12256== by 0x400B0D: A::~A() (bug.c++:7)
==12256== by 0x400AAD: main (bug.c++:18)
==12256== Address 0x58e9030 is 0 bytes inside a block of size 16 free'd
==12256== at 0x4C2086C: operator delete(void*) (vg_replace_malloc.c:342)
==12256== by 0x400B0D: A::~A() (bug.c++:7)
==12256== by 0x400A92: main (bug.c++:17)
==12256==
==12256== ERROR SUMMARY: 3 errors from 3 contexts (suppressed: 4 from 1)
==12256== malloc/free: in use at exit: 16 bytes in 1 blocks.
==12256== malloc/free: 2 allocs, 2 frees, 32 bytes allocated.
==12256== For counts of detected errors, rerun with: -v
==12256== searching for pointers to 1 not-freed blocks.
==12256== checked 162,136 bytes.
==12256==
==12256== LEAK SUMMARY:
==12256== definitely lost: 16 bytes in 1 blocks.
==12256== possibly lost: 0 bytes in 0 blocks.
==12256== still reachable: 0 bytes in 0 blocks.
==12256== suppressed: 0 bytes in 0 blocks.
==12256== Rerun with --leak-check=full to see details of leaked memory.
status = 0

Compilation finished at Fri Sep 5 10:15:52

*/

Thanks Pascal, but I wasn't saying that valgrind did not work at all,
I was just commenting on the fact that with the code I supplied and
limited testing, valgrind was not producing errors, which was what I
expected given the limited scope of my tests. I have used valgrind
before for other projects and it helped to eliminate memory leaks and
errors.
 
G

goodfella

Since you need to explicitly specify when your tracking system
is being used, in the allocation and the deletion, why not use
the classical solution, with placement new. Something like:

#define track_new new( __FILE__, __LINE__ )

?

--
James Kanze (GABI Software) email:[email protected]
Conseils en informatique orientée objet/
Beratung in objektorientierter Datenverarbeitung
9 place Sémard, 78210 St.-Cyr-l'École, France, +33 (0)1 30 23 00 34


I wanted something that can go through and tell me what has not been
deleted; not just where something was created
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,777
Messages
2,569,604
Members
45,218
Latest member
JolieDenha

Latest Threads

Top