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?
vector
had adetach
function... but it doesn't – Fogboundunique_ptr<vector<T>> temp(new vector<T>(move(input)));
? Also, your solution only works ifT
is a trivially destructible type, otherwise you'll need to callallocator<T>::destroy
on each element. To answer your question, there's no easy way taking over the memory from avector
, you might be able to pull something off using a custom allocator, but I'd just stick to the current solution. – Selfemployedvector
, you could make your own vector-like class that does have adetach
function. – Fogboundvoid (*callback)(T * data, size_t size, void * user_data)
andstartProcessing(T* data, size_t size, void * userdata)
you'd have an easy path to a solution. – Excisetemp
array go out of scope? Even if that works in practice, surely that's undefined behavior? (I'm asking, I don't know the answer) – Selfemployedtemp
out of scope undefined behaviour (instead of just the destructor of whatever was placed there not being called)? The documentation ofstd::allocator<T>::deallocate
states, that the second argument should be exactly the one passed toallocate
-- surely violating the specification is non-standard. – Transitlist
– Fogboundvector
in some (synchronized) container that can be accessed from both places. Then there's no need for what you're doing above. And regardless of whether lettingtemp
go out of scope is UB, you're already breakingallocator::deallocate
's contract by passing it asize
that's not necessarily the same as that passed toallocator::allocate
– Selfemployedunique_ptr<T[]>
instead ofvector<T>
? – Monkeyvector
doesn't go out of scope. Something that you have to control by the code flow. You can dolib::startProcesing(input.data(), input.size);
as longs as that function promises not to reallocate or do strange things with its pointer. Who is callingcallback
BTW? – Terri