Вектор, который может содержать любой POD - PullRequest
0 голосов
/ 16 марта 2012

Есть ли какой-то вектор, который может содержать любой 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), - это большие издержки при использовании линейного хранения, что я и ищу.

Ответы [ 7 ]

3 голосов
/ 16 марта 2012

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

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

3 голосов
/ 16 марта 2012
std::vector<boost::variant<int,somestruct,...>> v;

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

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

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

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

Как сказал Йоханнес, boost::variant влияет на размер каждого элемента, но, как сказал Джеймс КанзеТем не менее, поддержание вектора смещения очень похоже на 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);
 }
};

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

0 голосов
/ 17 марта 2012
  • 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 тин.

0 голосов
/ 16 марта 2012

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

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

По сути, это реализация BLOB-объекта сериализации с возможностью смещения доступа к отдельным объектам, сериализованным в BLOB-объект.

Когда вы хотите отодвинуть объект назад, вы

  1. сериализовать его в конец хранилища BLOB-объектов
  2. добавить смещение (переднюю часть блоба вашего нового объекта) в offsetMap

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

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

Обе эти операции имеют порядок O (sizeof (объект)), но фактически постоянны.

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

0 голосов
/ 16 марта 2012

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

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

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

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

0 голосов
/ 16 марта 2012

вы можете попробовать что-то вроде комбинации вектора и пары.

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

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

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

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

0 голосов
/ 16 марта 2012

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

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

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