Есть ли контейнер STL, который хранит массив элементов в непрерывной памяти, где размер элемента указан во время выполнения? - PullRequest
2 голосов
/ 31 августа 2011

Я пытаюсь создать контейнер, который выглядит близко к тому, как работает моя спецификация файла. Это как вектор, но тип элементов определяется хеш-таблицей.

Если бы я знал тип во время компиляции, я мог бы просто написать что-то вроде этого:

struct foo {
              float a,b,c;
              int d;
              byte e,f;
           };

std::vector<foo> foovector;
foovector.push_back(foo f);

У меня нет структуры во время компиляции. У меня есть только схема, которую я получаю из заголовка файла. Все элементы имеют одинаковый размер и имеют одинаковые смещения для каждого элемента внутри элемента. Контейнер имеет хеш-таблицу, определенную до добавления каких-либо элементов.

typedef Toffset uint; //byte offset;
typedef Ttype   uint; //enum of types

std::unordered_map<std::string, std::pair<Toffset,Ttype>> itemKey; 
itemKey["a"] = 0;
itemKey["b"] = 4;
itemKey["c"] = 8;
itemKey["d"] = 12;
itemKey["e"] = 16;
itemKey["f"] = 17;

nstd::interleaved_vector superfoo(itemKey, 10); //hashtable, pre-allocation size

nstd::interleaved_vector::iterator myIterator;

myIteratorGlobal = superfoo.begin;
myIteratorA = superfoo["a"].begin;
myIteratorB = superfoo["b"].begin;

*myIteratorB = 2.0f;
*myIteratorGlobal["d"] = 512;

Идея состоит в том, чтобы я мог быстро и легко извлекать необработанные данные из файлов. Смещать итераторы легко. Мои вопросы:

  1. Что-нибудь уже делает это?

  2. Это плохая идея? Должен ли я просто создать вектор и обновить каждый элемент? Я ожидаю иметь миллионы элементов. Диапазон размеров foo будет от 20 до 200 байт.

  3. Это плохая идея? Вместо этого я должен создать связанные векторы, по одному для каждого элемента?

  4. Или этот interleaved_vector является хорошим решением моей проблемы?

Ответы [ 3 ]

4 голосов
/ 31 августа 2011

Есть ли контейнер STL, который хранит массив элементов в непрерывной памяти, где размер элемента указан во время выполнения?

Нет.

Что вызапрос выглядит как конкретная реализация пула памяти.Может быть, вам пригодится библиотека Boost.Pool или другие реализации?Написание собственного не должно быть трудным, если вы привыкли работать с необработанной памятью и C ++ - специфическим созданием / уничтожением объектов.

Чтобы ответить на ваши вопросы:

что-нибудь уже делает это?

Для меня идея выглядит как пул памяти.Существует множество типов пулов памяти, поэтому реализация, которую вы хотите, полностью зависит от ваших конкретных потребностей.

Это плохая идея?Должен ли я просто создать вектор и обновить каждый элемент?Я ожидаю иметь миллионы элементов.Диапазон размеров foo составляет от 20 до 200 байт.

Неплохая идея, если вы хотите ограничить фрагментацию памяти.Пулы часто используются для исправления этой и других проблем, связанных с организацией памяти.

Например, в видеоиграх очень часто это делается, в основном на консоли, а также на ПК, если вам нужна высокая производительность или многопамять.

Я просто не рекомендовал бы беспокоиться, если вы делаете прототип или если у вас нет тонны данных для выделения.Если вы это сделаете, то, возможно, сначала было бы неплохо реализовать простейшее распределение (с векторами и новыми), скрытое за фабрикой, и оно позволило бы заменить реализацию фабрики с помощью пула.Таким образом, вы сначала проверяете, что все работает, а затем оптимизируете.

1 голос
/ 31 августа 2011

Учитывая этот код:

typedef Toffset uint; //byte offset;
typedef Ttype   uint; //enum of types
typedef std::pair<Toffset,Ttype> member;
typedef std::unordered_map<std::string, member> memberdefs;

memberdefs itemKey; 
itemKey["a"] = member(0, 0);
itemKey["b"] = member(4, 1);
itemKey["c"] = member(8, 2);
itemKey["d"] = member(12,1);
itemKey["e"] = member(16,3);
itemKey["f"] = member(17,2);

Вы можете читать в буфер char * и использовать простой класс-оболочку. Все еще подвержен ошибкам и очень запутанный. Эта демонстрация не имеет итератора (хотя это было бы просто) и требует, чтобы внешний буфер оставался в области действия хотя бы до тех пор, пока класс делает это.

class interleaved_vector {
    const char* buffer;
    size_t count;
    size_t size;
    std::shared_ptr<memberdefs> members;
public: 
    class dynamic_object {
        const char* buffer;
        std::shared_ptr<memberdefs> members;
        friend interleaved_vector;
        dynamic_object(const char* buffer_, std::shared_ptr<memberdefs> members_)
        :buffer(buffer_), members(members_)
        {}
        dynamic_object& operator=(const dynamic_object& b) = delete;
    public:
        dynamic_object(const dynamic_object& b) 
        :buffer(b.buffer), members(b.members)
        {}
        template <class T>
        T get(const std::string& member) const {
            assert((*members)[member].second > 0); //no new members, requires pos sizes
            assert((*members)[member].second == sizeof(T));
            return *reinterpret_cast<T*>(buffer+(*members)[member].first); //technically undefined I think
        };
        template <>
        T* get<T*>(const std::string& member) const {
            assert((*members)[member].second > 0); //no new members, requires pos sizes
            assert((*members)[member].second == sizeof(T));
            return reinterpret_cast<T*>(buffer+(*members)[member].first); //technically undefined I think
        };
        void* operator[](const std::string& member) const {
            assert((*members)[member].second > 0); //no new members, requires pos sizes
            assert((*members)[member].second == sizeof(T));
            return reinterpret_cast<void*>(buffer+(*members)[member].first); //technically undefined I think
        };
    };
    interleaved_vector(const char* buffer_, size_t count_, size_t size_, const memberdefs& members_)
    :buffer(buffer_), count(count_), size(size_), members(members_) 
    {}
    dynamic_object get(size_t index) const { 
        assert(index<count);
        return dynamic_object(buffer + size*index, members);
    }
    dynamic_object operator[](size_t index) const { 
        assert(index<count);
        return dynamic_object(buffer + size*index, members);
    }
    size_t size() {
        return count;
    }
};

Это позволило бы код, такой как:

size_t element_size = 32;
size_t num_elements = 1000000
char * buffer = new char[num_elements*element_size];
/*read into buffer*/
interleaved_vector iv(buffer, num_elements, element_size , members);
/*interleaved_vector DOES NOT COPY BUFFER. BUFFER MUST REMAIN IN SCOPE*/
for(int i=0; i<iv.size(); ++i) {
    for(memberdefs::const_iterator j=members.begin(); j!=members.end(); ++j) {
        dynamic_object ob = iv[i];
        std::cout << "data[" << i << "]." << j.first << " = ";
        std::cout << ob.get<int>(j.first) << '\n';
    }
}

В этом демонстрационном коде предполагается, что все участники являются целыми числами (получить), но, надеюсь, вы сможете увидеть, что предполагается.

0 голосов
/ 31 августа 2011

Вы могли бы написать свой собственный класс, но это было бы серьезной болью. Лучше просто использовать vector (или boost :: ptr_vector), который не требует усилий с вашей стороны и легко читается каждым программистом, который приходит.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...