The D(eimos) Dynamic Loader portably loads C libraries at run time and binds extern(C) function declared in a module given its module name at compile time. These functions can then be loaded at run time.

Loading a library at run time is useful for graceful degradation. First check whether a library exists and then use the provided functionality. Otherwise provide only a basic set of features.

For a module name given at compile time its extern(C) functions are loadable at run time. Writing a module containing extern(C) declared functions is easy. Even better there are already modules for the purpose of calling C libraries from within D provided by Deimos. This set of modules is steadily growing and it is easy to add a new module by converting a C library's header file(s) to D.

Note, that loading overloaded functions is not possible. Only the function name is used for loading. This may give surprising results when overloaded functions are loaded.

This module is written such that is should work on Windows but has been only compiled and tested on Linux. Please report any issue while trying it out.

Known Bugs
http://github.com/jkm/ddl/issues
License
Boost License 1.0
Version
0.1 (alpha release)
Date
2012-03-21


Examples
The canonical Hello World loading the C library at run time and calling its printf function.

 unittest
 {
     import ddl;
     import std.string : toStringz;
     import core.stdc.stdio; // import the module as usual

     // load the library C to resolve extern(C) functions declared
     // in core.std.stdio, but load no functions yet
     auto stdio = loadLibrary!(core.stdc.stdio)(libraryFilename("c")~".6", false);

     // not loaded yet
     assert(stdio.printf == null);

     // load the function printf
     stdio.loadFunction!("printf")();
     // ... and call it
     stdio.printf(toStringz("Hello World.\n"));
 }


To give another example we use OpenSSL. Deimos already provides a D module to interface it (see Deimos OpenSSL). Provided that you pass the path to deimos/openssl/aes.d to the compiler and the Openssl library is loadable the following code works.

 unittest
 {
     import ddl;
     import deimos.openssl.aes;

     auto aes = loadLibrary!(deimos.openssl.aes)("ssl");
     assert(aes.loadedFunctions == ["AES_options", "AES_set_encrypt_key",
               "AES_set_decrypt_key", "AES_encrypt", "AES_decrypt",
               "AES_ecb_encrypt", "AES_cbc_encrypt", "AES_cfb128_encrypt",
               "AES_cfb1_encrypt", "AES_cfb8_encrypt", "AES_ofb128_encrypt",
               "AES_ctr128_encrypt", "AES_ige_encrypt", "AES_bi_ige_encrypt",
               "AES_wrap_key", "AES_unwrap_key"]);
 }


To ease switching between loading at run time and linking at compile time the mixin template declareLibraryAndAlias is provided. This mixin constructs aliases for each extern(C) function.

If module is compiled with version(ddl) then the library is loaded at run time. Otherwise it is linked at compile time.
 import ddl;
 // declare either Library!(deimos.openssl.sha) sha or import deimos.openssl.sha
 version(ddl) mixin declareLibraryAndAlias!("deimos.openssl.sha", "sha");
 else import deimos.openssl.sha;

 unittest
 {
     version(ddl) sha = loadLibrary!(deimos.openssl.sha)("ssl");

     // use as usual
     ubyte[] ibuf = [0x61, 0x62, 0x63];
     ubyte[20] obuf;
     SHA1(ibuf.ptr, ibuf.length, obuf.ptr);

     assert(obuf == [0xA9, 0x99, 0x3E, 0x36, 0x47, 0x06, 0x81, 0x6A, 0xBA,0x3E,
                     0x25, 0x71, 0x78, 0x50, 0xC2, 0x6C, 0x9C, 0xD0, 0xD8, 0x9D]);
 }


Finally, to show case possible exceptional cases:
 unittest
 {
     import ddl;
     import std.string : toStringz;
     import core.stdc.stdio;

     // throws if some_library could not be loaded
     assertThrown!UnsatisfiedLinkException(loadLibrary!(core.stdc.stdio)("some_library", false));
     auto stdio = loadLibrary!(core.stdc.stdio)(libraryFilename("c")~".6", false);

     // compile error, since foobar is not extern(C) function in core.stdc.stdio
     static assert(!__traits(compiles, stdio.loadFunction!("foobar")()));

     // stdio.printf is not loaded
     assert(stdio.printf == null);
     // calling it results in a segfault
     //stdio.printf(toStringz("Hello World\n"));

     // assuming fun is a extern(C) declared function but not defined
     assertThrown!UnsatisfiedLinkException(stdio.loadFunction!("fun")());
 }

