Building a mixed C++ and D project
NOTE: This article is WIP!
In this tutorial we're going to look into how to build a project made of part written in C++ and D.
Compilation model
A typically program goes through the following phases:
- Write code -> source files (.d, .cpp and .h)
- Compile -> object files (.o or .obj)
- Link (object files and static libraries) -> executable
- Run the executable -> profit
Quite often compilers do steps 2) and 3) in a single command, but it is important to know the difference.
If something happens during 2) we say it happens at compile-time. If it happens during 3) - it happened at link-time and if it occurred during 4) - it was at run-time.
The input to the comiler (one or more source files) is known as a compilation unit (or translation unit in C++). The output of the compiler is called an object file. Object files contain relocatable machine code and debugging and linking metadata. Object files can be packaged in archives (static or dynamic libraries).
-> static or dynamic libraries (.a or .lib for static and .so or .lib for dynamic)
Often the compiler
Compiling
The compiler reads the .cpp or .d file(s) and produces object files or archive files (static libraries)
$ dmd -c dfile1.d
- Produces:
.d
->.obj
on Windows.d
->.o
on Linux
Linking
The linker reads the object and/or static library files produced by the compiler and produces a new object file or library or executable.
The compiler can
Consuming D libraries from C++ and vice-versa 101
DRuntime
A large portion of D's features depend on the D runtime. This means that DRuntime needs to be initialized before these features are used.
ABI
- To call C++ functions from D and vice-versa, they need to be declared in a particular way on both sides, so that C++ and D binaries are ABI-compatible.
- To pass objects back and forth you need to match the layout in their declarations on both sides.
- Generally there are 4 types of binaries:
- executables (like
.exe
or.com
on Windows) - object files (
.obj
on Windows and.o
on Linux) - static libraries (
.a
on Linux.lib
on Windows ) - dynamic libraries (
.dll
on Windows,.so
on Linux)
- executables (like
Program initialization
Typically, when the operating system starts an executable, the C runtime is first initialized before the control is transferred to the user C main()
function.
With D the process is similar. The difference is that the C main() is automatically generated and is used to initialize DRuntime. After the DRuntime is initialized the control is passed to the user D main()
function.
Examples
Consuming a D library with (no DRuntime and Phobos support) from C
Let's start with a simple D library that provides only a single function:
// ex1_d_library.d
module ex1_d_library;
extern (C) int add(int a, int b)
{
return a + b;
}
// Only needed on Linux. Read below from more info.
extern(C) void _d_dso_registry() {}
And a C program that calls it:
// ex1_c_main.c
#include <stdio.h>
int add(int, int);
int main()
{
int result = add(40, 2);
printf("The result is: %i\n", result);
}
Since we do not need DRuntime support for such a small library, we can use the -betterC
switch to tell the compiler to not produce references to DRuntime. That way we can avoid the need to link to DRuntime which will help to reduce the binary size.
Linux
On Linux the compiler generates code for shared libraries support that will call the _d_dso_registry
from DRuntime.
Since we don't need neither support for shared libraries, nor DRuntime, we can workaround this by adding an empty void _d_dso_registry()
function with extern (C)
linkage.
dmd -c ex1_d_library.d
# This should have produced a file named 'ex1_d_library.o'.
gcc ex1_c_main.cpp ex1_d_library.o -o ex1_prog
# The result should be an executable named 'ex1_prog'.
Windows 32-bit, Visual Studio 2015
# The commands below are tested in the VS2015 x86 Native Tools Command Prompt
dmd -lib -m32mscoff -betterC ex1_d_library.d
# This should have produced a file named 'ex1_d_library.obj'.
cl /nologo /Feex1_prog.exe ex1_c_main.c ex1_d_library.lib
# The result should be an executable named 'ex1_prog.exe'.
Windows 64-bit, Visual Studio 2015
# The commands below are tested in the VS2015 x64 Native Tools Command Prompt
dmd -lib -m64 -betterC ex1_d_library.d
# This should have produced a file named 'ex1_d_library.obj'.
cl /nologo /Feex1_prog.exe ex1_c_main.c ex1_d_library.lib
# The result should be an executable named 'ex1_prog.exe'.
////?? Currently, to produce a working library on Windows, the the library needs to have an empty main() function. We can tell the compiler to provide one for us by specifying the -main
switch.