Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is it possible to completely disable the default C++ new operator?

Because our app has hard performance and memory constraints, our coding standards forbid the use of the default heap — ie, no malloc, no default new. Every memory allocation has to choose one of a few specific allocators; something like

// declared globally void* operator new( size_t size, CustomAllocHeap* heap, const char* perpetrator_name ) {   return heap->Allocate( size, perpetrator_name ); }  // imagine a bunch of CustomAllocHeap's declared globally or statically, thus  Vector* v = new( gPhysicsHeap, __FUNCTION__ ) Vector( 1.0f, 2.0f, 3.0f, 4.0f ); // or in a class Thingy* p = new( this->LocalArenaHeap, __FUNCTION__ ) Thingy(); 

Although we've maintained good discipline on this with our code, some standard C++ components ( containers, std::function ) covertly make calls to the default new heap, which is very bad.

It would be nice to disable the default new altogether in some way, so that any line of code that implicitly results in a default allocation immediately throws a compiler error. That would let us notice these things right away.

We can obviously make new throw a runtime error ie

void* operator new ( size_t ) { __debugbreak(); return NULL; }   

but it would be much better to get warnings about this at compile time. Is that possible?

Our app is built for a fixed platform (x64 with Visual Studio); portability is irrelevant.

like image 289
Crashworks Avatar asked Aug 21 '13 19:08

Crashworks


People also ask

Is operator new thread safe?

The C++ new and delete operators are thread safe, but this means that a thread may have to wait for a lock on these operations. Once memory is obtained for a thread, the thread_alloc memory allocator keeps that memory available for the thread so that it can be re-used without waiting for a lock.

Does the new operator allocate memory?

The new operator denotes a request for memory allocation on the Free Store. If sufficient memory is available, a new operator initializes the memory and returns the address of the newly allocated and initialized memory to the pointer variable.

What is :: operator new?

The new operator invokes the function operator new . For arrays of any type, and for objects that aren't class , struct , or union types, a global function, ::operator new , is called to allocate storage. Class-type objects can define their own operator new static member function on a per-class basis.


2 Answers

You can implement the default new to call an unimplemented function. Then, at link time, you will get an error to the users of the bare new call:

#include <stdexcept> inline void * operator new (std::size_t) throw(std::bad_alloc) {     extern void *bare_new_erroneously_called();     return bare_new_erroneously_called(); } 

When I tested it on IDEONE, I got this error:

/home/geXgjE/ccrEKfzG.o: In function `main': prog.cpp:(.text.startup+0xa): undefined reference to `bare_new_erroneously_called()' collect2: error: ld returned 1 exit status 

In my tests, using g++, there is no link error if there are no references to the bare new in the program. This is because g++ does not emit code for unused inline functions.

I don't have Visual Studio installed on my system, so the following information is just based on some documentation I have found. In order to get the inlined new operator to be seen everywhere, you should put its definition in a header file, and then use the /FI detect_bare_new.h option in your compiler.* According to this answer, Visual Studio will not generate code for unused inline functions (like g++). However, you should check to see if there is an optimization level that needs to be enabled for that behavior or not.

* g++ has a similar compiler option: -include detect_bare_new.h.

This assumes that you intend to pass your own allocators to C++ templates and classes in the standard C++ library. If you do not, then inlined code in the standard headers that call the default allocator (which will call new) will trigger the linking error as well. If you wish to allow the standard C++ library to use the default new, then an easy way to make it work (at the expense of longer compile times) is to add all the standard C++ headers you intend to include at the top of the detect_bare_new.h file.

You state that portability of the solution is not important to you. But for the sake of completeness, I should highlight the issue that Ben Voigt correctly points out: The C++ standard does not guarantee the behavior of not generating code for unused inline functions. So, one may get a linking error even if the function is not used. But, if the code has no other references to the unimplemented function except within the stubbed new implementation, the error would be within the new definition itself. For example, g++ could generate an error like:

/home/QixX3R/cczri4AW.o: In function `operator new(unsigned int)': prog.cpp:(.text+0x1): undefined reference to `bare_new_erroneously_called()' collect2: error: ld returned 1 exit status 

If your system is one that generates code for unused inline functions, you may still have a workaround. The workaround will work if the linker will report all erroneous references to the undefined function. In that case, if the only linking error observed is due to the definition of the new operator itself, there are no unexpected calls to the bare new. After verifying that the code only has that single error, you could then change the link line to include an object or library that has an appropriate definition of bare_new_erroneously_called() that would throw a runtime exception.

like image 88
jxh Avatar answered Oct 07 '22 23:10

jxh


If your own "new" operator is not named "new" but differently (e.g. "myNew") you could use a "#define" in a way replacing "new" by rubbish:

#define new *+-/& 

The pre-compiler would now replace a "new":

x = new mytype; 

By the rubbish:

x = *+-/& mytype; 

The advantage compared to a message at linking time is that this will generating a compiler message immediately while compiling the C++ file, not in the end when linking. You also see the line where the "new" is located.

The disadvantage is that you'll have to "#include" the file containing this "#define" in all C++ files in your project.

like image 26
Martin Rosenau Avatar answered Oct 07 '22 23:10

Martin Rosenau