I use an external library which operates on large quantities of data. The data is passed in by a raw pointer, plus the length. The library does not claim ownership of the pointer, but invokes a provided callback function (with the same two arguments) when it is done with the data.
The data gets prepared conveniently by using std::vector<T>
, and I'd rather not give up this convenience. Copying the data is completely out of the question. Thus, I need a way to "take over" the memory buffer owned by an std::vector<T>
, and (later on) deallocate it in the callback.
My current solution looks as follows:
std::vector<T> input = prepare_input();
T * data = input.data();
size_t size = input.size();
// move the vector to "raw" storage, to prevent deallocation
alignas(std::vector<T>) char temp[sizeof(std::vector<T>)];
new (temp) std::vector<T>(std::move(input));
// invoke the library
lib::startProcesing(data, size);
and, in the callback function:
void callback(T * data, size_t size) {
std::allocator<T>().deallocate(data, size);
}
This solution works, because the standard allocator's deallocate
function ignores its second argument (the element count) and simply calls ::operator delete(data)
. If it did not, bad things could happen, as the size
of the input vector might be quite a bit smaller than its capacity
.
My question is: is there a reliable (wrt. the C++ standard) way of taking over the buffer of std::vector
and releasing it "manually" at some later time?
Vectors are assigned memory in blocks of contiguous locations. When the memory allocated for the vector falls short of storing new elements, a new memory block is allocated to vector and all elements are copied from the old location to the new location. This reallocation of elements helps vectors to grow when required.
These abilities come at a price: vectors consume more memory in exchange to handle storage and grow dynamically in size.
No, memory are not freed. In C++11, you can use the shrink_to_fit method for force the vector to free memory. Show activity on this post.
You can't take ownership of the memory from a vector, but you can solve your underlying problem another way.
Here's how I'd approach it - its a bit hacky because of the static global variable and not thread safe, but it can be made so with some simple locking around accesses to the registry
object.
static std::map<T*, std::vector<T>*> registry;
void my_startProcessing(std::vector<T> * data) {
registry.put(data->data(), data);
lib::startProcesing(data->data(), data->size());
}
void my_callback(T * data, size_t length) {
std::vector<T> * original = registry.get(data);
delete original;
registry.remove(data);
}
Now you can just do
std::vector<T> * input = ...
my_startProcessing(input);
But watch out! Bad things will happen if you add/remove elements to the input after you've called my_startProcessing
- the buffer the library has may be invalidated. (You may be allowed to change values in the vector, as I believe that will write through the to data correctly, but that will depend on what the library allows too.)
Also this doesn't work if T
=bool
since std::vector<bool>::data()
doesn't work.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With