I am writing a memory tracker to keep track of allocations from the 'new'
and 'new[]' operators. I want to be able to distinguish between both forms,
so I am looking for code similar to the following:
#include <cstddef>
#include <iostream>
template <class T>
T* TrackAllocation( T* ptr, const char* file, int line )
{
std::cout << "Tracking object allocation." << std::endl;
return ptr;
}
template <class T, std::size_t N>
T* TrackAllocation( T (&arr)[N], const char* file, int line )
{
std::cout << "Tracking array allocation." << std::endl;
return arr;
}
#define OBJECT_NEW( T ) TrackAllocation( T, __FILE__, __LINE__ )
#define ARRAY_NEW( T ) TrackAllocation( T, __FILE__, __LINE__ )
int main()
{
// Calls TrackAllocation<int*>
// as excpected.
int* p = OBJECT_NEW( new int );
delete p;
// Calls TrackAllocation<int*>
// but should rather call
// TrackAllocation<int(&)[64]>.
p = ARRAY_NEW( new int[64] );
delete [] p;
return 0;
}
Unfortunately, the return value of the 'new[]' operator is a pointer, not an
array, so the above code does not work the way it should. One solution would
be to code like this:
template <class T>
T* TrackObjectAllocation( T* ptr, const char* file, int line )
{
std::cout << "Tracking object allocation." << std::endl;
return ptr;
}
template <class T>
T* TrackArrayAllocation( T* arr, const char* file, int line )
{
std::cout << "Tracking array allocation." << std::endl;
return arr;
}
#define OBJECT_NEW( T ) TrackObjectAllocation( T, __FILE__, __LINE__ )
#define ARRAY_NEW( T ) TrackArrayAllocation( T, __FILE__, __LINE__ )
However, I want to create a compile time error if clients of my code use the
macros the wrong way:
// ERROR: Allocating an object, but
// tracking it as an array allocation.
int* p = ARRAY_NEW( new int );
// ERROR: Allocating an array, but
// tracking it as an object allocation.
int* p = OBJECT_NEW( new int[64] );
Is there any way I can define my macros in a safer way to prevent clients
from confusing the object and array forms?
--
Matthias Hofmann
Anvil-Soft, CEO
http://www.anvil-soft.com - The Creators of Toilet Tycoon
http://www.anvil-soft.de - Die Macher des Klomanagers
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
Matthias Hofmann wrote:
> Hello everyone,
>
> I am writing a memory tracker to keep track of allocations from the 'new'
> and 'new[]' operators. I want to be able to distinguish between both
> forms, so I am looking for code similar to the following:
[...]
> template <class T>
> T* TrackObjectAllocation( T* ptr, const char* file, int line )
> {
> std::cout << "Tracking object allocation." << std::endl;
> return ptr;
> }
>
> template <class T>
> T* TrackArrayAllocation( T* arr, const char* file, int line )
> {
> std::cout << "Tracking array allocation." << std::endl;
> return arr;
> }
>
> #define OBJECT_NEW( T ) TrackObjectAllocation( T, __FILE__, __LINE__ )
> #define ARRAY_NEW( T ) TrackArrayAllocation( T, __FILE__, __LINE__ )
>
> However, I want to create a compile time error if clients of my code use
the
> macros the wrong way:
Isn't the "new" redundant here? It would be a lot simpler if you could have
#define OBJECT_NEW(T) TrackObjectAllocation<T>(new T, __FILE__, __LINE__ );
#define ARRAY_NEW(T) TrackArrayAllocation<T>(new T, __FILE__, __LINE__ );
p = OBJECT_NEW( int );
p = ARRAY_NEW( int[64] );
This way, the object/array allocation trackers could check if the type T is
an array or object type and create the appropriate compile-time error. (Of
course, the return and argument types would have to be adjusted...)
Markus
--
#include <cstddef>
#include <iostream>
template <class T>
struct TrackObjectAllocation
{
T* operator()( const char *file, int line)
{
std::cout << "Tracking object allocation" << std::endl;
return new T;
}
};
template <class T, std::size_t N>
struct TrackObjectAllocation< T[N] >;
template <class T>
struct TrackArrayAllocation ;
template <class T, std::size_t N>
struct TrackArrayAllocation< T [N]>
{
T * operator()( const char *file, int line)
{
std::cout << "Tracking array allocation " << std::endl;
return new T[N];
}
};
#define OBJECT_NEW( T ) TrackObjectAllocation<T>()( __FILE__,
__LINE__ )
#define ARRAY_NEW( T ) TrackArrayAllocation<T>()( __FILE__, __LINE__)
int main()
{
int * p = OBJECT_NEW( int ); // OBJECT_NEW( int[64]) will give
compile time error
delete p;
p = ARRAY_NEW( int [64] ); // ARRAY_NEW( int ) will give a compile
time error
delete [] p;
return 0;
}
regards,
Wijnand Suijlen
--
> Isn't the "new" redundant here? It would be a lot simpler if you could
> have
>
> #define OBJECT_NEW(T) TrackObjectAllocation<T>(new T, __FILE__,
> __LINE__ );
> #define ARRAY_NEW(T) TrackArrayAllocation<T>(new T, __FILE__, __LINE__ );
> p = OBJECT_NEW( int );
> p = ARRAY_NEW( int[64] );
Oops, that's in fact what I meant to write.
> This way, the object/array allocation trackers could check if the type T
> is
> an array or object type and create the appropriate compile-time error. (Of
> course, the return and argument types would have to be adjusted...)
On Visual C++ 2005 Express Edition it does not work this way either. It's
maybe because the functions just overload each other rather than the latter
being a specialization of the former.
--
Matthias Hofmann
Anvil-Soft, CEO
http://www.anvil-soft.com - The Creators of Toilet Tycoon
http://www.anvil-soft.de - Die Macher des Klomanagers
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
Matthias Hofmann wrote:
> "Markus Moll" <marku...@esat.kuleuven.ac.be> schrieb im Newsbeitrag
> news:4714abde$0$7694$9b4e...@newsspool2.arcor-online.net...
>> This way, the object/array allocation trackers could check if the type T
>> is
>> an array or object type and create the appropriate compile-time error.
>> (Of course, the return and argument types would have to be adjusted...)
>
> On Visual C++ 2005 Express Edition it does not work this way either. It's
> maybe because the functions just overload each other rather than the
> latter being a specialization of the former.
I don't know about VC++, but the following works for me:
#include <iostream>
#include <ostream>
#include <boost/static_assert.hpp>
#include <boost/type_traits.hpp>
using boost::is_array;
using boost::remove_all_extents;
using boost::add_pointer;
template<typename T>
typename add_pointer<typename remove_all_extents<T>::type>::type
track_array(typename add_pointer<typename
remove_all_extents<T>::type>::type ptr)
{
BOOST_STATIC_ASSERT( is_array<T>::value );
// do logging
return ptr;
}
template<typename T>
typename add_pointer<typename remove_all_extents<T>::type>::type
track_object(typename add_pointer<typename
remove_all_extents<T>::type>::type ptr)
{
BOOST_STATIC_ASSERT( !is_array<T>::value );
// do logging
return ptr;
}
#define NEW_ARRAY(T) (track_array<T>(new T))
#define NEW_OBJECT(T) (track_object<T>(new T))
int main()
{
int *a1 = NEW_OBJECT(int);
int *a2 = NEW_OBJECT(int[64]);
int *a3 = NEW_ARRAY(int);
int *a4 = NEW_ARRAY(int[64]);
delete a1;
delete a2;
delete[] a3;
delete[] a4;
}
Markus
--
> "Markus Moll" <marku...@esat.kuleuven.ac.be> schrieb im Newsbeitrag
> news:4714abde$0$7694$9b4e...@newsspool2.arcor-online.net...
>
>> Isn't the "new" redundant here? It would be a lot simpler if you
>> could have
>>
>> #define OBJECT_NEW(T) TrackObjectAllocation<T>(new T, __FILE__,
>> __LINE__ );
>> #define ARRAY_NEW(T) TrackArrayAllocation<T>(new T, __FILE__,
>> #__LINE__ );
>> p = OBJECT_NEW( int );
>> p = ARRAY_NEW( int[64] );
>
> Oops, that's in fact what I meant to write.
>
>> This way, the object/array allocation trackers could check if the
>> type T is
>> an array or object type and create the appropriate compile-time
>> error. (Of course, the return and argument types would have to be
>> adjusted...)
>
> On Visual C++ 2005 Express Edition it does not work this way either.
> It's maybe because the functions just overload each other rather than
> the latter being a specialization of the former.
>
That is right, you can't create partial specialisations of function
templates.
To get the desired effect, I have used enable_if/disable_if from Boost
to make sure that the tracking function is only available when you use
the correct type.
My code:
#include <cstddef>
#include <iostream>
#include <boost/type_traits/is_array.hpp>
#include <boost/utility/enable_if.hpp>
template <class T>
typename boost::disable_if<
boost::is_array<T>,
T
>::type*
TrackObjectAllocation( T* ptr, const char*, int)
{
std::cout << "Tracking object allocation." << std::endl;
return ptr;
}
/* Helper struct to obtain the element type of an array */
template <class T> struct get_element_type;
template <class T, size_t N> struct get_element_type<T[N]>
{
typedef T type;
};
template <class T>
typename boost::enable_if<
boost::is_array<T>,
typename get_element_type<T>::type
>::type*
TrackArrayAllocation( typename get_element_type<T>::type* arr,
const char*,
int )
{
std::cout << "Tracking array allocation." << std::endl;
return arr;
}
#define OBJECT_NEW( T ) TrackObjectAllocation<T>( new T, __FILE__,\
__LINE__ )
#define ARRAY_NEW( T ) TrackArrayAllocation<T>( new T, __FILE__,\
__LINE__ )
int main()
{
// Calls TrackObjectAllocation<int*>
// as excpected.
int* p = OBJECT_NEW( int );
delete p;
// Calls TrackArrayAllocation<int[64]>
// as excpected.
p = ARRAY_NEW( int[64] );
delete [] p;
// p = ARRAY_NEW( int ); /* Fails to compile */
// p = OBJECT_NEW( int[64] ); /* Fails to compile */
return 0;
}
Bart v Ingen Schenau
--
a.c.l.l.c-c++ FAQ: http://www.comeaucomputing.com/learn/faq
c.l.c FAQ: http://c-faq.com/
c.l.c++ FAQ: http://www.parashift.com/c++-faq-lite/
Markus Moll wrote:
> I don't know about VC++, but the following works for me:
[my version snipped]
Okay... my version at least has a bug with multi-dimensional arrays. Bart's
code is both more concise and more correct.
Great, this did the trick! :-) And with your idea of using classes for
partial specialization, I was even able to improve my code to provide only
one macro definition and automatically call the appropriate function for
each form of the new operator:
#include <cstddef>
#include <iostream>
// Primary template for object new.
template <class T> struct TrackNewImpl
{
static T* TrackNew( T* ptr, const char* file, int line)
{
std::cout << "Tracking object allocation" << std::endl;
return ptr;
}
};
// Partial specialization for array new.
template <class T, std::size_t N> struct TrackNewImpl<T[N]>
{
static T* TrackNew( T* ptr, const char* file, int line)
{
std::cout << "Tracking array allocation " << std::endl;
return ptr;
}
};
#define NEW( T ) TrackNewImpl<T>::TrackNew( new T, __FILE__, __LINE__ )
int main()
{
int * p = NEW( int ); // Calls the primary template.
delete p;
p = NEW( int[64] ); // Calls the partial specialization.
delete [] p;
return 0;
}
It's a pity that there is no partial specialization for function templates
though, as that would have made a much cleaner implementation possible.
Anyway, thank you very much for your solution!
--
Matthias Hofmann
Anvil-Soft, CEO
http://www.anvil-soft.com - The Creators of Toilet Tycoon
http://www.anvil-soft.de - Die Macher des Klomanagers
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
> /* Helper struct to obtain the element type of an array */
> template <class T> struct get_element_type;
> template <class T, size_t N> struct get_element_type<T[N]>
> {
> typedef T type;
> };
Great, this is just the solution to a problem I had with having a template
function forward the call to helper classes to emulate function template
partial specialization! :-) Here's the new code:
#include <cstddef>
#include <iostream>
// Extracts the type of an object.
template <class T>
struct extract { typedef T type; };
// Extracts the type of an array.
template <class T, std::size_t N>
struct extract<T[N]> { typedef T type; };
// Primary template for object new.
template <class T> struct TrackNewHelper
{
static T* TrackNew( T* ptr, const char* file, int line)
{
std::cout << "Tracking object allocation" << std::endl;
return ptr;
}
};
// Partial specialization for array new.
template <class T, std::size_t N> struct TrackNewHelper<T[N]>
{
static T* TrackNew( T* ptr, const char* file, int line)
{
std::cout << "Tracking array allocation " << std::endl;
return ptr;
}
};
// Forwards the call to helper classes.
template <class T> typename extract<T>::type* TrackNew(
typename extract<T>::type* ptr, const char* file, int line )
{
return TrackNewHelper<T>::TrackNew( ptr, file, line );
}
#define NEW( T ) TrackNew<T>( new T, __FILE__, __LINE__ )
int main()
{
int * p = NEW( int ); // Calls the primary template.
delete p;
p = NEW( int[64] ); // Calls the partial specialization.
delete [] p;
return 0;
}
Anyway, thank you very much for your solution! And if anyone is interested
in what my problem was, then try to implement the above without the
'extract' helper classes... ;-)
--
Matthias Hofmann
Anvil-Soft, CEO
http://www.anvil-soft.com - The Creators of Toilet Tycoon
http://www.anvil-soft.de - Die Macher des Klomanagers
[ See http://www.gotw.ca/resources/clcm.htm for info about ]