Interfacing D with C and Fortran

This article was written in collaboration with Ilya Yaroshenko a D contributor and the creator of Mir a collection of numerical packages for D. The code for this article is located here. Thanks to the D community for their suggestions that informed the article.

The C and Fortran programming languages have many popular libraries that are heavily relied upon. D’s generic and meta-programming features, as well as its multi-paradigm nature and clear syntax makes it a great choice for various applications. Many programming languages allow varying degrees of compatibility with C and Fortran libraries. However C libraries have full compatibility with D, and Fortran subroutines can be called from D. This article describes how to interface D code with C and Fortran. This includes outlining the ease in which functions in C standard libraries can be accessed from D, calling functions from C static libraries, calling D functions from C, and calling Fortran subroutines from D. The article also covers how to use D’s string mixins to generate code to avoid repetitive writing of wrapper functions and declarations.

The D code in this article uses -betterC style. This mode of programming is increasingly popular in D circles focusing on D as a replacement for C in systems programming. The betterC flag in the D compiler uses a light-weight subset of D and has the side-effect of facilitating an integration of D code to C that is as seamless as calling C code from D. The main effect of the betterC flag is to remove link-time dependencies for DRuntime and Phobos (D’s standard library), however at the time of writing this article, the flag is partially implemented and removes only module information.

The examples presented here are run on a Linux Ubuntu 64-bit 16.04 system and use the gcc 5.4.0 C and Fortran compilers and ldc2 version 1.1.0 D’s LLVM-based D compiler.

Calling C functions from D

The math.h header describes a standard library in C. Example1 code below shows the D code for importing the fabs and pow functions from this library. The extern (C) braces contain functions to be imported from C. The qualifiers nogc and nothrow are used because the imported C functions are not garbage collected and do not throw exceptions.

In general the C language all too easily allows unsafe actions one of which is corrupting data. Some of the imported functions are modified with @safe which ensures that they do not carry out a number of potentially unsafe actions. The printf function is not marked with @safe but instead the scope statement is used because the pointer’s lifetime does not escape the function. In addition the scoped pointer can not be passed to another function internally that is not itself scoped, or to a global variable or another internal pointer, nor can it be returned.