On Posix the libraryFilenamePrefix is "lib". On Windows it is "".


On Posix the libraryFilenameExtension is ".so". On Windows it is ".dll".


pure bool isLibraryFilename(string libraryFilename);

Returns
true if libraryFilename is a library filename on this system.

pure nothrow string libraryFilename(string libraryName);

Returns
the system specific filename of libraryName.

E.g. on Posix the library name "c" has the filename "libc.so". On Windows the library "c" has the filename "c.dll".

bool isLibraryPath(string libraryPath);

Returns
true if libraryPath is a library path name for this system. It does not check that the library path actually exists on this system.

nothrow bool isLoadable(string libraryName);

Returns
true if libraryName is loadable on this system.

string libraryPath(string libraryName);

Returns
the absolute library path for the given library name on this system.

Note, that the library will be loaded.
Throws
UnsatisfiedLinkException if libraryName could not be loaded.

auto loadLibrary(alias moduleName)(string libraryName, bool loadAllNow = true);

Returns
a Library!moduleName loading library libraryName and all extern(C) functions declared in module moduleName by default.
Parameters
moduleName is the module name those extern(C) functions will be loaded
libraryName is the library name to load
Throws
UnsatisfiedLinkException if libraryName or a function (if requested) could not be loaded.

auto loadLibrary(alias moduleName)(bool loadAllNow = true);

Same as above but the library name is inferred from the module name.

Examples
 import deimos.openssl.aes;
 auto aes = loadLibrary!(deimos.openssl.aes); // loads library with name "aes

template declareLibraryAndAlias(alias moduleName, alias as)

Imports moduleName, declares Library!(moduleName) and alias to all the libraries extern(C) functions.

Parameters
moduleName is the module name those extern(C) functions will be loaded
as is the variable name used when declaring Library!(moduleName)
Examples
 mixin declareLibraryAndAlias!("deimos.openssl.sha", "sha");

struct Library(alias moduleName);

A Library is capable of loading all extern(C) functions declared in module moduleName.

On construction a given library will be loaded which is unloaded on destruction. Note, that all instances of a Library share the extern(C) functions as these are declared as static member variables.

Parameters
moduleName is the module name those extern(C) functions will be loaded

alias Filter!(isExternCFunction, AllModuleMembers) ExternCFunctions;

ExternCFunctions is a TypeTuple containing the names of all extern(C) functions declared in moduleName.


this(string libraryName, bool loadAllNow);

Loads the library with given name.

A loaded library is unloaded on destruction. Note, that loaded functions are not unset, which can be a source of errors.

Parameters
string libraryName specifies the library to load. libraryName may either be a library name, a filename, or a path to a library.
bool loadAllNow specifies whether all functions should be loaded.
Throws
UnsatisfiedLinkException if libraryName or a function (if requested) could not be loaded.

nothrow bool isLoaded();

Returns
true, if this Library is loaded. Otherwise false.

void loadFunction(string functionName)();

Load function with given functionName.

After successful execution the function with name functionName is loaded and accessible by all instances of this Library.

Fails at compile time, if functionName is not declared in moduleName.

Parameters
functionName is the name of the extern(C) function to load.
Throws
UnsatisfiedLinkException if functionName could not be loaded from libraryName.

nothrow void unloadFunction(string functionName)();

Unload function with given functionName.

Note, that the function is unavailable for all instances of this Library after unloading.

Fails at compile time, if functionName is not declared in moduleName.

Parameters
functionName is the name of the extern(C) function to unload.

Load all functions.

It calls loadFunction for each ExternCFunctions.

Throws
UnsatisfiedLinkException if a function could not be loaded.

nothrow void unloadAllFunctions();

Unload all functions by calling unloadFunction for each ExternCFunctions.


string[] loadedFunctions();

Returns
all loaded functions of Library.

Note, this is a static property as it is independent of the instance.

class UnsatisfiedLinkException: object.Exception;

Exception that is thrown if a library or function could not be loaded.