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