Calling D from C

There's very little new to bring to the table when discussing how to call D from C. On the D side, any functions and global variables that should be available for C should be declared extern(C). You could use extern(Windows) or extern(System) for special cases, but generally extern(C)is what you want. On the C side, all that needs to be done is to create a standard C header with the appropriate type declarations and function prototypes, or global variables. As long as the linkage attributes are correct, any C code linked with the D code will think it's talking to C.

At the time of writing, shared library support for DMD is not complete. It isn't implemented at all on OS X. Now and again, someone asks for help in the forums when having trouble compiling shared libraries on other platforms. In the interest of saving space, we won't cover how to build them here. You can find information on how to compile shared libraries with DMD on Windows at http://wiki.dlang.org/Win32_DLLs_in_D and instructions for Linux at http://dlang.org/dll-linux.html#dso7. If you run into trouble, please head to the forums for help.

Regardless of how a D library is used in C, through static or dynamic linking, or manual loading, it is critical that DRuntime be initialized somewhere. When linking with static D libraries, this can be done on the C side. The runtime exposes two C functions, rt_init and rt_term, which do what needs to be done. Simply declare prototypes for them in C and call them during initialization and shutdown:

#include <stdlib.h>
#include <stdio.h>
extern int rt_init(void);
extern void rt_term(void);
void initialize() {
  // Initialize D runtime.
  if(rt_init() == 0) {
    fputs("Failed to initialize D runtime.", stderr);
    exit(EXIT_FAILURE);
  }
}
void terminate() {
  // Shutdown D runtime.
  rt_term();
}

When using D shared libraries on Linux, either through dynamic linking or manual loading, the C program can link with libphobos2.so and do the same thing. Multiple calls to rt_init are harmless, so don't worry about whether or not the shared libraries are doing the same.

When using D shared libraries on Windows, the responsibility to initialize the runtime lies with the DLLs, since they each have their own copy and there is no DLL version of Phobos. One way to do this is to give each DLL its own initialize and terminate functions, but it's probably easier just to do it in the DLLMain function. In this case, the D code has access to the Runtime struct, so it need not use rt_init or rt_term:

import core.sys.windows.windows;
extern (Windows)
BOOL DllMain(HINSTANCE hInstance, ULONG ulReason, LPVOID pvReserved)
{
  import core.runtime : Runtime;
  switch (ulReason)
  {
    case DLL_PROCESS_ATTACH:
      dll_process_attach( hInstance, true );
      Runtime.initialize();
      break;

    case DLL_PROCESS_DETACH:
      Runtime.terminate();
      dll_process_detach( hInstance, true );
      break;

    case DLL_THREAD_ATTACH:
      dll_thread_attach( true, true );
      break;

    case DLL_THREAD_DETACH:
      dll_thread_detach( true, true );
      break;

    default:
  }
  return true;
}

As the highlighted lines demonstrate, the calls should happen when handling the DLL_PROCESS_ATTACH and DLL_PROCESS_DETACH events. We only need to call the functions once per process, not once per thread. Again, if you make a mistake and do it in the _THREAD_ cases, no harm will be done.

Aside from managing the runtime, the only other thing that you should be cognizant of on the D side is how you handle pointers to memory allocated on the GC heap. Don't try to free memory in C that was allocated in D; don't store pointers to GC memory for the life of the program unless it really needs to be around that long; and so on. Most of the work that needs to be done to prevent crashes and unexpected behavior has to happen on the D side.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.133.126.199