Поскольку некоторая графика работает, я должен читать в большом объеме данных как можно быстрее и идеально хотел бы непосредственно читать и записал бы структуры данных в диск. В основном у меня есть загрузка 3-х моделей в различных форматах файлов, которые занимают слишком много времени загружаться так, я хочу выписать им в их "подготовленном" формате как кэш, который загрузится намного быстрее на последующих выполнениях программы.
Действительно ли безопасно сделать это как это? Мои заботы вокруг прямого чтения в данные вектора? Я удалил проверку ошибок, трудно кодировали 4 как размер интервала и так далее так, чтобы я мог дать короткий рабочий пример, я знаю, что это - плохой код, мой вопрос действительно состоит в том, если безопасно в C++ считать целый массив структур непосредственно в вектор как это? Я полагаю, что это так, но C++ имеет столько прерываний и неопределенного behavour, когда Вы начинаете идти низкий уровень и иметь дело непосредственно с необработанной памятью как это.
Я понимаю, что числовые форматы и размеры могут измениться через платформы и компиляторы, но это будет только даже считано и записано той же программой компилятора в данные кэша, которые могут быть необходимы на более позднем выполнении той же программы.
#include <fstream>
#include <vector>
using namespace std;
struct Vertex
{
float x, y, z;
};
typedef vector<Vertex> VertexList;
int main()
{
// Create a list for testing
VertexList list;
Vertex v1 = {1.0f, 2.0f, 3.0f}; list.push_back(v1);
Vertex v2 = {2.0f, 100.0f, 3.0f}; list.push_back(v2);
Vertex v3 = {3.0f, 200.0f, 3.0f}; list.push_back(v3);
Vertex v4 = {4.0f, 300.0f, 3.0f}; list.push_back(v4);
// Write out a list to a disk file
ofstream os ("data.dat", ios::binary);
int size1 = list.size();
os.write((const char*)&size1, 4);
os.write((const char*)&list[0], size1 * sizeof(Vertex));
os.close();
// Read it back in
VertexList list2;
ifstream is("data.dat", ios::binary);
int size2;
is.read((char*)&size2, 4);
list2.resize(size2);
// Is it safe to read a whole array of structures directly into the vector?
is.read((char*)&list2[0], size2 * sizeof(Vertex));
}
Как говорит Лауринас, std :: vector
гарантированно будет смежным, так что это должно работать, но потенциально не переносимо.
В большинстве систем sizeof (Vertex)
будет 12, но нередко структура дополняется, так что sizeof (Vertex) == 16
. Если вы должны были записать данные в одной системе, а затем прочитать этот файл в другой, нет гарантии, что он будет работать правильно.
Другой альтернативой явному чтению и записи вашего vector <>
из и в файл является замена основного распределителя на тот, который выделяет память из файла с отображением памяти. Это позволит вам избежать промежуточного копирования, связанного с чтением / записью. Однако у этого подхода есть некоторые накладные расходы. Если ваш файл не очень большой, это может не иметь смысла в вашем конкретном случае. Профилируйте как обычно, чтобы определить, подходит ли этот подход.
К этому подходу также есть некоторые предостережения, которые очень хорошо обрабатываются библиотекой Boost.Interprocess . Особый интерес для вас могут быть его распределители и контейнеры .
std :: vector
гарантированно будет непрерывным в памяти, так что да.
Если это используется для кэширования тем же кодом, я не вижу в этом никаких проблем. Я без проблем использовал эту технику на нескольких системах (все на базе Unix). В качестве дополнительной меры предосторожности вы можете захотеть написать структуру с известными значениями в начале файла и проверить, правильно ли она читается. Вы также можете записать размер структуры в файл. Это сэкономит много времени на отладку в будущем, если заполнение когда-либо изменится.
Возможно, вас заинтересует библиотека Boost.Serialization . Помимо прочего, он знает, как сохранять / загружать контейнеры STL на / с диска. Это может быть излишним для вашего простого примера, но может стать более полезным, если вы выполните другие типы сериализации в своей программе.
Вот пример кода, который делает то, что вы ищете:
#include <algorithm>
#include <fstream>
#include <vector>
#include <boost/archive/binary_oarchive.hpp>
#include <boost/archive/binary_iarchive.hpp>
#include <boost/serialization/vector.hpp>
using namespace std;
struct Vertex
{
float x, y, z;
};
bool operator==(const Vertex& lhs, const Vertex& rhs)
{
return lhs.x==rhs.x && lhs.y==rhs.y && lhs.z==rhs.z;
}
namespace boost { namespace serialization {
template<class Archive>
void serialize(Archive & ar, Vertex& v, const unsigned int version)
{
ar & v.x; ar & v.y; ar & v.z;
}
} }
typedef vector<Vertex> VertexList;
int main()
{
// Create a list for testing
const Vertex v[] = {
{1.0f, 2.0f, 3.0f},
{2.0f, 100.0f, 3.0f},
{3.0f, 200.0f, 3.0f},
{4.0f, 300.0f, 3.0f}
};
VertexList list(v, v + (sizeof(v) / sizeof(v[0])));
// Write out a list to a disk file
{
ofstream os("data.dat", ios::binary);
boost::archive::binary_oarchive oar(os);
oar << list;
}
// Read it back in
VertexList list2;
{
ifstream is("data.dat", ios::binary);
boost::archive::binary_iarchive iar(is);
iar >> list2;
}
// Check if vertex lists are equal
assert(list == list2);
return 0;
}
Обратите внимание, что мне пришлось реализовать функцию сериализации
для вашей Vertex
в повышении : : пространство имен serialization
. Это позволяет библиотеке сериализации знать, как сериализовать элементы Vertex
.
Я просмотрел исходный код boost :: binary_oarchive
, и мне показалось, что он считывает / записывает необработанные данные векторного массива непосредственно из / в буфер потока. Так что это должно быть довольно быстро.