2

We have a library that provides a C interface via extern "C", and is used from C code, but inside it uses STL containers and some C++ features like RAII for convenience.

Now there is a new requirement that the library should be able to take pointers to a custom malloc and free function coming from the client code, and use that for allocations inside. I can place them into the context structure of the library, and use them where needed, but using them with STL is puzzling...

I looked at allocator classes but it seems STL containers must be able to use the default constructors to create the allocator and it seems there is no way to place these pointers into them to let them call through them to do the allocations.

Is it possible to work this around preferably in a thread safe manner (without using globals)?

Calmarius
  • 18,570
  • 18
  • 110
  • 157

3 Answers3

5

I looked at allocator classes but it seems STL containers must be able to use the default constructors to create the allocator

That's not true, all containers can be constructed with an allocator explicitly, so you can create your allocator object and then pass it to the container.

extern "C"
{
  typedef void* (*allocation_function)(size_t);
  typedef void (*deallocation_function)(void*);
}

template<typename T>
class Allocator
{
public:
  typedef T value_type;

  Allocator(allocation_function alloc, deallocation_function dealloc)
  : m_allocate(alloc), m_deallocate(dealloc)
  { }

  template<typename U>
    Allocator(const Allocator<U>& a)
    : m_allocate(a.m_allocate), m_deallocate(a.m_deallocate)
    { }

  T* allocate(size_t n)
  { return static_cast<T*>(m_allocate(n * sizeof(T))); }

  void deallocate(T* p, size_t)
  { m_deallocate(p); }

private:
  template<typename U>
    friend class Allocator<U>;

  template<typename U>
    friend bool operator==(const Allocator<U>&, const Allocator<U>&);

  allocation_function   m_allocate;
  deallocation_function m_deallocate;
};

template<typename T>
bool operator==(const Allocator<T>& l, const Allocator<T>& r)
{ return l.m_allocate == r.m_allocate; }

template<typename T>
bool operator!=(const Allocator<T>& l, const Allocator<T>& r)
{ return !(l == r); }


Allocator<int> a(custom_malloc, custom_free);
std::vector<int, Allocator<int>> v(a);

If you're using not using C++11 yet then you need to provide a lot more members for your allocator to meet the old requirements, but the one above is OK for C++11. Using custom allocators in C++03 is difficult and not portable anyway, so you should aim to use a C++11 compiler if you need to do this.

Jonathan Wakely
  • 166,810
  • 27
  • 341
  • 521
  • This constructor of std::map causes me the trouble: `map(): Mybase(key_compare(), allocator_type())` hence the question. (In VS2008). But that can be well because there are instances constructed by the default constructor. Are compilers smart enough to not complain if I don't use the default constructor at all when declaring variables? – Calmarius Mar 11 '15 at 17:49
  • 1
    Yes. If you don't use the default constructor (and don't explicitly instantiate the whole `map` type) then the allocator default constructor isn't needed. – Jonathan Wakely Mar 11 '15 at 17:51
  • I don't know how much of the C++11 allocator requirements VS2008 implements, you might need to provide a number of other member functions such as `construct` and `destroy` and define several typedefs such as `rebind::other`, `pointer`, `const_pointer`, `reference` etc. but if you do all that it should work OK. – Jonathan Wakely Mar 11 '15 at 17:59
  • Indeed after refactoring all instances to take an allocator instance then it compiles fine. That's interesting I never knew C++ compilers are such smart. – Calmarius Mar 11 '15 at 18:42
  • It's because `std::map` is a template. Unused member functions of class templates are not instantiated. – Jonathan Wakely Mar 11 '15 at 18:48
0

Yes. As an example, look into header gc/gc_allocator.h from Boehm garbage collector (you could easily replace the lower calls to GC_MALLOC etc by some function pointers). See this answer.

Community
  • 1
  • 1
Basile Starynkevitch
  • 223,805
  • 18
  • 296
  • 547
-2

Since allocators cannot be state-full but must be default constructible, I would suggest using templates to instantiate the allocators at compile time.

randomusername
  • 7,927
  • 23
  • 50
  • 1
    _"Since allocators cannot be state-full but must be default constructible"_ neither of those things is true these days. – Jonathan Wakely Mar 11 '15 at 17:48
  • @JonathanWakely I'm talking about C++-03 – randomusername Mar 11 '15 at 17:48
  • 2
    They aren't required to be default constructible even in C++03. And it isn't true that they _cannot_ be stateful, it's just not guaranteed your implementation will support it ... but in practice all do support stateful allocators. Anyway, using templates doesn't solve the problem of using user-provided allocation functions (unless they just refer to globals, which the OP ruled out) – Jonathan Wakely Mar 11 '15 at 17:55
  • The problem I remember when doing a stateful allocator was "moving" resources. As far as I can remember, GCC always copied my allocator. http://stackoverflow.com/a/24279177/1462718 Seems to say stateful allocators are "nearly" impossible. – Brandon Mar 11 '15 at 18:13