francesco
francesco

Reputation: 7539

std::memcpy vs std::copy_n for legacy c structs

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

Answers (2)

M.M
M.M

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

ALX23z
ALX23z

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

Related Questions