Bind D to C

From D Wiki
Jump to: navigation, search

Introduction

Article and Series on creating bindings to C libraries for the D programming language.

See C++ interop for information on interoperability between D and C++. More can be done than is currently described in the official documentation.


Response to the article / more information

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

Usage

For instance, for a C library called "fancyLib" with the header "fancyLib.h" :

// fancyLib.h
struct S
{
    int a, b;
}
char const *fancyCFunction(int a, long b, struct S s);

The D binding should be :

// fancyLib.d
import core.stdc.config : c_long;
struct S
{
    int a, b;
}
extern(C) const(char)* fancyCFunction(int a, c_long b, S s);

To use this binding :

// myCode.d
import fancyLib;
void main()
{
    auto res = fancyCFunction(4, 53, S(1, 1));
}

To compile the application :

dmd fancyLib.d myCode.d -L-lfancyLib

You can also add fancyLib to "libs", if you use dub.

See also