A Proposal of operator new with typeinfo

Motivation

At present we can override operator new as follows:

void* operator new(size_t size, ArgT1 arg1, ArgT2 arg2, ...)
{
    return malloc(size); // just an example
}

Even though it is good enough, still I think something is missing here. For example, if I want to implement a debug version of operator new which reports the memory leak and the kind of objects leaked, I cannot get the class name of an allocated object.

An operator new with typeinfo can solve this problem. And in fact, it has more potential applications.

Impact on the Standard

It is only a pure extension of operator new. It doesn't change semantic interpretation of existed standards except calling new Type(a1, a2, …) and new Type[count].

Design Decisions

operator new with typeinfo

We introduce a new operator named "operator _new". Please refer to the following expressions:

template <typename Type>
void* operator _new(ArgT1 arg1, ArgT2 arg2, ...)
{
    return malloc(sizeof(Type)); // just an example
}

When we create an object by operator new:

Type* obj = new(arg1, arg2, ...) Type(a1, a2, ...);

It means:

if an operator _new<Type> is matched:
    if a normal operator new is also matched:
        report error: ambiguous call
    else:
        void* ptr = operator _new<Type>(arg1, arg2, ...);
        obj = new(ptr) Type(a1, a2, ...);
else:
    Use formerly standard process;
    // void* ptr = operator new(sizeof(Type), arg1, arg2, ...);
    // obj = new(ptr) Type(a1, a2, ...);

operator new[] with typeinfo

We introduce a new operator named "operator _new[]". Please refer to the following expressions:

template <typename Type>
void* operator _new[](size_t size, size_t count, ArgT1 arg1, ArgT2, ...)
{
    assert(size >= sizeof(Type) * count); // depends
    return malloc(size); // just an example
}

When we create an array by operator new[]:

Type* obj = new(arg1, arg2, ...) Type[count];

It means:

if an operator _new[]<Type> is matched:
    if a normal operator new is also matched:
        report error: ambiguous call
    else:
        size_t size = sizeof(Type) * count + extra_bytes_store_array_size;
        void* ptr = operator _new[]<Type>(size, count, arg1, arg2, ...);
        void* array = store_array_size_and_return_array_buffer(ptr, count);
        obj = construct<Type>(array, count);
else:
    Use formerly standard process;

Issues for Discussion

I. Corresponding operator _delete

Do we need a corresponding operator _delete/_delete[]? My answer is NO. If a standard form of delete pointer is not suitbale, then we can think about defining a corresponding destroy function.

Applications based on operator new with typeinfo

I. A Debug Version of operator new

struct DbgAllocInfo
{
    const std::type_info* typeinfo;
    const char* file;
    int line;
    size_t count;
};
 
std::map<void*, DbgAllocInfo> g_mapDbgInfo;
 
template <typename Type>
void* operator _new(const char* file, int line)
{
    void* ptr = malloc(sizeof(Type));
    DbgAllocInfo info = { &typeid(Type), file, line, 1 };
    g_mapDbgInfo.insert(std::pair<void*, DbgAllocInfo>(ptr, info));
    return ptr;
}
 
template <typename Type>
void* operator _new[](size_t size, size_t count, const char* file, int line)
{
    assert(size >= sizeof(Type) * count); // depends
    void* ptr = malloc(size);
    DbgAllocInfo info = { &typeid(Type), file, line, count };
    g_mapDbgInfo.insert(std::pair<void*, DbgAllocInfo>(ptr, info));
    return ptr;
}
 
void operator delete(void* p)
{
    size_t ret = g_mapDbgInfo.erase(p);
    assert(ret == 1);
    free(p);
}
 
void operator delete[](void* p)
{
    size_t ret = g_mapDbgInfo.erase(p);
    assert(ret == 1);
    free(p);
}
 
void report_memory_leaks()
{
    for (const std::pair<void*, DbgAllocInfo>& leak : g_mapDbgInfo) // for each
    {
        report_leak(leak.first, leak.second);
    }
}

II. A Region Allocator

typedef void (*DestructorType)(void* data);
 
class RegionAlloctor
{
    // Allocate memory without given a cleanup function
    void* allocate(size_t cb);
 
    // Allocate memory with a cleanup function
    void* allocate(size_t cb, DestructorType fn);
 
    // Cleanup and deallocate all allocated memory
    ~RegionAlloctor();
};
 
template <class Type>
struct DestructorTraits
{
    static void destruct(void* data)
    {
        ((Type*)data)->~Type();
    }
 
    static void destructArray(void* data)
    {
        void* array = std::get_array_buffer(data); // depends
        size_t count = std::get_array_size(data); // depends
        destruct<Type>(array, count);
    }
};
 
template <typename Type>
void* operator _new(RegionAlloctor& alloc)
{
    return alloc.allocate(sizeof(Type), DestructorTraits<Type>::destruct);
}
 
template <typename Type>
void* operator _new[](size_t size, size_t count, RegionAlloctor& alloc)
{
    assert(size >= sizeof(Type) * count); // depends
    return alloc.allocate(size, DestructorTraits<Type>::destructArray);
}
 
void main()
{
    RegionAlloctor alloc;
 
    int* intObj = new(alloc) int;
    int* intObjWithArg = new(alloc) int(10);
    int* intArray = new(alloc) int[count];
 
    MyObj* obj = new(alloc) MyObj;
    MyObj* objWithArg = new(alloc) MyObj(100);
    MyObj* objArray = new(alloc) MyObj[count];
 
    // Don't need to call operator delete
}

Proposed Text

template <typename Type>
void* operator _new(ArgT1 arg1, ArgT2 arg2, ...);
 
template <typename Type>
void* operator _new[](size_t size, size_t count, ArgT1 arg1, ArgT2, ...);
 
namespace std
{
    void* get_array_buffer(void* data);
    size_t get_array_size(void* data);
}
Unless otherwise stated, the content of this page is licensed under Creative Commons Attribution-ShareAlike 3.0 License