Jump to page: 1 2
Thread overview
C to D bindings: how much do you D-ify the code?
Oct 25, 2013
Lionello Lunesu
Oct 25, 2013
John Colvin
Oct 25, 2013
John Colvin
Oct 25, 2013
Dicebot
Oct 26, 2013
Lionello Lunesu
Oct 25, 2013
Dicebot
Oct 26, 2013
Lionello Lunesu
Oct 25, 2013
Mike Parker
Oct 25, 2013
Paulo Pinto
Oct 25, 2013
Jakob Ovrum
Oct 26, 2013
Jacob Carlborg
Oct 29, 2013
Lionello Lunesu
Oct 26, 2013
Benjamin Thaut
Oct 26, 2013
Mike Parker
Oct 26, 2013
Michel Fortin
Oct 26, 2013
Mike Parker
October 25, 2013
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
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
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
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
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
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
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
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
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
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
« First   ‹ Prev
1 2