views:

21

answers:

4

I'm trying hard to replace the global new and delete operators with XCode 3.2, GCC 4.2, libstdc++ 4.0, dynamic version.

I took the protypes directly from the header "new" and implemented them. They are pasted below.

The project is a .plugin so a dynamic lib. This plug-in MUST delegate allocation to the main application's own alloc/free routines, which are in an old C SDK.

All my own call to new/delete along with std::list and std::map allocations are correctly replaced, BUT not when std::vector::push_back has to grow its buffer. It that case, my operator new is not called but my operator delete is. I know that, because I write a token in the first four bytes of any buffer allocated by my new operator and I check this token in operator delete. See below for offending code.

extern "C++"
{
__attribute__((visibility("default"))) void* operator new(std::size_t) throw (std::bad_alloc);
__attribute__((visibility("default"))) void* operator new[](std::size_t) throw (std::bad_alloc);
__attribute__((visibility("default"))) void operator delete(void*) throw();
__attribute__((visibility("default"))) void operator delete[](void*) throw();
__attribute__((visibility("default"))) void* operator new(std::size_t, const std::nothrow_t&) throw();
__attribute__((visibility("default"))) void* operator new[](std::size_t, const std::nothrow_t&) throw();
__attribute__((visibility("default"))) void operator delete(void*, const std::nothrow_t&) throw();
__attribute__((visibility("default"))) void operator delete[](void*, const std::nothrow_t&) throw();

}

The following code will cause an assert when "yo" goes out of scope because the memory allocated for the std::vector was not allocated by my operator new.

   {
        std::vector<std::string> yo;
        yo.push_back("yoyoma");
        yo.push_back("yoyoma");
        yo.push_back("yoyoma");
        yo.push_back("yoyoma");
    }

The following code is ok because std::vector::reserve calls my operator new:

   {
        std::vector<std::string> yo;
        yo.reserve(4);
        yo.push_back("yoyoma");
        yo.push_back("yoyoma");
        yo.push_back("yoyoma");
        yo.push_back("yoyoma");
    }

GBD (debugger) won't let met step into the std::vector::push_back implementation when it need to grow the buffer (the method is named _M_insert_aux). All I know is that my operator new is never called from std::vector::push_back.

The workaround above can't be applied to all the 3rd party libs that I'm using. One of which is a big user of push_back.

I tried linking statically to libstdc++.a but I'm having the same issue.

Is there some specialization for std::vector< std::string > that doesn't use the global new operator?

BTW this worked perfectly on windows with VS9.

A: 

I'm not exactly sure how to do exactly what you're asking, but you can achieve your own allocation semantics in the STL by using a custom allocator.

http://www.cplusplus.com/reference/std/memory/allocator/

I think I remember when I was looking at the Hans-Bohen GC for C++ that the library would replace new/delete with a GC version but you still had to pass an allocator to the STL structures you wished to use.

Also, there may be code within libraries you are using still using malloc. Something to think about.

MGoDave
A: 

_M_insert_aux is in vector.tcc which annoyingly doesn't play well with GCC. However, a quick scan shows it calls the allocator properly just like other methods.

  pointer __new_start(this->_M_allocate(__len));

You might try setting a breakpoint in your operator new, defining a custom allocator (you can derive from std::allocator) which prints debugging output, or setting a breakpoint in new_allocator.h:91 where the library should call your override.

Potatoswatter
The implementation in vector.tcc is very cosher. I was in fact able to step into this code when debugging a simple console application. I don't know why GDB won't find the source code when debugging a .dylib.
Mathieu Lamarre
A: 

The most likely explanation is that GCC's internal implementation of std::vector includes additional overloads of new. MSVC has many extra operator new overloads. You will need to rip apart the source code or make a new custom allocator.

DeadMG
GCC does not do that.
Potatoswatter
A: 

I finally solved my problem by using George Costanza's technique: doing the opposite.

Instead of trying to make my operators new and delete visibles, I hid them completely.

The trick is to set these in every static libs & in the bundle settings:

  • Set C++ Standard Library Type : Static
  • Symbols Hidden by Default: Checked
  • Fix & Continue: Unchecked (very important otherwise silently disable previous setting)

and do a clean all & build because with XCode 3.2 simply hitting Build after changing these settings won't work.

Obviously, I changed the operator new & delete prototypes to:

#pragma GCC visibility push(hidden)

extern "C++"
{
    void* operator new(std::size_t) throw (std::bad_alloc);
    void* operator new[](std::size_t) throw (std::bad_alloc);
    void operator delete(void*) throw();
    void operator delete[](void*) throw();
    void* operator new(std::size_t, const std::nothrow_t&) throw();
    void* operator new[](std::size_t, const std::nothrow_t&) throw();
    void operator delete(void*, const std::nothrow_t&) throw();
    void operator delete[](void*, const std::nothrow_t&) throw();
} // extern "C++"

#pragma GCC visibility pop

No why does that work? Must have both Static + Hidden Symbols to work. It seems to shield my bundle plug-in from STL specializations implementations that inlined their allocators.

Also note that this only happened in a bundle plug-in dynamically loaded from a big app. In a trivial .dylib project called from a console application everything worked fine with any settings.

Mathieu Lamarre