views:

339

answers:

4

I'm maintaining a plugin (implemented as a dll) for a big closed source application. This has been working fine for years. However, with the latest update to it's SDK the vendor overloaded global operators new and delete. This causes lots of trouble for me. What happens is that my plugin allocates a string. I pass this string into a statically linked library which modifies it (changes it's length thus reallocating it). My application crashes.

The reason is of course, that the string lives on the vendor allocated custom heap. The statically linked library knows nothing about this heap and tries to use the default new/delete operators on that memory. Boom.

Now the question is: how can I keep my code clean and avoid using the vendor's operators? There is no conditional preprocessor macro. I can not avoid including the offending header since it contains 2000 lines more code I need for the plugin. I cannot pass the provided allocator into the other library since it does not provide any mechanisms for that. I have already bugged the vendor about it. I don't know what else I could try?

Addendum: After some heated debate I have managed to convince the vendor to remove the overloads again from the next version of the SDK. I have solved my immediate problem by simply hacking the current SDK and removing the overloads manually. Thanks for all the suggestions in this thread. They served as arguments and further "proof" of why the overloads were a bad idea in the first place.

A: 

One option is to create your own overloaded new operator that can be implemented in terms of malloc.

This could be defined like:

enum MyNew {EMyNew};

void *operator new(size_t size, MyNew);

This can then be called by you as MyClass* myClass = new (EMyNew)MyClass;

Since this is implemented in terms of malloc, it should behave as expected. The only downer is that you will have to replace all the instances of where you have used new.

doron
Unfortunately this is not an option since I don't know or control "all the instances where I have used new". Think STL, linked libraries etc.
BuschnicK
STL can be overcome with the use of a custom allocator. <br/>Linked libraries should be safe due to the fact that they are not compiled against your header files.
doron
+1  A: 

You can use another new in your namespace:

namespace MyNS {
    // Declare your new/delete operators here
    // and also declare a class implementing the same interface as std::allocator
    // using your newly created memory management functions.
    // Don't forget to put all your classes in the namespace.
    // (if you don't have one already)
}

Your can then use all STL classes, giving them your allocator type as template argument.

Fififox
Putting your new/delete in your own namespace won't ensure they are always used (types in that namespace are often referenced inside other namespaces). In fact, it'll probably just cause more allocator mismatch issues. Defining a new allocator for use with STL types is an orthogonal issue, such a type has nothing to do with the global new/delete and doesn't have to be in any particular namespace.
Terry Mahaffey
And, custom allocators are not derived from std::allocator. See the link in my answer.
Terry Mahaffey
Corrected the error about inheritance. Nevertheless, if new/delete operators are declared in a namespace, everything in that namespace will use them. Of course this not perfect (as you said, other classes not in that namespace won't use them). Better is to restore the original global new. A good idea when overloading is using the same overloading trick as used by the standard "nothrow" new.
Fififox
BTW It's the trick deus-ex-machina also explained.
Fififox
+3  A: 

If you're compiling in (via header inclusion) an overridden new/delete operator(s), then all calls in your code to new/delete will use them. There is no way to re-override it (link errors) or only partially override it, etc.

It is bad form to override the global new/delete operators, at all. It's a bad idea. If you don't realize why it's a bad idea, you're not qualified to do so. If you do realize why it's a bad idea, you're qualified to do so, but you'll generally choose not to.

Defining a global new/delete is exponentially more evil in a component you expect people to include directly into their project. It is your job as a customer to help the vendor doing this understand the seriousness of the situation, or stop being their customer.

You can define a custom allocator type (see this link for a good tutorial on how to do so, the interface needed, etc) and use that exclusively with your STL types (it's a template argument).

For shared_ptr, you need to do something a little different: it takes a deleter object as a parameter to the constructor if you don't want the default "delete p" behavior. This isn't a custom allocator; it's just a regular unary functor.

Terry Mahaffey
You are correct about being stuck with the overridden new and delete.However I disagree with your assertion that overriding global new and delete is bad form. I've done it, and worked on dozens of projects where other people have done it. As a general rule of thumb though you NEVER override new and delete in libraries because that can be a pain for your users. Which is why many good libraries follow the STL convention allowing users to specify their own allocators for objects. But overriding new and delete is very handy for managing system resources, you just need to know what you're doing.
Chris
+2  A: 

Isn't it possible to do this:

namespace evil{

#include "evil_header.h"

}

Then what evil_header declares as global new/delete becomes evil::new/evil::delete. I doubt this will play well if there are non-header definitions of things declared in evil_header though.

Autopulated