Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

Overloading on a pointer and an array type

0 views
Skip to first unread message

Matthias Hofmann

unread,
Oct 16, 2007, 8:48:01 AM10/16/07
to
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:

#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! ]

Markus Moll

unread,
Oct 16, 2007, 11:13:28 AM10/16/07
to
Hi

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

--

wijnand

unread,
Oct 16, 2007, 2:17:25 PM10/16/07
to
You can solve it with functors instead, because classes can have
partial template specialization.

#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


--

Matthias Hofmann

unread,
Oct 17, 2007, 4:21:09 PM10/17/07
to
"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.

--
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 ]

Markus Moll

unread,
Oct 18, 2007, 11:03:34 PM10/18/07
to
Hi

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

--

Bart van Ingen Schenau

unread,
Oct 18, 2007, 11:04:53 PM10/18/07
to
Matthias Hofmann wrote:

> "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

unread,
Oct 19, 2007, 6:26:46 PM10/19/07
to
Hi

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.

Matthias Hofmann

unread,
Oct 20, 2007, 10:36:36 AM10/20/07
to
"wijnand" <wijnand...@gmail.com> schrieb im Newsbeitrag
news:1192541622....@z24g2000prh.googlegroups.com...

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 ]

Matthias Hofmann

unread,
Oct 20, 2007, 10:36:55 AM10/20/07
to
"Bart van Ingen Schenau" <ba...@ingen.ddns.info> schrieb im Newsbeitrag
news:9485383.n...@ingen.ddns.info...
> Matthias Hofmann wrote:

> /* 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 ]

0 new messages