2012-03-16 2 views
0

Есть ли какой-нибудь вектор, который может содержать любые POD? что-то вроде:Вектор, который может содержать любые POD

anyvector v; 
v.push_back<int>(1); 
v.push_back<somestruct>({1, 2, 3}); 

и доступ к нему с:

int a = v.get<int>(0); 
somestruct b = v.get<somestruct>(1); 

Я знаю, что должно быть наверху, чтобы сохранить смещения каждого элемента, но оно должно быть меньше накладных расходов, vector<boost::any> это мой текущее решение.
Операции, в которых я нуждаюсь, - это вставка в конец, удаление из конечного и произвольного доступа.
Я знаю, что это не проблема для его реализации, просто спрашивая, есть ли готовый.

Редактировать: Решение, которое использует указатели для хранения данных (boost::any, boost::variant), является большим накладным расходами с использованием линейного хранилища, которое я ищу.

+4

Оставьте ваше внимание на BOOST [Any] (http://www.boost.org/doc/libs/1_49_0/doc/html/any.html) и [Вариант] (http://www.boost.org /doc/libs/1_49_0/doc/html/variant.html) – megabyte1024

+0

@ megabyte1024: как я уже сказал, это мое текущее решение, но каждый из них несет указатель, который является огромным накладным потоком от линейного хранилища. – Dani

+0

Или QVariant, если вы используете Qt. Сначала сконцентрируйтесь на создании переменной Any-Type-Variable (Any, Variant, QVariant), а затем поместив ее в вектор, просто используйте std :: vector для этого типа. – Patrick

ответ

3
std::vector<boost::variant<int,somestruct,...>> v; 

v.push_back(1); 
v.push_back(({1, 2, 3}); 

a = boost::get<int>(v[0]); 

Если вы знаете, какие типы должны обрабатываться при декларировании.

- редактировать

Как JohannesD сказал boost::variant влияет на размер каждого элемента, но как Джеймс Kanze сказал, поддерживая вектор смещения очень похож на boost::any, и я не уверен, что вы действительно можете быть более компактным ,

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

template <template <class,class> class C, typename T, typename ...Args> 
struct map_ { //FIXME: return a list of type C<T1>,C<T2>... 
}; 

template <template <class...> class C, typename ...Args> 
struct fill_ { // Hack to parametrize map by Args on GCC 4.6 
typedef T<Args> type; 
}; 

template <typename... Ts> 
struct hvec 
{ 
std::map<std::string,fill_<boost::variant,map_<std::vector,Ts>> vec_map; 
std::size_t last_id; 
std::string last_typeid; 

template <typename T> 
T get(std::size_t i) 
{ 
    std::vector<T>& v = vec_map[typeid(T).name]; 
    return vec[i]; 
} 

template <typename T> 
std::size_t push_back(const T& e) 
{ 
    std::vector<T>& v = vec_map[typeid(T).name]; 
    v.push_back(e); 
} 
}; 

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

+0

, как я уже сказал, это похоже на мое текущее решение (boost: any), но каждый несет указатель, который является огромным накладным потоком от линейного хранилища. – Dani

+0

@ Дани. Вы понесете эту накладную, несмотря ни на что, см. Ответ Стива. Я не думаю, что другое решение может быть существенно более эффективным, чем «boost :: any» или «boost :: variant». –

+0

@ Konrad: мой ответ был фактически неправильным. Я сосредоточился на «своего рода векторе», когда мне следовало сосредоточиться на действительных необходимых операциях. Случайный доступ несет накладные расходы, но у вас есть два варианта. Вы можете выделить данные вне вектора и указать указатели фиксированного размера (например, boost :: any), или вы можете поместить элементы в конец большого массива и провести отдельный индекс отображения массива для смещения в данные. Последнее может быть меньше накладных расходов, чем отдельное распределение, полагаясь на то, что удаление происходит только в конце, которое обычный сервер распределения памяти не может принять. –

0

Значит, вам не нужны указатели: это означает, что вам нужен тип элемента, достаточно большой, чтобы напрямую содержать любые POD. Учитывая, что объекты POD могут иметь произвольный размер, это, очевидно, невозможно.

Если у вас есть набор типов кандидатов, кажется, что вы можете иметь ограниченный размер дискриминации, но разве это не то, что делает Boost.Variant? Документация указывает, что там, где это возможно, будет стековый (предположительно встроенный). Как вы измерили это «большие накладные расходы»?

+0

Я не хочу объединение, я хочу вектор, который может содержать элементы произвольного размера. – Dani

+0

Если размер элемента не является фиксированным, этот контейнер не будет работать как в любом значащем смысле _be_, ни в векторе, ни в массиве. Как бы он вычислил смещение для индекса? – Useless

+0

Я думаю, он хочет O (n) искать время. Или, может быть, O (n) дополнительная память для хранения смещений. – bames53

0

вы могли бы попробовать soemthing как комбинации вектора и пары

enum ElementType : unsigned int 
{ 
    ET_INT, 
    ET_WHATEVER 
}; 

vector < pair < void*, unsigned int >* > v; 
pair < void*, unsigned int > prv; 
int i; 

prv.first = &i; 
prv.second = ET_INT; 

v.push_back(&prv); 

это будет ваш выбор для хранения объектов по ссылке или по значению. доступ к элементам singloe будет так:

int a = *(int*) v.at(0)->first 

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

надеюсь, что это помогает;)

3

Я никогда не слышал об одном, и я был бы удивлен, если таковой существует, учитывая то, как очень особенный это. Однако не должно быть слишком сложно реализовать, но с использованием двух векторов, std::vector<unsigned char> для необработанной памяти , где вы помещаете объекты, и std::vector<size_t> для отображения индексов. Пока он содержит только POD, вы можете использовать memcpy для вставки элементов . Только не забудьте уважать выравнивание. И что индекс массив отображает индексы: не пытайтесь вставлять в него указатели, так как они будут недействительными позже push_back.

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

+0

Главной проблемой, которую я ожидаю, является необходимость вызова destructirs, whethet для POD или нет: созданные объекты должны быть уничтожены, хотя это часто не делается для POD. Получив это право, я бы подумал, что вы эффективно реализовали 'std :: vector '. –

+1

@ DietmarKühl ** ЕСЛИ ** вы можете гарантировать, что объекты имеют тривиальные деструкторы (это большой, если, IMHO), тогда вы можете пропустить деструкторы. Более того: распределитель, используемый «вектором», не является проблемой; то, что вы реализуете, является эквивалентом 'boost :: any', который использует ваш распределитель: в основном у вас есть вектор вашего' any', в котором ваш 'any' использует вектор' unsigned char', поскольку это источник памяти , а не стандартный «новый». (Конечно, он должен был бы поддерживать свой «указатель» как 'size_t', чтобы избежать аннулирования при росте вектора.) –

0

Std :: tuple может хранить последовательность элементов произвольного размера, но только фиксированный набор из них.

Произвольное количество элементов с произвольными типами при запуске звучит с ошибкой и очень опасно. Что вы ожидаете, если будете делать следующее?

anyvector v; 
v.push_back<Foo>(Foo()); 
int i = v.back<int>(); 

Вы бы повезло, если реализация может предоставить вам исключение во время выполнения, и я не думаю, что есть какой-нибудь способ, что реализация anyvector может гарантировать, что. Например, если он попытался использовать RTTI, он столкнулся бы с проблемой, что на самом деле нет гарантии, что два объекта type_info, относящиеся к разным типам, не будут равны.

0

Я думаю, чтобы получить наилучшее приближение к тому, что вы хотите, вам нужно будет создать свою собственную структуру данных. Вы можете основывать свой тип от двух типов реализации:

std::vector<unsigned char> blobStorage_; 
std::vector<size_t> offsetMap_; 

Это эффективна реализация сериализации блоба с возможностью смещения доступа отдельных объектов сериализован в сгусток.

Если вы хотите push_back объект, вы

  1. сериализация его в конце blobStorage
  2. добавить смещение (фронт сгустка вашего нового объекта) в offsetMap

Аналогично, если вам нужен элемент доступа, который вы считаете вектором [n], вы:

  1. получить смещение от offsetMap_ [п]
  2. deserialise объекта в качестве поставляемого типа из blobStorage_ [смещение]

Оба этих операций порядка O (SizeOf (объект)), но фактически постоянная.

Обычно, если вы хотите сделать что-то подобное, вы делаете «фактическую» сериализацию в блобе, где «фактическим» я имею в виду, что вы на самом деле проектируете сериализацию в свою программу. Обычно это означает, что вы используете стандартный заголовок для хранения ваших типов, который дает идентификатор типа и потенциально размер информации. Причина, по которой вы обычно это делаете, заключается в том, что вы теряете информацию о типе при сериализации, а в указанном вами шаблоне использования (где тип указан в get) вы создаете очень хрупкую программу с большим количеством неписаных контрактов , Обычно вы не хотите, чтобы ваши программы легко ломались в профессиональной среде, работая с другими. Таким образом, вы можете сделать его гораздо более надежным, сохранив идентификаторы типов и зарегистрировав типы с помощью своих идентификаторов типов в большей конструкции сериализации. Затем «вектор» будет знать, как десериализовать и представить правильный тип объекта.

0
  • std::vector хранит элементы в виде непрерывного массива
  • В таком массиве каждый элемент имеет начальный адрес sizeof(Elem) мимо предыдущего элемента
  • типа POD может быть сколь угодно большим
  • Там нет никакого способа, чтобы иметь вектор произвольно больших объектов, потому что sizeof(Elem) не ограничен.

Ответ, если вы не можете ограничить себя дальше, просто нет.

Если вы можете установить верхний предел размера, то да, это возможно, без каких-либо накладных расходов. Объекты POD могут использоваться без явной конструкции или убиты без явного уничтожения. Таким образом, вы можете просто создать вектор std::array< char, N > и reinterpret_cast элементов для нужного вам типа.

typedef std::vector< std::array< char, pod_size_max > > pod_variant_vector; 

template< typename destination > 
destination &get(pod_variant_vector &vec, std::size_t index) 
    { return reinterpret_cast< destination & >(vec[ index ]); } 

template< typename destination > 
destination const &get(pod_variant_vector const &vec, std::size_t index) 
    { return reinterpret_cast< destination const & >(vec[ index ]); } 

Убедитесь, что не использовать значение любого такого объекта до его инициализации, используя тот же тип вы get тин.

Смежные вопросы