Thread overview | ||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
October 25, 2013 C to D bindings: how much do you D-ify the code? | ||||
---|---|---|---|---|
| ||||
There's a lot of expressiveness that can be added to D bindings, when compared to the C or C++ headers, for a particular library: 1. enum names vs prefixes enum FOO { FOO_A, FOO_B }; -> enum FOO {A,B} 2. enum vs int parameters (based on a lib's documentation) void takefoo(int) -> void takefoo(FOO) 3. "in" for input buffers (again, based on docs) int puts(char*) -> puts(in char*) 4. "out" or "ref" for output parameters void getdouble(double*) -> void getdouble(out double value) 5. D arrays vs length+pointer pairs void bar(size_t len, int* ptr) -> void bar(int[] a) 6. D array wrappers void bar(int* ptr, int size) -> void bar(int[] a) { bar(a.ptr, cast(int)a.length; } 6. library specific sized-int typedefs to D natives png_uint_16 -> short These are some of the more trivial ones, but I'd like to see how other people go about making bindings. Do you keep as close to C as possible? Or do you "add value" by using more D style constructs? L. |
October 25, 2013 Re: C to D bindings: how much do you D-ify the code? | ||||
---|---|---|---|---|
| ||||
Posted in reply to Lionello Lunesu | On Friday, 25 October 2013 at 13:10:05 UTC, Lionello Lunesu wrote:
> There's a lot of expressiveness that can be added to D bindings, when compared to the C or C++ headers, for a particular library:
>
> 1. enum names vs prefixes
> enum FOO { FOO_A, FOO_B }; -> enum FOO {A,B}
>
> 2. enum vs int parameters (based on a lib's documentation)
> void takefoo(int) -> void takefoo(FOO)
>
> 3. "in" for input buffers (again, based on docs)
> int puts(char*) -> puts(in char*)
>
> 4. "out" or "ref" for output parameters
> void getdouble(double*) -> void getdouble(out double value)
>
> 5. D arrays vs length+pointer pairs
> void bar(size_t len, int* ptr) -> void bar(int[] a)
>
> 6. D array wrappers
> void bar(int* ptr, int size) ->
> void bar(int[] a) { bar(a.ptr, cast(int)a.length; }
>
> 6. library specific sized-int typedefs to D natives
> png_uint_16 -> short
>
>
> These are some of the more trivial ones, but I'd like to see how other people go about making bindings. Do you keep as close to C as possible? Or do you "add value" by using more D style constructs?
>
> L.
I would go for a two stage approach:
1) Write bindings that map as closely as possible to the C API, only adding anything extra by necessity and/or where it is transparent in correct usage.
2) Create a full on D wrapper around the bindings with the best API you can design using as much D as you like.
|
October 25, 2013 Re: C to D bindings: how much do you D-ify the code? | ||||
---|---|---|---|---|
| ||||
Posted in reply to Lionello Lunesu | I think best approach is to have 2-step bindings. First step is pure 1-to-1 translation with no D-ification at all. Second step is D wrapper that expresses same functionality in more native syntax (probably even more type-safe). Step-2 module imports Step-1 module of course. Benefit of such approach is that you can generate bindings using automatic tool when new header version is out without wasting time on adjusting those to D style again and again - you only need to change step-2 module if there are some breaking API changes. |
October 25, 2013 Re: C to D bindings: how much do you D-ify the code? | ||||
---|---|---|---|---|
| ||||
Posted in reply to Lionello Lunesu | On 10/25/2013 10:10 PM, Lionello Lunesu wrote:
>
> These are some of the more trivial ones, but I'd like to see how other
> people go about making bindings. Do you keep as close to C as possible?
> Or do you "add value" by using more D style constructs?
>
IMO, a binding to an existing library should never add anything extra if it is intended to be released to the public. It should adhere as closely as possible to the C API. This is especially important if the C library is well-known. It would mean that existing sample code, tutorials and so on would require minimal adjustment to work in D. In that case, the two-step process recommended in other replies is the way to go. If it's for internal use only, then I think it doesn't really matter either way (with the caveat that D-ifying the binding may increase maintenance costs when the C library is updated -- but I don't think it's so high anyway).
However, if it were me and I weren't binding an existing C library but, instead, developing a new one and a D binding to go along with it, I would be more inclined to D-ify the binding in that case.
|
October 25, 2013 Re: C to D bindings: how much do you D-ify the code? | ||||
---|---|---|---|---|
| ||||
Posted in reply to Lionello Lunesu | Am 25.10.2013 15:10, schrieb Lionello Lunesu:
> There's a lot of expressiveness that can be added to D bindings, when
> compared to the C or C++ headers, for a particular library:
>
> 1. enum names vs prefixes
> enum FOO { FOO_A, FOO_B }; -> enum FOO {A,B}
>
> 2. enum vs int parameters (based on a lib's documentation)
> void takefoo(int) -> void takefoo(FOO)
>
> 3. "in" for input buffers (again, based on docs)
> int puts(char*) -> puts(in char*)
>
> 4. "out" or "ref" for output parameters
> void getdouble(double*) -> void getdouble(out double value)
>
> 5. D arrays vs length+pointer pairs
> void bar(size_t len, int* ptr) -> void bar(int[] a)
>
> 6. D array wrappers
> void bar(int* ptr, int size) ->
> void bar(int[] a) { bar(a.ptr, cast(int)a.length; }
>
> 6. library specific sized-int typedefs to D natives
> png_uint_16 -> short
>
>
> These are some of the more trivial ones, but I'd like to see how other
> people go about making bindings. Do you keep as close to C as possible?
> Or do you "add value" by using more D style constructs?
>
> L.
Speaking from my experience in other languages, try to map 1:1 to the C API as much as possible, given the language features offered for FFI.
Offer another layer that makes use of this binding API in a more canonical way for the given language.
--
Paulo
|
October 25, 2013 Re: C to D bindings: how much do you D-ify the code? | ||||
---|---|---|---|---|
| ||||
Posted in reply to John Colvin | On Friday, 25 October 2013 at 13:26:33 UTC, John Colvin wrote:
> On Friday, 25 October 2013 at 13:10:05 UTC, Lionello Lunesu wrote:
>> There's a lot of expressiveness that can be added to D bindings, when compared to the C or C++ headers, for a particular library:
>>
>> 1. enum names vs prefixes
>> enum FOO { FOO_A, FOO_B }; -> enum FOO {A,B}
>>
>> 2. enum vs int parameters (based on a lib's documentation)
>> void takefoo(int) -> void takefoo(FOO)
>>
>> 3. "in" for input buffers (again, based on docs)
>> int puts(char*) -> puts(in char*)
>>
>> 4. "out" or "ref" for output parameters
>> void getdouble(double*) -> void getdouble(out double value)
>>
>> 5. D arrays vs length+pointer pairs
>> void bar(size_t len, int* ptr) -> void bar(int[] a)
>>
>> 6. D array wrappers
>> void bar(int* ptr, int size) ->
>> void bar(int[] a) { bar(a.ptr, cast(int)a.length; }
>>
>> 6. library specific sized-int typedefs to D natives
>> png_uint_16 -> short
>>
>>
>> These are some of the more trivial ones, but I'd like to see how other people go about making bindings. Do you keep as close to C as possible? Or do you "add value" by using more D style constructs?
>>
>> L.
>
> I would go for a two stage approach:
>
> 1) Write bindings that map as closely as possible to the C API, only adding anything extra by necessity and/or where it is transparent in correct usage.
As an aside, I would suggest that function overloads that take arrays instead of pointer + length is normally a harmless addition to an otherwise 1:1 set of bindings.
|
October 25, 2013 Re: C to D bindings: how much do you D-ify the code? | ||||
---|---|---|---|---|
| ||||
Posted in reply to John Colvin | On Friday, 25 October 2013 at 16:22:46 UTC, John Colvin wrote:
> As an aside, I would suggest that function overloads that take arrays instead of pointer + length is normally a harmless addition to an otherwise 1:1 set of bindings.
I disagree. You can always add those wrappers in stage-2 module and get it inlined so there is no profit in doing it in stage-1 module. But losing ability to auto-generate stuff is huge.
|
October 25, 2013 Re: C to D bindings: how much do you D-ify the code? | ||||
---|---|---|---|---|
| ||||
Posted in reply to Lionello Lunesu | On Friday, 25 October 2013 at 13:10:05 UTC, Lionello Lunesu wrote:
> These are some of the more trivial ones, but I'd like to see how other people go about making bindings. Do you keep as close to C as possible? Or do you "add value" by using more D style constructs?
>
> L.
I also think keeping the C bindings as faithful as possible is the best, correct even, approach. By keeping the C bindings faithful, one can defer to the existing documentation of the C library, and user code can be ported from C trivially.
In a good wrapper, the C bindings are present only because the second layer depends on them and for compatibility purposes, while the second layer - the idiomatic D interface - should cover all use cases. I've found that with D's expressive modelling power and metaprogramming capabilities, the second layer does not need to compromise on performance or functionality while providing a safer, more intuitive and more convenient interface.
|
October 26, 2013 Re: C to D bindings: how much do you D-ify the code? | ||||
---|---|---|---|---|
| ||||
Posted in reply to Lionello Lunesu | On 2013-10-25 15:10, Lionello Lunesu wrote: > There's a lot of expressiveness that can be added to D bindings, when > compared to the C or C++ headers, for a particular library: I agree with what others have said. I can add that I sometimes use three layers. 1. The actual C bindings. Stay as close as possible to the original code 2. Slightly D-ify the C bindings. I.e. be able to pass D strings instead of C strings 3. Create wrappers. Either object oriented with classes or something in between, like structs with some internal sate and a couple of methods -- /Jacob Carlborg |
October 26, 2013 Re: C to D bindings: how much do you D-ify the code? | ||||
---|---|---|---|---|
| ||||
Posted in reply to Lionello Lunesu | Am 25.10.2013 15:10, schrieb Lionello Lunesu:
> There's a lot of expressiveness that can be added to D bindings, when
> compared to the C or C++ headers, for a particular library:
>
> 1. enum names vs prefixes
> enum FOO { FOO_A, FOO_B }; -> enum FOO {A,B}
>
> 2. enum vs int parameters (based on a lib's documentation)
> void takefoo(int) -> void takefoo(FOO)
>
> 3. "in" for input buffers (again, based on docs)
> int puts(char*) -> puts(in char*)
>
> 4. "out" or "ref" for output parameters
> void getdouble(double*) -> void getdouble(out double value)
>
> 5. D arrays vs length+pointer pairs
> void bar(size_t len, int* ptr) -> void bar(int[] a)
>
> 6. D array wrappers
> void bar(int* ptr, int size) ->
> void bar(int[] a) { bar(a.ptr, cast(int)a.length; }
>
> 6. library specific sized-int typedefs to D natives
> png_uint_16 -> short
>
>
> These are some of the more trivial ones, but I'd like to see how other
> people go about making bindings. Do you keep as close to C as possible?
> Or do you "add value" by using more D style constructs?
>
> L.
I actually do number 1 whenever creating C bindings. Because its not really DRY to write: FOO.FOO_A
Kind Regards
Benjamin Thaut
|
Copyright © 1999-2021 by the D Language Foundation