Reputation: 227
I decided to overload the new, new[],... operators in my classes so I can log the file and line at which they were called so I can easier track memory allocations/leaks.
Now the problems is in my stack and array classes (and other template container classes which allocate memory):
If I use them with one of my classes which has the new,new[],... operators overloaded it works fine.
But if I use it with the standard c++ data types (int,float,...) I can't allocate them, since no overloaded new operator matches the arguments of the new(__ LINE __ , __ FILE __) operator (or others like placement new).
Example of stack code:
// placement new
T* t=new(__ LINE __ , __ FILE__)(&m_data[i])T;
So I'm out of good ideas on how to make this work. If I replace new(__ LINE __ ,__ FILE __) with new I loose memory logging ability. One solution is to make a separated stack for standard data types in which the default new is used.
Is there any way to detect at compile time if a template parameter is a struct, class or a built in c++ type?
How do you handle stuff like this? What do you suggest? Any comments on this design (good,bad) are obviously welcome (just don't post stuff like "don't reinvent the wheel with your own containers ").
Upvotes: 10
Views: 3133
Reputation: 1141
Please note that your current solution requires adding the logging code to every new(line, file)
overload you have. Also, you cannot easily switch it off in release builds unless you surround each of your logging calls within #ifndef DEBUG ... #endif
.
Here's one way of achieving what you wnat: Instead of overloading the new
operator for each of your classes, consider overloading the global new
operator using the placement syntax; that way you avoid interfering with the 'normal' new
operator. Then you can #define
new and delete macros for convenience and, most importantly, you can have control over when your memory-tracking new/delete
is applied and when the standard version is used.
#ifdef ENABLE_CUSTOM_ALLOC
// Custom new operator. Do your memory logging here.
void* operator new (size_t size, char* file, unsigned int line)
{
void* x = malloc(size);
cout << "Allocated " << size << " byte(s) at address " << x
<< " in " << file << ":" << line << endl;
return x;
}
// You must override the default delete operator to detect all deallocations
void operator delete (void* p)
{
free(p);
cout << "Freed memory at address " << p << endl;
}
// You also should provide an overload with the same arguments as your
// placement new. This would be called in case the constructor of the
// created object would throw.
void operator delete (void* p, char* file, unsigned int line)
{
free(p);
cout << "Freed memory at address " << p << endl;
}
#define new new(__FILE__, __LINE__)
#endif
// A test class with constructors and destructor
class X
{
public:
X() { cout << "X::ctor()" << endl; }
X(int x) { cout << "X::ctor(" << x << ")" << endl; }
~X() { cout << "X::dtor()" << endl; }
};
int main (int argc, char* argv[])
{
X* x3 = new X();
X* x4 = new X(20);
delete x3;
delete x4;
}
you should see something like:
Allocated 1 byte(s) at address 00345008 in Alloc.cpp:58
X::ctor()
Allocated 1 byte(s) at address 003450B0 in Alloc.cpp:59
X::ctor(20)
X::dtor()
Freed memory at address 00345008
X::dtor()
Freed memory at address 003450B0
Try substituting X
for int
and you'll see it works too. You can extend this to the array and placement new as well but i'd rather not make the post longer than it is.
Few last pointers at the end:
- MSVC has this functionality, see here
- There is a toturial about doing memory tracking this way here under the 'Tracing Memory Leaks' section
Upvotes: 3
Reputation: 124
Is there any way to detect at compile time if a template parameter is a struct, class or a built in c++ type?
You can use boos::type_traits and boost::mpl for it.
Example:
#include <boost/type_traits.hpp>
#include <boost/mpl/not.hpp>
#include <boost/mpl/logical.hpp>
template <class T>
typename boost::enable_if<boost::is_class<T>, T>::type
foo(){cout << "is class " << endl;};
template <class T>
typename boost::enable_if<boost::mpl::not_<boost::is_class<T> >, T>::type
foo(){cout << "is not class "<< endl;};
Types list - http://www.boost.org/doc/libs/1_47_0/libs/type_traits/doc/html/index.html
Or you can use boost::mpl::set for your set of types
Upvotes: 0
Reputation: 33405
struct Int {
int i;
Int (int _i) : i(_i) {}
operator int () const {return i;}
};
#define __LINE_NUMBER__ Int(__LINE__)
Use this macro instead of the standard line number macro and overload resolution will distinguish Int
line numbers from int
other numbers.
I can't imagine how this will work out in full. Are you going to use it like int * x = NEW(int,123);
or something like that?
I agree with the commenters, by the way -- you probably don't have to go down this road. Overloading new
is something of a black art and should normally be avoided.
Upvotes: 0