taking over memory from std::vector
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?
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.
You could create custom class build over a vector.
Key point here is to use move semantics in SomeData
constructor.
thisData
vector destructor Since underlying datatype is going to be array you can calculate start pointer and a data size (see SomeDataImpl.h
below):
SomeData.h
#pragma once
#include <vector>
template<typename T>
class SomeData
{
std::vector<T> thisData;
public:
SomeData(std::vector<T> && other);
const T* Start() const;
size_t Size() const;
};
#include "SomeDataImpl.h"
SomeDataImpl.h
#pragma once
template<typename T>
SomeData<T>::SomeData(std::vector<T> && otherData) : thisData(std::move(otherData)) { }
template<typename T>
const T* SomeData<T>::Start() const {
return thisData.data();
}
template<typename T>
size_t SomeData<T>::Size() const {
return sizeof(T) * thisData.size();
}
Usage example:
#include <iostream>
#include "SomeData.h"
template<typename T>
void Print(const T * start, size_t size) {
size_t toPrint = size / sizeof(T);
size_t printed = 0;
while(printed < toPrint) {
std::cout << *(start + printed) << ", " << start + printed << std::endl;
++printed;
}
}
int main () {
std::vector<int> ints;
ints.push_back(1);
ints.push_back(2);
ints.push_back(3);
SomeData<int> someData(std::move(ints));
Print<int>(someData.Start(), someData.Size());
return 0;
}
链接地址: http://www.djcxy.com/p/23554.html
下一篇: 从std :: vector接管内存