tags:

views:

93

answers:

2

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.

+1  A: 

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).

Jerry Coffin
Allocators don't have a state, so I'm not sure how I could have a flag in one?
NuSkooler
@NuSkooler: more accurately, the standard library isn't *required* to support allocators that have state. Nonetheless, it's the first thing I'd try. If memory serves, C++0x requires them to support stateful allocators, so I'd guess there's a decent chance it'll work. I'd only do something else if you find it's truly needed.
Jerry Coffin
@Jerry: I created a release_ownership() in my custom allocator that sets the value of a member bool. In deallocate() the bool is checked and does nothing if true. However, the address is always different between the release_ownership() and deallocate() so bool is always false (it's ctor default).
NuSkooler
+1  A: 

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.

Potatoswatter
Is the performance penalty of swap() heavy? The point in me doing all of this is so I can utilize STL (e.g. vector<> ) and C++, yet when it's time, pass the data on to the C API without a the_api_malloc() and memcpy(). If swap() is light weight, I suppose I could have a non-freeing compatible allocator to swap to anywhere I was previously thinking of calling release() ?
NuSkooler
@NuSkooler: `swap` is very, very fast. On the order of a dozen machine cycles. I wouldn't try a custom allocator here. Replace the pointer that receives control with a `vector` (empty before swap), replace `release` with `swap`, and you should be set.
Potatoswatter
@Potatoswatter: Sorry if I'm being dense here: Are you suggesting I swap() from the custom allocator (that uses the_api_malloc()) to a standard vector? Wouldn't that just cause the second vector to attempt to delete memory when it's destroyed? If I take the custom allocator out of the mix entirely, memory won't be allocated using the_api_malloc() and therefor cannot be free'd by the_api_give_back(). Perhaps I'm missing something simple here?
NuSkooler
@NuSkooler: Sorry, I forgot you already had a custom allocator. What I mean is, use the custom allocator only to call the special malloc and free, and call swap outside the allocator, where vector->release would be desirable. The second vector will delete the memory when it's destroyed… so don't destroy until you want that to happen. Whoever receives the block of memory, receives the vector.
Potatoswatter
@Potatoswatter: I think you're missing the intent here. the_api_give_back() takes ownership of the pointer handed to it and internally calls the_api_free(). Therefor, I need the memory to NOT be deleted by my code when I hand said API it's pointer. This is the intent of a release() type functionality.
NuSkooler
@NuSkooler: I guess I am. `release` functionality does mean not deleting, and that's what I'm describing how to do, but you are saying that you want to call `the_api_free` as soon as it's released, which sounds equivalent to deleting, so I'm confused.
Potatoswatter