NuSkooler
NuSkooler

Reputation: 5525

Custom allocator for std::vector<> with release?

I am working with a 3rd party C API set in C++ that has two methods of concern for this discussion:

  1. It's equivalent of malloc(): the_api_malloc(size) (plus a matching the_api_free())
  2. A function in which memory created with the_api_malloc() is returned to that takes ownership of it and the_api_free()'s it internally: the_api_give_back(ptr)

I have created a custom allocator wrapping the_api_malloc() and the_api_free() to use with for example std::vector. This works great.

What I'd like to do is have a std::vector type class that utilizes my custom allocator but also has a release() method that when called, releases ownership of it's memory and therefor will not call my custom allocators the_api_free().

pointer release() /* pointer is of T* */

Example usage:

MyClass myClass(1024); // the_api_malloc()'s 1024 bytes
// ... do something with myClass
the_api_give_back(myClass.release());

I'm not sure the best way to pull this off. What I have right now as a experiment is rather nasty:

class MyClass : public std::vector<char, MyAllocator<char> > {
public:
    using typename std::vector<char, MyAllocator<char> >::pointer;

    pointer release() {
        // note: visual studio impl.
        pointer p = this->_Myfirst;
        this->_Myfirst = 0;
        this->_Mylast = 0;
        this->_Myend = 0;
        return p;
    }
}

Is there a better way?

UPDATE 1: Here is what I've tried based on suggestions below. This should also help illustrate desired behavior & where it is currently failing.

template <class T>
class MyAllocator
{
public:
  // types omitted for clarity

  MyAllocator() : m_released(false) { }

  template <class U>
  MyAllocator(MyAllocator<U> const& a) : m_released(a.m_released) { }

  // other ctors, dtors, etc. omitted for clarity

  // note: allocate() utilizes the_api_malloc()

  void deallocate(pointer p, size_type num)
  {
    if(!m_released) {
      the_api_free(p);
    }
  }

  void release_ownership() { m_released = true; }

  bool m_released;
};

template <typename T>
char* ReleaseOwernship(T& container)
{
  container.get_allocator().release_ownership();
  return &container[0];
}

// usage:
{ // scope
  std::vector<char, MyAllocator<char> > vec;

  // ...do something to populate vec...

  char* p = ReleaseOwnership(vec);
  the_api_give_back(p); // this API takes ownership of p and will delete it itself
} // end scope - note that MyAllocator::deallocate() gets called here -- m_release is still false

UPDATE 2: Tried creating a MyOwningAllocator and a MyNonOwningAllocator then swapping from the owning to the non-owning where at "release time", but can't get swap() to work as they are different types.

Upvotes: 2

Views: 3270

Answers (3)

ceztko
ceztko

Reputation: 15207

I don't know if you were able to finish your implementation but I were able to code a solution for the same issue using modern C++ in this other SO answer.

Upvotes: 1

Potatoswatter
Potatoswatter

Reputation: 137810

vector::swap will transfer ownership of the allocated block to another vector. However, there is no way to stop a vector from calling vector::allocator_type::deallocate in its destructor, and there is no portable way to directly modify the internal pointers.

Upvotes: 1

Jerry Coffin
Jerry Coffin

Reputation: 490148

Instead of trying to stop the vector from calling the allocator's free function, I'd include your release as a member of your allocator, and have it set a flag. When the flag is set, the_api_free will simply return (i.e., act as a nop).

Upvotes: 1

Related Questions