Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Type-Safe C++ wrapper for memcpy?

Tags:

c++

memcpy

Given that std::copy (for Trivial Types obviously) can only be implemented as a wrapper around memmove(*), I'm wondering:

  • Is there a Standard C++ type-safe wrapper for the times you need memcpy? (I can't count the number of times I forgot to multiply by sizeof.)
  • If there's nothing in the standard, have there been any proposals for this? If not, why not?
  • Are there any specific obstacles in providing a memcpy wrapper that does the sizeof multiplication automatically?

(*): C++ Standard Library implementations (from back MSVC 2005(!) up to modern MSVC2015, libc++ etc.) decay std::copy TriviallyCopyable types to memmove. Bot not to memcpy. Because:

  • std::copy(src_first, src_last, destination_first) defines that:

    The behavior is undefined if d_first is within the range [first, last).

    • Only the beginning of the destination range MUST NOT be within the source range. The destination range is allowed to extend into the source range. That is, d_first can be "to the left" of the source range, and the destination range can extend into the source range.
  • For std::memcpy the definition is that

    If the objects overlap, the behavior is undefined.

    • That is, the full ranges must not overlap: This is what allows memcpy to be the fastest variant, because it can just assume that the memory of source and destination is completely disjoint.
  • For std::memmove, the definition is:

    The objects may overlap: copying takes place as if the characters were copied to a temporary character array and then the characters were copied from the array to dest.

    • That is, the source and destination range may arbitrarily overlap, there is no restriction.

Given this, it is clear that you can use std::memove to implement std::copy for TrivialllyCopyable types, because memmove doesn't impose any restrictions and the dispatch to the correct implementation can be done at compile time via type traits --

but it's hard to implement std::copy in terms of memcpy because (a) the check whether the pointer ranges overlap would have to be done at run time, and (b) even implementing the runtime check for unrelated memory ranges could be quite a mess.

So, this leaves us with

void* memcpy( void* dest, const void* src, std::size_t count );

a function with a less than stellar interface, where you constantly need to multiply the input count of non-char objects with their sizeof and that is totally untyped.

But memcpy is fastest (and by quite a margin, measure it yourself), and when you need fast copies of TriviallyCopyable types, you reach for memcpy. Which superficially should be easy to wrap in a type safe wrapper like:

template<typename T>
T* trivial_copy(T* dest, T* src, std::size_t n) {
    return static_cast<T*>(std::memcpy(dest, src, sizeof(T) * n));
}

but then, it's unclear wether you should do compile time checks via std::is_trival or somesuch and of course there may be some discussion whether to go with the exact memcpy signature order, yadda yadda.

So do I really have to reinvent this wheel myself? Was it discussed for the standard? Etc.


like image 744
Martin Ba Avatar asked Nov 08 '22 00:11

Martin Ba


1 Answers

To clarify the difference between mencpy and memove, according to the docs memmove can copy memory to a location that overlaps the source memory, for memcpy this is undefined behavior.

"The objects may overlap: copying takes place as if the characters were copied to a temporary character array and then the characters were copied from the array to dest."

Is there a Standard C++ type-safe wrapper for the times you need memcpy? (I can't count the number of times I forgot to multiply by sizeof.)

Yes, std::copy (maybe, explained below)

If there's nothing in the standard, have there been any proposals for this? If not, why not?

As far as i know the standard does not enforce the usage of memmove/memcpy for std::copy for trivial types. So it's up the implementation. For example in visual studio update 2015 update 2 they did use memmove to speed things up:

"Increased the speed of std::vector reallocation and std::copy(); they are up to 9x faster as they call memmove() for trivially copyable types (including user-defined types)."

Are there any specific obstacles in providing a memcpy wrapper that does the sizeof multiplication automatically?

No, in fact you can implement this yourself by using std::is_trivial

Edit:

According to this document section 25.3.1 there are no restrictions to std::copy implementation only complexity:

Complexity: Exactly last - first assignments.

And this makes perfect sense when you consider that memcpy uses cpu speciffic instruction (that are not available on all cpus) to speed up memory copy.

like image 171
Raxvan Avatar answered Nov 14 '22 21:11

Raxvan