2014-11-26 3 views
7

Я использую внешнюю библиотеку, которая работает с большими объемами данных. Данные передаются с помощью необработанного указателя плюс длина. Библиотека не заявляет права собственности на указатель, но вызывает предоставленную функцию обратного вызова (с теми же двумя аргументами), когда это делается с данными.Получение памяти из std :: vector

Данные получают при помощи std::vector<T>, и я предпочел бы отказаться от этого удобства. Копирование данных полностью исключено. Таким образом, мне нужен способ «захватить» буфер памяти, принадлежащий std::vector<T>, и (позже) освободить его в обратном вызове.

Мое текущее решение выглядит следующим образом:

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

и, в функции обратного вызова:

void callback(T * data, size_t size) { 
    std::allocator<T>().deallocate(data, size); 
} 

Это решение работает, потому что deallocate функция стандартного Allocator игнорирует его второй аргумент (счетчик элементов) и просто вызывает ::operator delete(data). Если это не так, могут произойти плохие вещи, так как size входного вектора может быть немного меньше его capacity.

Мой вопрос: есть ли надежный (по стандарту C++) способ взять буфер std::vector и высвободить его «вручную» через некоторое время?

+1

Вы должны будете взять на себя весь вектор. –

+0

Было бы неплохо, если бы 'vector' имел функцию' detach' ... но это не –

+0

@TC: но мне некуда ее хранить - входное производство и освобождение происходит в двух отдельных частях прогама –

ответ

2

Вы не можете взять на себя ответственность за память от вектора, но вы можете решить свою основную проблему по-другому.

Вот как я подхожу к нему - его немного взломать из-за статической глобальной переменной и не потокобезопасно, но это можно сделать с помощью некоторой простой блокировки при доступе к объекту registry.

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); 
} 

Теперь вы можете просто сделать

std::vector<T> * input = ... 
my_startProcessing(input); 

Но берегитесь! Плохие вещи произойдут, если вы добавите/удалите элементы на вход после того, как вы вызвали my_startProcessing - буфер, который может иметь библиотека, может быть признан недействительным. (Вам может быть позволено изменять значения в векторе, так как я считаю, что они будут правильно записывать данные, но это будет зависеть от того, что позволяет библиотека.)

Также это не работает, если T = bool так как std::vector<bool>::data() не работает.

+0

Выглядит хорошо. Если я не найду способ избежать глобальных переменных, я покрою это с помощью «std :: mutex» и «std :: unique_ptr», и все должно быть хорошо. Благодаря! –

1

Вы можете создать пользовательскую структуру класса над вектором.

Ключевым моментом здесь является использование семантики перемещения в конструкторе SomeData.

  • вы получаете подготовленные данные без копирования (обратите внимание, что вектор источника будет очищен)
  • данные будет правильно утилизирован thisData вектора деструктор
  • источник вектор может быть утилизирован без какого-либо вопроса

Поскольку базовый тип данных будет массивом, вы можете рассчитать стартовый указатель и размер данных (см. SomeDataImpl.h ниже):

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(); 
} 

Пример использования:

#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; 
} 
Смежные вопросы