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

Я получил область памяти из DirectX, где хранится информация о моих вершинах. Чрезвычайно удобный способ работы с информацией о вершинах — использовать std::vector‹> структуры, содержащей информацию о вершинах.

Учитывая, что у меня есть указатель на большой буфер, могу ли я использовать std::vector для управления элементами в буфере? Создание std::vector регулярно приводит к тому, что у него есть собственный адрес, а это не совсем то, что мне нужно. Могу ли я как-то использовать новое размещение оператора?


person kvanbere    schedule 11.02.2013    source источник
comment
Какие операции вы хотите использовать из std::vector?   -  person David Brown    schedule 11.02.2013
comment
Просто .push_back() для добавления вершин, возможность их удаления была бы отличной, но не обязательной, и .size() чтобы узнать количество вершин в контейнере.   -  person kvanbere    schedule 11.02.2013
comment
push_back заставит вектор выделить новый блок памяти в другой ячейке памяти, в которой есть место для дополнительных элементов.   -  person David Brown    schedule 11.02.2013
comment
Похоже, что самый простой способ - это, вероятно, просто написать свою собственную структуру, тогда std::vector кажется слишком сложным для этой цели.   -  person kvanbere    schedule 11.02.2013
comment
@kvanberendonck Возможно, вы могли бы использовать std::vector с настраиваемым распределителем, который постоянно возвращает одно и то же пространство в буфере, но ему придется выбросить, если вектор запрашивает больше места, чем есть в буфере, и нет простого способа попросить вектор выделить точно размер буфера.   -  person John5342    schedule 11.02.2013
comment
Вы можете проверить std::scoped_ptr< T[] >   -  person MSalters    schedule 11.02.2013
comment
Используйте array_view из GSL ( github.com/Microsoft/GSL ) или boost::multi_array_ref.   -  person sbabbi    schedule 15.01.2016


Ответы (2)


Да, ты можешь. Используйте настраиваемый распределитель. В этом аллокаторе возвращается адрес вашей памяти DirectX.

Вот полный пример, основанный на ответе Убедительные примеры пользовательских распределителей C++ STL?. Это решение использует новое размещение в распределителе.

#include <memory>
#include <iostream>
#include <vector>

using namespace std;

template <typename T>
class placement_memory_allocator: public std::allocator<T>
{
    void* pre_allocated_memory;
public:
    typedef size_t size_type;
    typedef T* pointer;
    typedef const T* const_pointer;

    template<typename _Tp1>
    struct rebind
    {
            typedef placement_memory_allocator<_Tp1> other;
    };

    pointer allocate(size_type n, const void *hint=0)
    {
            char* p = new(pre_allocated_memory)char[n * sizeof(T)];
            cout << "Alloc " << n * sizeof(T) << " bytes @" << hex << (void*)p <<endl;
            return (T*)p;
    }

    void deallocate(pointer p, size_type n)
    {
            cout << "Dealloc " << n << " bytes @" << hex << p << endl;
            //delete p;
    }

    placement_memory_allocator(void* p = 0) throw(): std::allocator<T>(), pre_allocated_memory(p) { cout << "Hello allocator!" << endl; }
    placement_memory_allocator(const placement_memory_allocator &a) throw(): std::allocator<T>(a) {pre_allocated_memory = a.pre_allocated_memory;}
    ~placement_memory_allocator() throw() { }
};

class MyClass
{   
    char empty[10];
    char* name;
public:
    MyClass(char* n) : name(n){ cout << "MyClass: " << name << " @" << hex << (void*)this << endl; }
    MyClass(const MyClass& s){ name = s.name; cout << "=MyClass: " << s.name << " @" << hex << (void*)this << endl; }
    ~MyClass(){ cout << "~MyClass: " << name << " @" << hex << (void*)this <<  endl; }
};

