Load dynamic libraries with no hand-written bindings!

Andrej Mitrovic andrej.mitrovich at gmail.com
Tue Sep 6 16:03:03 UTC 2022


There is a really cool benefit to having the ImportC feature in 
the language. It enables us to dynamically bind to C libraries 
without having to write any bindings code by hand!

I don't think I'm the first one to figure this out (surely 
someone came up with this stuff before?), but anyway here's the 
trick:

- Step 1: Preprocess your C library header file so you can import 
it in D.
- Step 2: Use the magic of D's compile-time introspection to 
generate function pointers and the associated loader routines.
- Step 3: ???
- Step 4: Profit!

The entire minimal example that works on Windows is in this repo: 
https://github.com/AndrejMitrovic/easybind/blob/master/main.d

Copied here verbatim:

```d
static import portaudio;

import std.meta;
import std.stdio;
import std.traits;
import core.sys.windows.windows;

struct Tuple(_FuncType, string _Name) {
     alias FuncType = _FuncType;
     enum Name = _Name;
}

/* Get the function pointer type of an actual function */
template FuncType(alias symbol) {
     ReturnType!symbol function(Parameters!symbol) func;
     alias FuncType = SetFunctionAttributes!(typeof(func), 
functionLinkage!symbol,
         functionAttributes!(typeof(func)));
}

/* Get a sequence of (Function type, Name) belonging to the 
provided module */
template GetFunctionList(alias Module) {
     alias GetFunctionList = AliasSeq!();
     static foreach (idx, member; __traits(allMembers, Module)) {
         static if (isFunction!(__traits(getMember, Module, 
member))) {
             GetFunctionList = AliasSeq!(GetFunctionList,
                 Tuple!(FuncType!(__traits(getMember, Module, 
member)), member));
         }
     }
}

/* Generate dynamic bindings for all functions in Module and load 
SharedLib */
class Dynamic(alias Module, string SharedLib)
{
     /* Load the shared library */
     static HANDLE dll;
     static this() {
         dll = LoadLibraryA(SharedLib);
         !dll && assert(0);
     }

     /* Declare the function pointers */
     static foreach (Tup; GetFunctionList!Module) {
         mixin("Tup.FuncType " ~ Tup.Name ~ ";");
     }

     /* Load the function pointers */
     this()
     {
         static foreach (Tup; GetFunctionList!Module) {
             *(cast(void**)&__traits(getMember, this, Tup.Name))
                 = cast(void*)GetProcAddress(dll, Tup.Name);
         }
     }
}

void main() {
     // easy!
     auto dynamic = new Dynamic!(portaudio, "portaudio_x64.dll");
     printf("Version info %s\n", dynamic.Pa_GetVersionText());
}
```

And then:

```
$ dub run
Version info PortAudio V19.7.0-devel, revision unknown
```

Pretty neat, huh?



More information about the Digitalmars-d mailing list