Reputation: 7539
In dealing with legacy C
code, I need to read and copy the memory location of a C-style struct.
Given a pointer to the struct, should I better use the C-style std::memcpy
or the C++ std::copy_n
? Or are they equivalent?
To have a concrete, albeit trivial, example:
#include <cstring>
#include <algorithm>
struct Ctype {
double x;
int a;
};
int main()
{
Ctype a{1, 2};
auto p = &a;
auto buffer = reinterpret_cast<decltype(p)>(::operator new(sizeof(a)));
// Are the two following equivalent and both well-defined?
std::memcpy(buffer, p, sizeof(a));
std::copy_n(p, 1, buffer);
Ctype b{3, 4};
// Are the two following equivalent and both well-defined?
std::memcpy(&b, buffer, sizeof(b));
std::copy_n(buffer, 1, &b);
delete buffer;
return 0;
}
The question arises in the context of serializing a C struct. In the concrete case I do not know the contents of Ctype
which is defined in a C library.
Some clarifications
The example is perhaps overly simplified, and clearly one could simply use b=*buffer
. What I have in mind, however, is that buffer
could be provided from outside, for example it could be a sequence of data read from the disk. Then I need to actively copy buffer into b.
Addition
This is an example closer to what I have in mind. It uses the zlib
library to write/read from the disk: the zlib functions gzread
and gzwrite
accept a void *
pointer to a buffer of memory to read/write.
#include <cstring>
#include <algorithm>
#include <fstream>
extern "C" {
#include <zlib.h>
}
struct Ctype {
double x;
int a;
};
int main()
{
Ctype a{1, 2};
auto p = &a;
auto buffer = ::operator new(sizeof(a));
// Are the two following equivalent and both well-defined?
std::memcpy(buffer, p, sizeof(a));
std::copy_n(p, 1, static_cast<Ctype *>(buffer));
// Saving the files
gzFile f = gzopen("conf.dat", "w");
gzwrite(f, buffer, sizeof(a));
gzclose(f);
Ctype b{3, 4};
// Reading file
f = gzopen("conf.dat", "r");
gzread(f, buffer, sizeof(a));
gzclose(f);
// Are the two following equivalent and both well-defined?
std::memcpy(&b, buffer, sizeof(b));
std::copy_n(static_cast<Ctype *>(buffer), 1, &b);
operator delete(buffer);
return 0;
}
Upvotes: 1
Views: 1477
Reputation: 141618
The first code is incorrect as you attempt to copy into storage that contains no objects. memcpy and copy_n can only update existing objects, they cannot create objects in vacant storage . An easy fix would be to write write auto buffer = new Ctype;
(although I would suggest using a container for for memory management if you must use dynamic allocation).
Having fixed that, your two methods are both equivalent for a C-style struct (i.e. one whose definition is syntactically valid in C). These are a subset of trivially-copyable types in C++. So it is a matter of style as to which to use; my preference would be to write:
b = *buffer;
which seems clearer than a function call syntax . A problem with the memcpy version is that it would not be robust in the case of future code changing the struct to no longer be trivially copyable.
Upvotes: 1
Reputation: 4713
std::copy_n
is a template function that performs copy operations. For trivially copyable types (C style structs should all be trivially copyable) it is the same as memcpy
- and should have the same performance.
Also, memcpy
is not suitable for non-copyable types unlike std::copy_n
.
Upvotes: 2