Which one of the following would be the most efficient and why? I'm leaning towards the unique_ptr because I think that there is no copy being done when returning the data read. It's just a transfer of ownership of a pointer.
For example, I think that the string version would create a temporary string, read data into that, and upon returning, it'd copy its data into the assigned result?
However, I'm not sure that I'm right. Any ideas what I mean and which is the best?
UniquePtr:
std::unique_ptr<const char[]> ReadFile(const char* FileName)
{
std::fstream file(FileName, std::ios::in);
if (file.is_open())
{
file.seekg(0, std::ios::end);
std::size_t size = file.tellg();
std::unique_ptr<char[]> result(new char[size]);
file.seekg(0, std::ios::beg);
file.read(result.get(), size);
file.close();
return std::move(result);
}
return nullptr;
}
Vector:
std::string ReadFile(const char* FileName)
{
std::fstream file(FileName, std::ios::in);
if (file.is_open())
{
file.seekg(0, std::ios::end);
std::vector<std::int8_t> buffer(file.tellg());
file.seekg(0, std::ios::beg);
file.read(result.data(), result.size());
file.close();
return std::string(result.data());
}
return std::string();
}
String:
std::string ReadFile(const char* FileName)
{
std::fstream file(FileName, std::ios::in);
if (file.is_open())
{
std::string result = std::string();
file.seekg(0, std::ios::end);
result.resize(file.tellg());
file.seekg(0, std::ios::beg);
file.read(&result[0], result.size());
file.close();
return result;
}
return std::string();
}
std::string ReadFile(const char* FileName)
{
std::fstream file(FileName, std::ios::in);
std::string result = std::string();
if (file.is_open())
{
file.seekg(0, std::ios::end);
result.resize(file.tellg());
file.seekg(0, std::ios::beg);
file.read(&result[0], result.size());
file.close();
}
return result;
}
I have no proof , but if function has only one return , copy elision might be implemented by compiler , if there are 2 returns inside function ,
copy elision might not work as expected ~~~
The
return std::string(result.data());
will only work if the data is null-terminated.
Apart from that complication it needlessly copies from a vector to a string.
The std::string code is most natural for reading text (text mode open of file).
You won't see much of a performance difference since the file i/o dwarfs the rest, but anyway with C++03 you will most likely get Return Value Optimization (depends on compiler), and with C++11 you will get move optimization of the result if you just use std::move.
Related
The function is to extract the file contents into a vector as below:
std::vector<uint8_t> GetFileContents(const std::string& filename)
{
std::ifstream file(filename, std::ios::binary);
if (!file.good())
{
return {};
}
file.seekg(0, std::ios::end);
std::streampos fileSize = file.tellg();
file.seekg(0, std::ios::beg);
std::vector<uint8_t> fileData(fileSize);
file.read((char*) &fileData[0], fileSize);
return fileData;
}
On the lines:
std::vector<uint8_t> fileData(fileSize);
file.read((char*) &fileData[0], fileSize);
return fileData;
The fileData vector is copied into a temporary vector to be returned here?
Do I need to change to:
std::vector<uint8_t> fileData(fileSize);
file.read((char*) &fileData[0], fileSize);
return std::move(fileData);
to do a move and save a vector copy operation?
Any other changes required in the function to support move?
Is there any value in std::move({}) for the empty case?
No, you shouldn't use std::move there.
That avoids possible NRVO, and there is an implicit move anyway.
See Automatic_move_from_local_variables_and_parameters for further detail.
I am trying to write a char* to a binary file.
This is what I have now.
void Write(char* fileName, char* pData)
{
ofstream binFile (fileName, ios::out | ios::binary);
if (binFile.open())
{
binFile.write((char*)&pData, sizeof(pData));
binFile.close();
}
}
void Read(char* fileName, char* pData)
{
ifstream binFile(fileName, ios::in | ios::binary);
if(binFile.open())
{
binFile.read(char*)&pData, sizeof(pData));
binFile.close
}
}
int main()
{
char* testData = "ABCdEFG"; // not real data
char* getTestData;
char* file = "C:\\testData.dat";
Write(file, testData);
Read(file, getTestData);
}
Test data will be of unknown length. May not always be the same.
When i run the program once, and write and read. I can get back the test data.
But when i stop the program and run it again, this time without writing. Just reading, i cannot get back the test data.
I don't really understand whats happening here.
Can some one explain it to me?
binFile.write((char*)&pData, sizeof(pData));
is wrong. It just writes the value of the pointer. It does not write the data.
You need to use:
binFile.write(pData, strlen(pData));
However, that won't be adequate to read the data back. To be able to read the data back, you'll need to write the size of the string first.
size_t len = strlen(pData);
binFile.write((char*)&len, sizeof(len));
binFile.write(pData, len);
And when reading the data back, you will need to use:
size_t len = 0;
binFile.read(char*)&len, sizeof(len));
binFile.read(pData, len);
and then, null terminate the string.
pData[len] = '\0';
PS
Make sure getTestData is properly initialized before using it to read the data.
char getTestData[100];
will be adequate for your test case.
Update
You can make your program a bit better by using std::string instead of char*. The size of the saved data can be more easily managed when a std::string is used.
void Write(std::string const& fileName, std::string const& data)
{
std::ofstream binFile(fileName, std::ios::out | std::ios::binary);
if (binFile.is_open())
{
size_t len = data.size();
binFile.write((char*)&len, sizeof(len));
binFile.write((char*)&data[0], len);
// No need. The file will be closed when the function returns.
// binFile.close();
}
}
void Read(std::string const& fileName, std::string& data)
{
std::ifstream binFile(fileName, std::ios::in | std::ios::binary);
if(binFile.is_open())
{
size_t len = 0;
binFile.read((char*)&len, sizeof(len));
data.resize(len);
binFile.read((char*)&data[0], len);
}
}
int main()
{
std::string file = "testData.dat";
std::string testData = "ABCdEFG";
Write(file, testData);
std::string getTestData;
Read(file, getTestData);
std::cout << getTestData << std::endl;
}
Trying to understand the cause of the performance difference.
I'm reading a ~70M file with the function below.
Running the code with:
gcc 4.4.6 takes less than a second
gcc 3.2.3 takes more than 6 minutes
Most of the time is spend in the assign part.
What was changed to make this speed difference between the 2 compilers ?
bool ReadFile(const string& path, string& file_data)
{
ifstream ifs(path.c_str(), ifstream::in | ifstream::ate);
if (!ifs) return false;
int size = ifs.tellg();
if (size==0) return false;
ifs.seekg(0, ios::beg);
file_data.assign((istreambuf_iterator<char>(ifs)),
istreambuf_iterator<char>());
return true;
}
Could you try tweak a bit this code (one extra line):
bool ReadFile(const string& path, string& file_data)
{
ifstream ifs(path.c_str(), ifstream::in | ifstream::ate);
if (!ifs) return false;
int size = ifs.tellg();
if (size==0) return false;
ifs.seekg(0, ios::beg);
file_data.reserve(size);
file_data.assign((istreambuf_iterator<char>(ifs)),
istreambuf_iterator<char>());
return true;
}
and do measurement again
In second attempt you can try this:
bool ReadFile(const string& path, string& file_data)
{
ifstream ifs(path.c_str(), ifstream::in | ifstream::ate);
if (!ifs) return false;
int size = ifs.tellg();
if (size==0) return false;
ifs.seekg(0, ios::beg);
file_data.resize(size);
return ifs.read(&file_data[0], size);
}
When i try to create my shader with CreateVertexShader(), I get COM error 'The paremeter is incorrect.'.
struct ShaderData
{
LPVOID ShaderCode;
size_t ShaderSize;
};
This function works but it leaks. When delete[] buffer is uncommented I get the error.
Shader::ShaderData Shader::LoadShader(const std::string& file)
{
std::ifstream ifs(file, std::ios::in | std::ios::binary);
if (!ifs) return;
ifs.seekg(0, std::ios::end);
size_t size = static_cast<std::size_t>(ifs.tellg());
ifs.seekg(0, std::ios::beg);
char* buffer = new char[size];
ifs.read(buffer, len);
ShaderData data = { buffer, size };
//delete[] buffer;
return data;
}
Another version of the same function but using std::vector, I still get the same error.
Shader::ShaderData Shader::LoadShader(const std::string& file)
{
std::ifstream ifs(file, std::ios::in | std::ios::binary);
if (!ifs) return;
ifs.seekg(0, std::ios::end);
size_t size = static_cast<std::size_t>(ifs.tellg());
ifs.seekg(0, std::ios::beg);
std::vector<char> buffer(size);
ifs.read(buffer.data(), size);
ShaderData data = { static_cast<void*>(buffer.data()), size };
return data;
}
Function creating the shaders. m_vertex_data and m_pixel_data are both member variables of class Shader, and holds the data returned from LoadShader().
void Shader::CreateShaders(ID3D11Device* device)
{
device->CreateVertexShader(m_vertex_data.ShaderCode,
m_vertex_data.ShaderSize,
nullptr,
m_vertex_shader.GetAddressOf()));
device->CreatePixelShader(m_pixel_data.ShaderCode,
m_pixel_data.ShaderSize,
nullptr,
m_pixel_shader.GetAddressOf()));
}
You need to keep the buffer pointed to by ShaderData::ShaderCode; valid until you create a shader. To avoid leaks just replace it with ::std::vector< char > ShaderCode; so the buffer will be kept safe. Actually you don't need ShaderData struct at all, just return a vector containing shader code.
I am trying to write a char* to a binary file.
This is what I have now.
void Write(char* fileName, char* pData)
{
ofstream binFile (fileName, ios::out | ios::binary);
if (binFile.open())
{
binFile.write((char*)&pData, sizeof(pData));
binFile.close();
}
}
void Read(char* fileName, char* pData)
{
ifstream binFile(fileName, ios::in | ios::binary);
if(binFile.open())
{
binFile.read(char*)&pData, sizeof(pData));
binFile.close
}
}
int main()
{
char* testData = "ABCdEFG"; // not real data
char* getTestData;
char* file = "C:\\testData.dat";
Write(file, testData);
Read(file, getTestData);
}
Test data will be of unknown length. May not always be the same.
When i run the program once, and write and read. I can get back the test data.
But when i stop the program and run it again, this time without writing. Just reading, i cannot get back the test data.
I don't really understand whats happening here.
Can some one explain it to me?
binFile.write((char*)&pData, sizeof(pData));
is wrong. It just writes the value of the pointer. It does not write the data.
You need to use:
binFile.write(pData, strlen(pData));
However, that won't be adequate to read the data back. To be able to read the data back, you'll need to write the size of the string first.
size_t len = strlen(pData);
binFile.write((char*)&len, sizeof(len));
binFile.write(pData, len);
And when reading the data back, you will need to use:
size_t len = 0;
binFile.read(char*)&len, sizeof(len));
binFile.read(pData, len);
and then, null terminate the string.
pData[len] = '\0';
PS
Make sure getTestData is properly initialized before using it to read the data.
char getTestData[100];
will be adequate for your test case.
Update
You can make your program a bit better by using std::string instead of char*. The size of the saved data can be more easily managed when a std::string is used.
void Write(std::string const& fileName, std::string const& data)
{
std::ofstream binFile(fileName, std::ios::out | std::ios::binary);
if (binFile.is_open())
{
size_t len = data.size();
binFile.write((char*)&len, sizeof(len));
binFile.write((char*)&data[0], len);
// No need. The file will be closed when the function returns.
// binFile.close();
}
}
void Read(std::string const& fileName, std::string& data)
{
std::ifstream binFile(fileName, std::ios::in | std::ios::binary);
if(binFile.is_open())
{
size_t len = 0;
binFile.read((char*)&len, sizeof(len));
data.resize(len);
binFile.read((char*)&data[0], len);
}
}
int main()
{
std::string file = "testData.dat";
std::string testData = "ABCdEFG";
Write(file, testData);
std::string getTestData;
Read(file, getTestData);
std::cout << getTestData << std::endl;
}