Best interface for memcpy() (and the string.h family of functions)

Jonathan Marler johnnymarler at gmail.com
Wed May 29 15:41:42 UTC 2019


On Wednesday, 29 May 2019 at 11:46:28 UTC, Stefanos Baziotis 
wrote:
> I'm a GSoC student (I'll post this week an update) in
> the project "Independency of D from the C Standard Library".
> Part of this project is a D implementation of the family of 
> functions
> memcpy(), memset() etc.
>
> What do you think is the best interface for say memcpy()?
>
> My initial pick was void memcpyD(T)(T* dst, const T* src), but 
> it was proposed
> that `ref` instead of pointers might be better.
>
> Thanks,
> Stefanos

The default memcpy signature is still pretty useful in many 
cases.  The original signature should still be implemented and 
available as a non-template function:

void memcpy(void* dst, void* src, size_t length);

For D, you should also create a template so developer's don't 
have to cast to `void*` all the time, but it just forwards all 
calls to the real memcpy function like this:

void memcpy(T,U)(T* dst, U* src, size_t length)
{
     pragma(inline, true);
     memcpy(cast(void*)dst, cast(void*)src, length);
}

And there's no need to have a different name like `memcpyD`. The 
function behaves the same as libc's memcpy, and when you have 
libc available, you should use that implementation instead so you 
can leverages other people's work when you can.

However, we also want to get type-safety and bounds-checking when 
when can.  So we should also provide a set of templates that 
accept D arrays, verifies type-safety and bounds checking, then 
forwards the call to memcpy.

/**
acopy - Array Copy
*/
void acopy(T,U)(T dst, U src) @trusted
if (isArrayLike!T && isArrayLike!U && dst[0].sizeof == 
src[0].sizeof)
in { assert(dst.length >= src.length, "copyFrom source length 
larger than destination"); } do
{
     pragma(inline, true);
     static assert (!__traits(isStaticArray, T), "acopy doest not 
accept static arrays since they are passed by value");

     import whereever_memcpy_is: memcpy;
     memcpy(dst.ptr, src.ptr, src.length * ElementSizeForCopy!dst);
}
/// ditto
void acopy(T,U)(T dst, U src) @system
if (isArrayLike!T && isPointerLike!U && dst[0].sizeof == 
src[0].sizeof)
{
     pragma(inline, true);
     static assert (!__traits(isStaticArray, T), "acopy doest not 
accept static arrays since they are passed by value");

     import whereever_memcpy_is: memcpy;
     memcpy(dst.ptr, src, dst.length * ElementSizeForCopy!dst);
}
/// ditto
void acopy(T,U)(T dst, U src) @system
if (isPointerLike!T && isArrayLike!U && dst[0].sizeof == 
src[0].sizeof)
{
     pragma(inline, true);

     import whereever_memcpy_is: memcpy;
     memcpy(dst, src.ptr, src.length * ElementSizeForCopy!dst);
}
/// ditto
void acopy(T,U)(T dst, U src, size_t size) @system
if (isPointerLike!T && isPointerLike!U && dst[0].sizeof == 
src[0].sizeof)
{
     pragma(inline, true);
     import whereever_memcpy_is: memcpy;
     memcpy(dst, src, size * ElementSizeForCopy!dst);
}


Note that the isArrayLike and isPointerLike and 
ElementSizeForCopy would probably look something like:


template isArrayLike(T)
{
     enum isArrayLike =
            is(typeof(T.init.length))
         && is(typeof(T.init.ptr))
         && is(typeof(T.init[0]));
}
template isPointerLike(T)
{
     enum isPointerLike =
            T.sizeof == (void*).sizeof
         && is(typeof(T.init[0]));
}

// The size of each array element.  If the actual size is 0, then 
it
// is assumed to be 1.
template ElementSizeForCopy(alias Array)
{
     static if (Array[0].sizeof == 0)
         enum ElementSizeForCopy = 1;
     else
         enum ElementSizeForCopy = Array[0].sizeof;
}

Note that everything here is an inline-template, so everything 
gets reduced to a single memcpy call and some bounds checks.




More information about the Digitalmars-d mailing list