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

Jonathan Marler johnnymarler at gmail.com
Wed May 29 17:45:59 UTC 2019


On Wednesday, 29 May 2019 at 17:35:03 UTC, Stefanos Baziotis 
wrote:
> On Wednesday, 29 May 2019 at 15:41:42 UTC, Jonathan Marler 
> wrote:
>>
>> 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.
>>
>
> I'm not sure about that. Does it really make sense to have such 
> an
> interface in the case where you don't have libc memcpy 
> available?

Sure.  Any time you have a buffer whose type isn't known at 
compile-time and you need to copy between them.  For example, I 
have an audio program that copies buffers of audio, but the 
format of that buffer could be an array of floats or integers 
depending on the format that your audio hardware and OS support.

> Although, there is a discussion about such fallback functions. 
> But I don't
> know, I feel like it will encourage bad practices.
>
> In the same way, I don't know about whether it should accept 
> two different types.

Well that's why you have memcpy (for those who know what they're 
doing) and you have other functions for safe behavior.  But you 
don't want to instantiate a new version of memcpy for every type 
variation, that's why they all just forward the call to the real 
memcpy.

>
>> 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.
>>
>
> Those are good ideas. But I think all this could be done 
> explicitly with
> (ref T[] dst, ref T[] source). This makes a specific-to-arrays 
> version,
> which again I'm unsure if it is good to make specific cases.
>

Yes it could be done, but then you end up with N copies of your 
memcpy implementation, one for every combination of types.  
You're code size is going to explode.  You can certainly support 
the signature you provided, I just wouldn't have the 
implementation inside of that template, instead you should cast 
and forward to memcpy.

> The thing with all this code depending on libc memcpy is that 
> to my understanding,
> the prospect is that libc will be removed. And this project is 
> a step towards that
> by making some better D versions (meaning, leveraging D 
> features).

Right, which is why you use the libc version by default, and only 
use your own when libc is disabled.  This is what I do in my 
standard library https://github.com/marler8997/mar which works 
with or without libc.  I went through several designs for how to 
go about this memcpy solution and what I've provided you is the 
result of that.

> If the better version calls libc, then when libc
> is finally removed, all this code will break. And because we 
> encouraged
> this bad practice, _a lot_ of code will break.

How would it break?  If you remove libc, your module should now 
enable your implementation of memcpy.  And all the code that 
calls memcpy doesn't care whether it came from libc or from a D 
module.




More information about the Digitalmars-d mailing list