Binding D to C

Jacob Carlborg doob at me.com
Fri May 17 00:44:14 PDT 2013


On 2013-05-16 02:37, Walter Bright wrote:
> http://www.gamedev.net/page/resources/_/technical/game-programming/binding-d-to-c-r3122
>
>
> http://www.reddit.com/r/programming/comments/1eerd9/binding_d_to_c/
>
> https://news.ycombinator.com/newest

It's a good article. I haven't read it completely to the end but I would 
like to mention a few things.

You didn't mention macros, global variables or inline functions. I think 
I've seen inline functions somewhere.

Global Variables

Global variables need to have an extra "extern" and the __gshared 
storage. Example in C:

int a;

Translated to D:

extern (C) extern __gshared int a;

For TLS variables __gshared is not used.

Typedefs

When I do a binding I'm trying to figure out why they used a typedef in 
the first place. There's a couple of reasons:

* To get a fixed type on all platforms. In the C language there are no 
fixed types, it's just relations between the sizes of the types. I.e. 
long => int => short => char, or something like that.

In this case, use the native D type. In D a given type has a fixed size 
on all platforms (except for real). Examples of these can be: "int8_t", 
"uint16_t", "uint32_t" and so on.

* To get different sizes on different platforms. For example, a 32bit 
value of 32bit platforms and a 64bit value of 64bit platforms. In this 
case you're basically forced to use an alias.

* Opaque types. Perhaps the API is hiding the actual type behind a void* 
or for other reasons uses a void*. Then it uses a tyepdef on top of that 
to give it an idea of what type we're dealing with.

In this case use the typedef.

* The typedef is well known in the API. Examples of this would be GLint. 
I don't remember why they use typedefs but if I recall correctly GLint 
is used in all examples, tutorials, books and so on.

In this case it's best to the typedef.

* Hiding a complex type. An example of this could be function pointers. 
In this case use the typedef.

Function pointers

With function pointers there are (at least) two cases where an alias 
have to be used, instead of a function pointer.

* When declaring function parameters with a specific linkage. The 
following is syntactically invalid in D:

void foo (extern(C) void function () callback);

Use an alias:

alias extern (C) void function () Callback;

void foo (Callback callback);

* When using a cast with a specific linkage. You won't see this in a 
binding, if you're not converting inline functions. This is invalid in D 
as well:

void* foo;
...
auto bar = cast(extern (C) void function ()) foo;

Use the same approach as above.

Structs/Unions

You actually didn't mention unions at all but basically all the same 
rules that apply when translating structs apply to unions as well. Two 
other things that you didn't mention was nested structs and anonymous 
structs.

Structs

* For named structs in typedefs I feel a bit undecided of how I want to 
have it. You can either just use the name of the typedef in D when 
declaring the struct. Or you can use the real name of the struct and 
then use an alias as well. In some cases the actual name of the struct 
could be used, i.e. "struct foo".

Anonymous Structs

* If an anonymous struct is used directly to declare a variable you're 
forced to invent a name for the struct in D, since D doesn't support 
anonymous structs. Example:

struct
{
     int a;
     int b;
} c;

Translate to:

struct _AnonymousStruct1
{
     int a;
     int b;
}

_AnonymousStruct1 c;

Any name can be used in this case. In my tool, DStep, for automatically 
generating bindings I'm using names similar to above.

* If an anonymous struct is used in a typedef just use the name of the 
typedef. No alias is required in the D code in this case. I actual 
noticed now that you have this example.

Nested structs

I always fail to remember how these should be translated to D.

* For nested structs that are named. Example:

struct Foo
{
     int a;
     struct Bar
     {
         int b;
     } bar;
}

In this case translate it to a named struct in D as well:

struct Foo
{
     int a;
     struct Bar
     {
         int b;
     }
     Bar bar;
}

* For nested struct that are anonymous. Example:

struct Foo
{
     int a;
     struct
     {
         int b;
     } bar;
}

In this case you could translate it to an anonymous struct in D as well:

struct Foo
{
     int a;
     struct
     {
         int b;
     }
}

The problem with this is that the API changes. Instead of accessing "b" 
like this:

struct Foo foo;
foo.bar.b = 1;

You would do like this:

Foo foo;
foo.b = 1;

I actually looked at the documentation now and see that this example is 
not used anymore:

http://dlang.org/interfaceToC.html
http://dlang.org/htod.html

It's still on the D1 page:

http://digitalmars.com/d/1.0/htomodule.html

-- 
/Jacob Carlborg


More information about the Digitalmars-d mailing list