Deimos
Deimos is a collection of repositories for interfacing C libraries. Its aim is to integrate existing C libraries. Since D is binary compatible with C ABI code, this makes a wealth of existing functionality available in D with limited effort. Deimos repositories are available at github.
How to use a Deimos repository
Each Deimos repository is named after the corresponding C library. Each must follow the directory structure,
C/ // contains the original C header files
deimos/ // contains the translated D modules
examples/ // contains examples
README //
Each library should contain a README in the root folder. The README file should contain exact source and version of the C library. Furthermore it should contain specific notes about the usage of the D import file.
Every library should contain a simple D example. This could be used for basic self testing.
To use a Deimos library you need to clone/submodule its repository. Adjust the import path and link/load the C library. E.g. to use OpenSSL execute
git clone git://github.com/D-Programming-Deimos/openssl.git
dmd -Iopenssl/ -L-lssl ...
How to create a Deimos repository
Deimos is constantly growing but having more libraries at your finger tips is always an improvement. Let's walk through the basic steps of creating a Deimos repository.
- Initialize the repository
- Add the plain C header files in branch C
- Merge branch C and copy C header files to deimos/
- each slash (/) has to be replaced by a dot (.)
- each dash (-) has to be replaced by a dot (.)
- each D keyword gets a underscore (_) appended
- module and package names must be lower case
- Changing the contents of each module
- Replace C include
- Add extern(C) and nothrow
- Global variables
- Replacing types
- signed char becomes byte.
- unsigned char becomes ubyte.
- unsigned becomes uint.
- long becomes core.stdc.config.c_long.
- long double becomes real.
- T(*)(params) becomes T function(params).
- Replacing typedef with alias
- Enumerated types
- Versioning
- Replacing macros
- Tag with library version
git init
touch README.md
git add README.md
git commit
git checkout -b C
mkdir C/
cp -a path/to/include/files/* C/
git add C/
git commit
git checkout master
git merge C
cp -a C/ deimos/
git commit
For each file a proper module declaration has to be provided. That is, you have to specify a fully qualified module name (see Module). The fully qualified module name has to start with deimos.. In addition
Once the D module name is fixed the C header file should be moved using git mv where the new file name follows the chosen module name (see Module). E.g. the module llvm.c.analysis is in the package c, which itself is a package of llvm which means llvm-c/Analysis.h will be moved to deimos/llvm/c/analysis.d. Note, that if the module name contains dots (i.e. the module is part of a package) a hierarchy of directories has to be created.
Once all header files are moved make a commit. This commit only reflects the renaming and allows git to track the renaming of each file. Never rename a file and change its content as this will destroy git's ability to track a moved file.
So far, each C header file was renamed to a D module. Next the contents of each module will be adjusted. In general following the advices from interfacing to c is recommended.
The D files should try to do as least modifications as possible to simplify updates of the C headers. This includes leaving comments intact. The copyright for the D files should match the one being used by the C header as they are derived work.
In particular,
A C include like #include <path/to/header.h> needs to be replaced by import path.to.header; (see D import statement) or removed.
Public imports should be avoided as much as possible. This might result in additional imports are needed, without any corresponding C include.
For C headers there is usually exactly one corresponding module in core.stdc. E.g.,
#include <stdbool.h> // can be removed since D's bool is compatible with C's
#include <wchar.h> // core.stdc.wchar_
#include <stdarg.h> // core.stdc.stdarg
#include <inttypes.h> // core.stdc.inttypes
#include <stdint.h> // core.stdc.stdint
#include <complex.h> // core.stdc.complex
If there are system specific headers (Posix, Windows, OS X, etc.) included,
search the package core.sys for an appropriate replacement.
To instruct the compiler to use the C linkage add extern(C). Further, since C has no exceptions adding nothrow is recommended.
// imports
extern(C) nothrow:
// contents
Global variables in C are not local to the thread as they are in D (see migrating to shared and C globals). That's why each global variable needs __gshared prepended.
int a;
is translated to
extern __gshared int a;
If the variable is declared with static add the private protection to ensure that the variable is only
visible within the module.
static int a;
is translated to
private extern __gshared int a;
Types are replaced according to interfacing to c (see table "Data Type Compatibility"). E.g.,
The equivalent of C's typedef is alias in D. Code like
typedef Int int;
is replaced by
alias Int int;
In D a named enum has scoped enum members contrary to C's enum. To adjust replace code like
enum Day { monday, tuesday, wednesday, thursday, friday, saturday, sunday };
enum Day day = monday;
by
enum Day { monday, tuesday, wednesday, thursday, friday, saturday, sunday };
enum Day day = Day.monday;
In C enumerated types are of type int only. Respectively, code like
typedef enum { monday, tuesday, wednesday, thursday, friday, saturday, sunday } Day;
is replaced by
alias int Day;
enum : Day { monday, tuesday, wednesday, thursday, friday, saturday, sunday };
C versioning macros should be translated, as close as possible, to predefined version identifiers. E.g.
#ifdef __WIN32__
#else
#endif
is translated to
version(Win32)
{
}
else
{
}
Translated header should not require linkage of any D binary. Necessary D functions can be written as nullary templates.
#define GET(x) x.value is replaced by int GET()(X x) { return x.value; }.When translating C macros, CTFE and import expression can be of help.
Tag the commit of the finished translation using the same version string as used by the C library.
git tag 3.1 <commit>
We strongly recommend to follow these guidelines to warrant inclusion in Deimos. However, deviation may be necessary in some cases.
To push the repository upstream a repository has to be created at github. Ask for such in digitalmars.D. Finally, create a pull request.
Handling multiple versions
When translating multiple versions of a C library each translation must be tagged with its appropriate version. Each version should have its own branch to ease updating/fixing a translation. The master branch is the translation of the latest version.
How to update an existing Deimos repository
Checkout the branch C
git checkout C
copy the header files
Merge the branch C and fix conflicts.
git checkout master
git merge C
// fix conflicts and adjust as explained above
git commit
git tag -f <old_tag>
References: