Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Implementing a C API in D

Tags:

c

d

So there's plenty of information about calling C APIs from within D, but how about the reverse? What do you need to do to write a library in D that works like a normal C shared library? Here's an easy case:

main.c

extern int foo(int x);
void main() {
    printf("foo(5)=%d\n",foo(5));
}

foo.d

extern(C)
{
    int foo(int x)
    {
         return x*x;
    }
}

Naively trying to build and link these with gcc and dmd just results in linker errors.

Linking with gcc main.o foo.o:

doFoo.o: In function `no symbol':
doFoo.d:(.text+0x7): undefined reference to `_Dmodule_ref'
collect2: ld returned 1 exit status

Linking with dmd main.o foo.o:

/usr/lib64/libphobos2.a(deh2_2eb_525.o): In function `_D2rt4deh213__eh_finddataFPvZPS2rt4deh213DHandlerTable':
src/rt/deh2.d:(.text._D2rt4deh213__eh_finddataFPvZPS2rt4deh213DHandlerTable+0xa): undefined reference to `_deh_beg'
src/rt/deh2.d:(.text._D2rt4deh213__eh_finddataFPvZPS2rt4deh213DHandlerTable+0x14): undefined reference to `_deh_beg'
src/rt/deh2.d:(.text._D2rt4deh213__eh_finddataFPvZPS2rt4deh213DHandlerTable+0x1e): undefined reference to `_deh_end'
src/rt/deh2.d:(.text._D2rt4deh213__eh_finddataFPvZPS2rt4deh213DHandlerTable+0x46): undefined reference to `_deh_end'
/usr/lib64/libphobos2.a(lifetime.o): In function `_D2rt8lifetime18_sharedStaticCtor9FZv':
src/rt/lifetime.d:(.text._D2rt8lifetime18_sharedStaticCtor9FZv+0x15): undefined reference to `_tlsend'
src/rt/lifetime.d:(.text._D2rt8lifetime18_sharedStaticCtor9FZv+0x29): undefined reference to `_tlsstart'
/usr/lib64/libphobos2.a(thread_a3_258.o): In function `_D4core6thread6Thread6__ctorMFPFZvmZC4core6thread6Thread':
src/core/thread.d:(.text._D4core6thread6Thread6__ctorMFPFZvmZC4core6thread6Thread+0x2b): undefined reference to `_tlsend'
src/core/thread.d:(.text._D4core6thread6Thread6__ctorMFPFZvmZC4core6thread6Thread+0x36): undefined reference to `_tlsstart'
/usr/lib64/libphobos2.a(thread_a3_258.o): In function `_D4core6thread6Thread6__ctorMFDFZvmZC4core6thread6Thread':
src/core/thread.d:(.text._D4core6thread6Thread6__ctorMFDFZvmZC4core6thread6Thread+0x28): undefined reference to `_tlsend'
src/core/thread.d:(.text._D4core6thread6Thread6__ctorMFDFZvmZC4core6thread6Thread+0x33): undefined reference to `_tlsstart'
/usr/lib64/libphobos2.a(thread_a3_258.o): In function `_D4core6thread6Thread6__ctorMFZC4core6thread6Thread':
src/core/thread.d:(.text._D4core6thread6Thread6__ctorMFZC4core6thread6Thread+0x26): undefined reference to `_tlsend'
src/core/thread.d:(.text._D4core6thread6Thread6__ctorMFZC4core6thread6Thread+0x31): undefined reference to `_tlsstart'
/usr/lib64/libphobos2.a(thread_a0_713.o): In function `thread_entryPoint':
src/core/thread.d:(.text.thread_entryPoint+0x36): undefined reference to `_tlsend'
src/core/thread.d:(.text.thread_entryPoint+0x41): undefined reference to `_tlsstart'
collect2: ld returned 1 exit status
--- errorlevel 1
like image 851
sholte Avatar asked Sep 20 '11 04:09

sholte


People also ask

Can I use C for API?

C/C++ application programming interfaces (APIs) are used to access IBM® i resources. These APIs are intended primarily for C/C++ programmers. They are also called from other languages that support calling C-style APIs.

What is C API in Python?

The Python/C API allows for compiled pieces of code to be called from Python programs or executed within the CPython interpreter. This process of producing compiled code for use by CPython is generally known as "extending" Python and the compiled pieces of code to be used are known as "extension modules".


3 Answers

My answer is about using D static libraries from C. Yes, this is a bit off topic, but shared libraries for Windows are described in D's documentation (http://www.d-programming-language.org/dll.html) and for Linux are still under construction (http://www.digitalmars.com/d/2.0/changelog.html). Working examples for both systems are attached.

  • Win32: dmd+dmc works great. Example: test_d_from_c_win32.zip

  • Linux32: dmd adds some required stuff once it has found D main function, so D's main is needed (tested for dmd2+gcc on Linux32). It's linkage name is "_Dmain" and it will not be mixed with C's one (real "main"). So one can just add the file dfakemain.d with text void main(){}. dmd -c dfakemain.d will create dfakemain.o with missing symbols. Link it with your object files and you will be happy. Example: test_d_from_c_linux32.tar.gz

like image 121
Denis Avatar answered Oct 14 '22 05:10

Denis


According to a quick glance at the compiler source code, _Dmodule_ref is the linked list of module constructors. To fix the issue, add this to your main.c:

void* _Dmodule_ref;

The program now links and runs fine.

(At least, that's how I think it works.)

like image 40
Vladimir Panteleev Avatar answered Oct 14 '22 03:10

Vladimir Panteleev


If gcc is compiling as C++, the default linkage used for the extern will be C++, not C. Try this instead:

extern "C" int foo(int x);

There does not seem to be anything wrong with your D syntax. There is a paragraph confirming your approach here: http://www.digitalmars.com/d/2.0/interfaceToC.html

like image 30
memetech Avatar answered Oct 14 '22 05:10

memetech