/* example1.d */ @nogc nothrow : /* Declare C functions */ extern ( C ) { double pow ( double x , double y ) @safe ; double fabs ( double x ) pure @safe ; int printf ( scope const char * format , ...); } pragma ( inline , false ) void printArray ( double [] arr ) { foreach ( elem ; arr ) printf ( "%f " , elem ); printf ( "

" ); } pragma ( inline , true ) void apply ( alias fun )( double [] arr ) { foreach ( ref elem ; arr ) elem = fun ( elem ); } __gshared double [] x = [- 1 , 2 , - 3 , 4 , - 5 , 6 ]; extern ( C ) int main () { apply ! fabs ( x ); printArray ( x ); apply !( a => pow ( a , 2.5 ))( x ); printArray ( x ); return 0 ; }

which gives the output:

ldc2 -betterC -Oz -release -linkonce-templates -run example1.d # 1.000000 2.000000 3.000000 4.000000 5.000000 6.000000 # 1.000000 5.656854 15.588457 32.000000 55.901699 88.181631

The following are helpful descriptors of the code above:

Full descriptions of the flags for the ldc2 compiler are given with the -help flag, but the current flags are to reduce code size and bloat and create light-weight executables.

If you are new to D the apply function is an example of using meta-programming techniques to pass a function using the template alias parameters. More details on template meta-programming can be found on Philippe Sigaud’s tutorial. In addition, the Mir and Phobos libraries offer map functions that iterate over arrays.

Purity of functions can be enforced in D using the pure qualifier to indicate that the function can not have side-effects which is the case with the fabs function.

The printArray function is used multiple times and can be explicitly marked with pragma(inline, false) .

The __gshared qualifier indicates global shared data - the default behaviour in C/C++, it places x in the DATA section of the object file.

Garbage collection and execptions are not used so extern(C) declares common C _main instead of D __Dmain . This allows the final executable to be reduced in size by 72 times (from 617.5 KB to 8.5 KB).

The nm tool allows the object file symbols to be inspected. The output shows the economy of the created symbols, it omits the creation of code that increases file size and perhaps efficiency of the executable.

ldc2 -betterC -nogc -Oz -release -linkonce-templates -c example1.d nm example1.o #0000000000000000 T __D7article10printArrayFNbNiAdZv #0000000000000050 T _main #0000000000000170 D __D7article1xAd #0000000000000180 d _.constarray # U _fabs # U _pow # U _printf # U _putchar

The symbols in the object file that can be described as:

__D7article10printArrayFNbNiAdZv - printArray , D mangling. _main - main , C mangling. __D7article1xAd - x 's length and pointer. _.constarray - x 's data. _fabs , _pow , _printf - extern C symbols. _putchar - extern C symbol, optimized version for printf("

") .

Calling static C libraries from D

The main difference between calling static C libraries from D and calling installed C libraries is in the compilation process. Below is an example of a simple multiplication function written in C:

/* example2c.c */ double mult ( double x , double y ) { return x * y ; }

The D code to call this is similar to our previous example:

/* example2d.d */ extern ( C ) @nogc nothrow : double mult ( double x , double y ) @safe pure ; int printf ( scope const char * format , ...); extern ( C ) int main () { printf ( "%f

" , mult ( 3 , 4 )); return 0 ; }

There are many ways to compile these two scripts. One way is to compile the C code to an object file and then use the D compiler to compile the C object file together with the .d script(s) to generate the executable. However below the C and D scripts are compiled into separate object files. Then both object files are compiled together with the gcc compiler.

gcc - O - c example2c . c ldc2 - Oz - c - release - betterC - nogc example2d . d gcc example2c . o example2d . o - oexample2 ./ example2 # 12.000000 nm example2c . o # 0000000000000000 T _mult nm example2d . o # 0000000000000000 T _main # U _mult # U _printf

The code above is given here.

Calling D functions from C

Calling D from C is a less seamless affair because D has features that are not supported in C, however as before, -betterC can be used to create compatitable object files. The D code below creates multiplication functions of different types using string mixins. String mixins are a meta-programming technique that allows the user to generate code with strings at compile time.

/* example3d.d */ extern ( C ) nothrow @nogc @safe pure : /* Template to generate string */ template GenWrap ( string type ) { enum string GenWrap = type ~ " " ~ type [ 0 ] ~ "mult(" ~ type ~ " x, " ~ type ~ " y)

{

\treturn x*y;

}" ; } mixin ( GenWrap ! "double" ); mixin ( GenWrap ! "float" );

So mixin(GenWrap!"double") will expand to the code:

double dmult ( double x , double y ){ return x * y ; }

Instead of using quotes some users prefer using D’s q{} notation to represent strings, in this case, syntactic D code is held in the curly braces. We will be revisiting the use of mixins in the final example to generate Fortran code.

C code references and calls the functions created in D.

/* example3c.c */ #include <stdio.h> extern double dmult ( double x, double y); extern float fmult ( float x, float y);; int main () { double xd = 3.0 , yd = 4.0 ; float xf = 3.0 , yf = 4.0 ; printf( "output: %f

" , dmult(xd, yd)); printf( "output: %f

" , fmult(xf, yf)); return 0 ; }

To compile:

gcc -O -c example3c.c ldc2 -O -release -betterC -linkonce-templates -c example3d.d gcc example3c.o example3d.o -oexample3 ./example3 # output: 12.000000 # output: 12.000000 nm example3c.o # U _dmult # U _fmult # 0000000000000000 T _main # U _printf nm example3d.o # 0000000000000000 T _dmult # 0000000000000010 T _fmult

The above code is located here.

Calling Fortran code from D

Fortran subroutines can be called directly from D in a very similar way to calling static C libraries from D. If you are working with Fortran and D, it is a good idea to explore Fortran Iso C Binding documentation. Below is a Fortran subroutine equivalent of the multiplication function:

!example4f.f90 SUBROUTINE MULT(x, y) IMPLICIT NONE REAL * 8 , INTENT (IN) :: x REAL * 8 , INTENT (INOUT) :: y y = x * y END SUBROUTINE MULT

The main difference between calling C code and Fortran code from D is that the inputs to the Fortran subroutines must be referenced and the name of the function is mangled in the Fortran object file to include an underscore afterwards. C-style notation can be used double mult_(double* x, double* y); , however D provides ref notation which allows referenced inputs without requring pointers:

/* example4d.d */ extern ( C ) nothrow @nogc : double mult_ ( ref double x , ref double y ) @safe pure ; int printf ( scope const char * format , ...); int main (){ double x = 4 , y = 5 ; printf ( "%f

" , mult_ ( x , y )); return 0 ; }

Compilation is similar to calling C from D:

gfortran - c example4f.f90 ldc2 - ofexample4 example4d.d example4f.o && . / example4

The output from the Fortran object file showing the underscore mangled append name:

gfortran -c example4f.f90 nm example4f.o 0000000000000000 T mult_

The code for the above is located here.

This link is a useful resource for Fortran and this is useful for compilation hints.

Calling Fortran code from D with mixins

Consider a situation where Fortran subroutines of the same signature but with different names need to be called in D. D can be used to generate the necessary declarative and wrapper code to avoid repetitive code. As an example consider porting the the following trigonometric subroutines from Fortran:

!example5f.f90 SUBROUTINE SIN (x) IMPLICIT NONE REAL * 8 , INTENT (INOUT) :: x x = DSIN (x) END SUBROUTINE SIN SUBROUTINE COS (x) IMPLICIT NONE REAL * 8 , INTENT (INOUT) :: x x = DCOS (x) END SUBROUTINE COS SUBROUTINE TAN (x) IMPLICIT NONE REAL * 8 , INTENT (INOUT) :: x x = DTAN (x) END SUBROUTINE TAN SUBROUTINE ATAN (x) IMPLICIT NONE REAL * 8 , INTENT (INOUT) :: x x = DATAN (x) END SUBROUTINE ATAN

We can use templates to generate strings at compile-time. These strings are compile-time “interpreted” into code using string mixins and templates.

The first template is used to generate the declarations under extern (C) :

template Declare ( string fun ) { enum string Declare = "double " ~ fun ~ "_(ref double x) pure;" ; }

Declare!"sin" will generate the string double sin_(ref double x) pure; which declares the ported Fortran sin_ function. Next is the wrapper function to allow the user to use sin(x) rather than sin_(x) :

template Wrap ( string fun ) { enum string Wrap = "double " ~ fun ~ "(double x)

{

return " ~ fun ~ "_(x);

}" ; }

Then a template function that recursively concatenates the outputs for many functions to generate one string for all the functions is given below.

template GenFuns ( string [] funs , alias wrapper ) { static if ( funs . length > 0 ) enum string GenFuns = wrapper !( funs [ 0 ]) ~ GenFuns !( funs [ 1. . $ ], wrapper ); else enum string GenFuns = "" ; }

The complete D script for this is given below:

/* example5d.d */ template Declare ( string fun ) { enum string Declare = "double " ~ fun ~ "_(ref double x) pure;" ; } template Wrap ( string fun ) { enum string Wrap = "double " ~ fun ~ "(double x)

{

return " ~ fun ~ "_(x);

}" ; } template GenFuns ( string [] funs , alias wrapper ) { static if ( funs . length > 0 ) enum string GenFuns = wrapper !( funs [ 0 ]) ~ GenFuns !( funs [ 1. . $ ], wrapper ); else enum string GenFuns = "" ; } /* Name of the functions to be ported */ immutable ( string )[ 4 ] trigFuns = [ "sin" , "cos" , "tan" , "atan" ]; extern ( C ) nothrow @nogc { int printf ( scope const char * format , ...); mixin ( GenFuns !( trigFuns , Declare )); } mixin ( GenFuns !( trigFuns , Wrap )); extern ( C ): int main (){ double pii = 1 ; immutable double pi = 4 * atan ( pii ); assert ( sin ( pi / 2 ) == 1 ); assert ( cos ( 0 ) == 1 ); assert ( tan ( 0 ) == 0 ); printf ( "Yay!

" ); return 0 ; }

The make code is below:

example5 : example5d . d example5f . o ldc2 - O - release - betterC - linkonce - templates - oftrig example5d . d example5f . o example5f . o : example5f . f90 gfortran - O - c example5f . f90 . PHONY : clean clean : rm example5 *. o

Running make && ./example5 will return the output "Yay!" passing the correct assertions in the code.

The code for this section is given here.

Summary

This article shows that D can interface with C and Fortran simply and efficiently. In the case of calling C functions from D, there is minimal effort required in that the function(s) need only be declared under extern (C) . In the case of calling D code from C, efforts need to be made to remove features in D that are incompatible with C using -betterC and other appropriate flags.