int main()
{
    // create allocator object, intialized with DirectX memory ptr.
    placement_memory_allocator<MyClass> pl(DIRECT_X_MEMORY_PTR);
    //Create vector object, which use the created allocator object.
    vector<MyClass, placement_memory_allocator<MyClass>> v(pl);
    // !important! reserve all the memory of directx buffer.
    // try to comment this line and rerun to see the difference
    v.reserve( DIRECT_X_MEMORY_SIZE_IN_BYTES / sizeof(MyClass));

    //some push backs.
    v.push_back(MyClass("first"));
    cout << "Done1" << endl;
    v.push_back(MyClass("second"));
    cout << "Done1" << endl;
    v.push_back(MyClass("third"));
    cout << "Done1" << endl;

}
person Yousf    schedule 11.02.2013
comment
Здравствуйте, я скопировал его в тестовый проект, чтобы отладить и посмотреть, как он работает. Я использовал malloc для создания простого буфера постоянного размера для имитации буфера vbo (и установил DIRECT_X_MEMORY_PTR как void*, указывающий на него) и установил размер, соответствующий тому, что я выделял (и установил DIRECT_X_MEMORY_SIZE_IN_BYTES для этого как size_t). При компиляции с использованием msvc 2012 я получаю error C2664: 'placement_memory_allocator<T>::placement_memory_allocator(void *) throw()' : cannot convert parameter 1 from 'std::_Wrap_alloc<_Alloc>' to 'void *'. Любые идеи? [Код здесь: pastie.org/6121725 ] - person kvanbere; 12.02.2013
comment
Что-то изменилось в Visual Studio 2012. Microsoft изменила некоторые заголовочные файлы, связанные с распределителем. См. здесь некоторую информацию bit.ly/UacbcT - person Yousf; 12.02.2013

Элементы std::vector динамически распределяются в куче (с new самим std::vector) таким образом, что они всегда находятся в памяти непрерывно. Итак, если ваша структура vertex, использование std::vector<vertex> не то, что вам нужно, поскольку элементы вектора не будут расположены в вашем большом буфере.

Однако вы можете использовать std::vector<vertex*>, например:

vertex* bigBuffer;     // provided by DirectX
size_t bigBufferLen;   // provided by DirectX

std::vector<vertex*> array;
for (size_t i = 0; i < bigBufferLen; ++i)
{
    array.push_back(bigBuffer + i * sizeof(vertex));
}
person Aurelien    schedule 11.02.2013
comment
Я думаю, что, возможно, неправильно понял вопрос. Если это так, скажите мне, и я удалю свой ответ. - person Aurelien; 11.02.2013
comment
Я действительно хочу управлять памятью с помощью вектора, как если бы это был массив с возможностью добавления/удаления. Итак, если буфер, заданный DX, является длинным и непрерывным буфером, я надеялся, что для добавления векторов в VBO я мог бы использовать push_back, а затем передать vector.size() как количество векторов для рисования в DX. Приветствуются предложения по другим возможным структурам/вариантам/рамкам! - person kvanbere; 11.02.2013
comment
Я бы предпочел не использовать memcpy из .data() в фактический буфер все время, так как это просто означает много копирования каждый раз, когда добавляется новый вектор. Я надеялся на крутой способ управлять ими прямо в буфере. - person kvanbere; 11.02.2013
comment
Хорошо, сейчас я понимаю. Вы можете запретить std::vector выделять память в куче, сообщив ему, как/где выделить, то есть предоставив ему Распределитель. Признаюсь, я никогда этого не пробовал, но, судя по описанию, можно добиться желаемого результата, если реализация вашего аллокатора вернет указатель на ваш буфер вместо вызова нового. - person Aurelien; 11.02.2013
comment
Ах! Это то, что я ищу, спасибо! Я попробую. - person kvanbere; 11.02.2013
comment
Если у вас получится, напишите, пожалуйста, ваше решение, мне тоже интересно. Спасибо. - person Aurelien; 11.02.2013