How to overload new and delete operator for a dll. I have written overloaded operators as part of the dll , but client linking with this dll does not use overloaded new and delete
Here is what the C++ standard has to say about this, in section 17.6.4.6/3:
The program's definitions (of the new / delete operators) are used instead of the default versions supplied by the implementation. Such replacement occurs prior to program startup. The program's definitions shall not be specified as inline. No diagnostic is required.
If you read that carefully, it exactly explains the trouble you are having. There is a kind of "catch 22" happening here.
On one hand, you cannot have the definitions of your new/delete operators compiled inside the DLL because the overloaded new/delete cannot be linked dynamically (this is because new/delete might be needed during static initialization, prior to loading the DLL, so you would have inconsistent new/delete operators before and after the loading of the DLL, and that's undefined behavior).
On the other hand, you cannot just put your new/delete operator definitions in your DLL header files, because they would need to be marked inline in order to satisfy the One Definition Rule (ODR), which, in turn, doesn't satisfy the above clause. The requirement for them to not be marked inline is probably there because a function definition marked inline has "no linkage", resulting in each translation unit using its own compiled version of it (or as inline expansions), which, normally is OK, but not for dynamic memory allocation.
Both of the above catches are motivated by the fact that, for correctness, there generally needs to be a guarantee that memory allocated with new is deallocated with the corresponding delete operator (i.e., "compiled together", so to speak, or both defaulted). For example, if your new/delete operators rely on an underlying malloc/free call, you rely on the heap used by the translation unit that called the new/delete operator, between a DLL and an executable, there is no guarantee that this heap will be the same (in fact, in Windows, in particular, it is not, the two modules use two separate heaps for dynamic memory allocations).
So, the solution to your problem, as Rook puts it, is "don't do that". Don't overload the new/delete operators for DLL objects because there is no clean method to correctly do this, whichever way you twist and turn your code, it will always boil down to the same problem stated above.
What you can and should do instead is to use a factory function pattern for your DLL objects and return a smart pointer (such as a std::shared_ptr) with a custom deleter that relies on a dynamic dispatching of the deletion back to the site where the object was created. This is inspired by a technique from Chad Austin. I have done something very similar here.
You could try and write your own malloc and delete functions and then create a C definition that basically contains the code to override new and delete and call these custom malloc and delete functions.
This way you could do something such as
MODULE_START()
// CODE HERE
MODULE_END()
and this should work smoothly.
Related
In C++ ABI implementations modeled after the Itanium C++ ABI, which is followed by many ABIs for other processors, virtual destructors actually occupy two vtable slots. Besides the "complete object destructor", which does what you would expect, there is a second entry for the "deleting destructor", which calls the first, and then deletes the memory of the object.
There is a problem with this approach, which can be a nuisance in small memory systems: The dynamic memory manager is linked in, even when no other code uses it. This is dead code when there is no call to delete anywhere in the application. This is because the C++ compiler/linker usually isn't able to detect that a slot in the vtable isn't called from anywhere, and hence to remove the associated code. Clearly, it would be better if the deleting destructor could be implemented in a different way that doesn't involve a vtable entry, and allows the compiler/linker to omit this dead code.
One can of course implement a custom void operator delete(void *) {} to prevent the linker from bringing in the dynamic memory code, but this still doesn't prevent the deleting destructor code to be emitted entirely.
Hence my question: Is there no better way to implement deleting destructors? My idea would have been to return the pointer to the start of the memory block to delete from the complete object destructor. If the memory block is to be deleted after destruction, this returned address can be used by a nonvirtual function that calls operator delete. Essentially, having the memory address returned by the complete object destructor would allow the deleting destructor to be nonvirtual, and therefore eligible for dead code elimination.
But I guess I must have overlooked something, which makes that rather simple solution impossible. But what would that be? Can someone expound the the design decision in the Itanium ABI for me?
Edit: I have found information that provides a partial answer here:
The top answer contains this explanation:
When some class defines its own operator delete, the selection of a specific operator delete to call is done as if it was looked up from inside the class destructor. The end result of that is that for classes with virtual destructor operator delete behaves as if it were a virtual function (despite formally being a static member of the class).
Apparently, the way chosen by the Itanium API to make it behave like a virtual function, is to make the destructor that calls it an actual virtual function.
However, that is not the only way to implement it. The linked article centers around an implementation that uses a single virtual function with a hidden parameter, but that solution produces the same undesirable behaviour I was describing above. Another implementation might be to have the complete object destructor return the address of the operator delete() if there is a custom implementation for the class, and nullptr otherwise. This would avoid the problem I described above.
So, in a somewhat modified form, my question still stands.
Unlike normal virtual functions at destruction of a class, one has to call its destructor and destructors of all its parents in the correct order.
It is technically possible to unite all the function calls into a single function call but it would require knowledge of the implementation of each destructor or full structure of the object. Basically de-virtualizing the call. Compilers aren't good at it. Not too sure on all the details as it is quite complex question considering all the possible overly-virtual objects.
The C++ standard very clearly and explicitly states that using delete or delete[] on a void-pointer is undefined behavior, as quoted in this answer:
This implies that an object cannot be deleted using a pointer of type void* because there are no objects of type void.
However, as I understand it, delete and delete[] do just two things:
Call the appropriate destructor(s)
Invoke the appropriate operator delete function, typically the global one
There is a single-argument operator delete (as well as operator delete[]), and that single argument is void* ptr.
So, when the compiler encounters a delete-expression with a void* operand, it of course could maliciously do some completely unrelated operation, or simply output no code for that expression. Better yet, it could emit a diagnostic message and refuse to compile, though the versions of MSVS, Clang, and GCC I've tested don't do this. (The latter two emit a warning with -Wall; MSVS with /W3 does not.)
But there's really only one sensible way to deal with each of the above steps in the delete operation:
void* specifies no destructor, so no destructors are invoked.
void is not a type and therefore cannot have a specific corresponding operator delete, so the global operator delete (or the [] version) must be invoked. Since the argument to the function is void*, no type conversion is necessary, and the operator function must behavior correctly.
So, can common compiler implementations (which, presumably, are not malicious, or else we could not even trust them to adhere to the standard anyway) be relied on to follow the above steps (freeing memory without invoking destructors) when encountering such delete expressions? If not, why not? If so, is it safe to use delete this way when the actual type of the data has no destructors (e.g. it's an array of primitives, like long[64])?
Can the global delete operator, void operator delete(void* ptr) (and the corresponding array version), be safely invoked directly for void* data (assuming, again, that no destructors ought to be called)?
A void* is a pointer to an object of unknown type. If you do not know the type of something, you cannot possibly know how that something is to be destroyed. So I would argue that, no, there is not "really only one sensible way to deal with such a delete operation". The only sensible way to deal with such a delete operation, is to not deal with it. Because there is simply no way you could possibly deal with it correctly.
Therefore, as the original answer you linked to said: deleting a void* is undefined behavior ([expr.delete] §2). The footnote mentioned in that answer remains essentially unchanged to this day. I'm honestly a bit astonished that this is simply specified as undefined behavior rather than making it ill-formed, since I cannot think of any situation in which this could not be detected at compile time.
Note that, starting with C++14, a new expression does not necessarily imply a call to an allocation function. And neither does a delete expression necessarily imply a call to a deallocation function. The compiler may call an allocation function to obtain storage for an object created with a new expression. In some cases, the compiler is allowed to omit such a call and use storage allocated in other ways. This, e.g., enables the compiler to sometimes pack multiple objects created with new into one allocation.
Is it safe to call the global deallocation function on a void* instead of using a delete expression? Only if the storage was allocated with the corresponding global allocation function. In general, you can't know that for sure unless you called the allocation function yourself. If you got your pointer from a new expression, you generally don't know if that pointer would even be a valid argument to a deallocation function, since it may not even point to storage obtained from calling an allocation function. Note that knowing which allocation function must've been used by a new expression is basically equivalent to knowing the dynamic type of whatever your void* points to. And if you knew that, you could also just static_cast<> to the actual type and delete it…
Is it safe to deallocate the storage of an object with trivial destructor without explicitly calling the destructor first? Based on, [basic.life] §1.4, I would say yes. Note that, if that object is an array, you might still have to call the destructors of any array elements first. Unless they are also trivial.
Can you rely on common compiler implementations to produce the behavior you deem reasonable? No. Having a formal definition of what exactly you can rely on is literally the whole point of having a standard in the first place. Assuming you have a standard-conforming implementation, you can rely on the guarantees the standard gives you. You can also rely on any additional guarantees the documentation of a particular compiler may give you, so long as you use that particular version of that particular compiler to compile your code. Beyond that, all bets are off…
If you want to invoke the deallocation function, then just call the deallocation function.
This is good:
void* p = ::operator new(size);
::operator delete(p); // only requires that p was returned by ::operator new()
This is not:
void* p = new long(42);
delete p; // forbidden: static and dynamic type of *p do not match, and static type is not polymorphic
But note, this also is not safe:
void* p = new long[42];
::operator delete(p); // p was not obtained from allocator ::operator new()
While the Standard would allow an implementation to use the type passed to delete to decide how to clean up the object in question, it does not require that implementations do so. The Standard would also allow an alternative (and arguably superior) approach based on having the memory-allocating new store cleanup information in the space immediately preceding the returned address, and having delete implemented as a call to something like:
typedef void(*__cleanup_function)(void*);
void __delete(void*p)
{
*(((__cleanup_function*)p)[-1])(p);
}
In most cases, the cost of implementing new/delete in such fashion would be relatively trivial, and the approach would offer some semantic benefit. The only significant downside of such an approach is that it would require that implementations that document the inner workings of their new/delete implementation, and whose implementations can't support a type-agnostic delete, would have to break any code that relies upon their documented inner workings.
Note that if passing a void* to delete were a constraint violation, that would forbid implementations from providing a type-agnostic delete even if they would be easily capable of doing so, and even if some code written for them would relies upon such ability. The fact that code relies upon such an ability would make it portable only to implementations that can provide it, of course, but allowing implementations to support such abilities if they choose to do so is more useful than making it a constraint violation.
Personally, I would have liked to see the Standard offer implementations two specific choices:
Allow passing a void* to delete and delete the object using whatever type had been passed to new, and define a macro indicating support for such a construct.
Issue a diagnostic if a void* is passed to delete, and define a macro indicating it does not support such a construct.
Programmers whose implementations supported type-agnostic delete could then decide whether the benefit they could receive from such feature would justify the portability limitations imposed by using it, and implementers could decide whether the benefits of supporting a wider range of programs would be sufficient to justify the small cost of supporting the feature.
void* specifies no destructor, so no destructors are invoked.
That is most likely one of the reasons it's not permitted. Deallocating the memory that backs a class instance without calling the destructor for said class is just all around a really really bad idea.
Suppose, for example, the class contains a std::map that has a few hundred thousand elements in it. That represents a significant amount of memory. Doing what you're proposing would leak all of that memory.
A void doesn't have a size, so the compiler has no way of knowing how much memory to deallocate.
How should the compiler handle the following?
struct s
{
int arr[100];
};
void* p1 = new int;
void* p2 = new s;
delete p1;
delete p2;
I understand that there are 3 general ways to modify the behaviour of new and delete in C++:
Replacing the default new/delete and new[]/delete[]
Overriding or overloading the placement versions (overriding the one with a memory location passed to it, overloading when creating versions which pass other types or numbers of arguments)
Overloading class specific versions.
What are the restrictions for performing these modifications to the behaviour of new/delete?
In particular are there limitations on the signatures that new and delete can be used with?
It makes sense if any replacement versions must have the same signature (otherwise they wouldn't be replacement or would break other code, like the STL for example), but is it permissible to have global placement or class specific versions return smart pointers or some custom handle for example?
First off, don't confuse the new/delete expression with the operator new() function.
The expression is a language construct that performs construction and destruction. The operator is an ordinary function that performs memory (de)allocation.
Only the default operators (operator new(size_t) and operator delete(void *) can be used with the default new and delete expressions. All other forms are summarily called "placement" forms, and for those you can only use new, but you have to destroy objects manually by invoking the destructor. Placement forms are of rather limited and specialised need. By far the most useful placement form is global placement-new, ::new (addr) T, but the behavior of that cannot even be changed (which is presumably why it's the only popular one).
All new operators must return void *. These allocation functions are far more low-level than you might appreciate, so basically you "will know when you need to mess with them".
To repeat: C++ separates the notions of object construction and memory allocation. All you can do is provide alternative implementations for the latter.
When you overload new and delete within a class you are effectively modifying the way the memory is allocated and released for the class, asking for it to give you this control.
This may be done when a class wants to use some kind of pool to allocate its instances, either for optimisation or for tracking purposes.
Restrictions, as with pretty much any operator overload, is the parameter list you may pass, and the behaviour it is expected to adhere to.
I've overloaded the global operator new/delete/new[]/delete[] but simple tests show that while my versions of new and delete are being called correctly, doing simple array allocations and deletes with new[] and delete[] causes the implementations in newaop.cpp and delete2.cpp to be called.
For example, this code
int* a = new int[10];
calls operator new[] in newaop.cpp, which in turn calls my version of operator new. So it seems they are globally overloaded but for some reason not the array versions. Is there something I'm missing?
EDIT: My implementation of the operators are in a separate project which is compiled into a library and linked to statically. In retrospect, this might have been useful to include in the original post, as it probably has something to do with this. Although I still can't figure out why only the array versions are affected.
I don't know how you overloaded operator new[] but I just tried it with MSVC2008:
void* operator new[](size_t size)
{
return 0;
}
int main()
{
int* a = new int[5];
}
The code above effectively calls my faulty implementation of operator new[].
So here is my guess: you failed at overloading operator new[] for some reason and your program uses the compiler's version of operator new[] which relies on operator new to allocate the memory. Since you overloaded operator new, your implementation gets called.
operator new allocates one object and calls its constructor. new[] allocates n objects and calls n constructors.
edit: Having multiple new[] and delete[] overloads could be considered a bit of and odd one. How does the compiler know which one to link too? Any chance you could post your overloads? Also do yours get called if you don't link newaop and delete2 in (ie yours are the only implementations in the exe)?
You told us your code is buggy but didn't post any of it :vP . My guess would be that you used int as the argument of the overload rather than size_t. Some overloads might get called because the compiler decided to be tolerant in that instance.
Ok, I managed to crack this so am posting in case anyone else stumbles upon this.
The reason for the operator not being called was because my implementation was located in a library, not in the project which called the operators. In fact, since technically you only need to include an implementation of the operators, they are already globally defined, I only specified the implementation of the operators in a .cpp in my library (this was the wrong step). The code obviously only included the header files from the library and didn't ahve visibility to the implementations. Moreover, Visual Studio seems to have linked newaop.cpp and delete2.cpp into my application. These two files contain implementations for operator new[] and operator delete[] (bot not for regular new/delete!). This is most likely the reason why the compiler saw these two implementations and chose them over mine, which resided in a .cpp file in a library.
The solution to this was to move the implementation of my overloaded operators to a header file in the library which is directly included from my code.
Apart from method calls not being optimized, are there any other issues?
A difference I have noticed is that default compiler generated methods (e.g. operator=) in the class appear either in the exporting dll (if dllimport is used by the client) or in the client binary (if dllimport is not used). In this latter case, it's almost as if part of the class is defined in the dll, and part in the client's binary. Are there any issues with this?
I'm assuming that in both cases the class implementation resides in a DLL, is that correct?
The main issue that I can see with this scenario is that if the compiler-generated assignment operator allocates any memory, even indirectly by calling other copy constructors or assignment operators, the memory would potentially be allocated in the "wrong" heap (the client's heap instead of the DLL's heap, which is where they would be allocated if the assignment operator resided in the DLL). This can lead to memory leak and the occasional hard to debug problem.
That said, if your objects are "trivial enough" that you don't really need to write your own copy constructors and assignment operators (and destructors - see the rule of three), you may well get away with this, especially if you're only throwing a couple of PODs around.