www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - Dynamically calling external libraries.

reply "Setra" <roederharpo hushmail.com> writes:
Hello all! I am in the process of creating a programming
language, and I have completed parsing for most of the actual
language (it is interpreted). I do not however have a way to
print to the screen yet. This is because I want to use external
libraries. My question is how in D can I call a external library
that is specified at runtime not compile time? I want the
scripting language to be able to specifiy a library to load /
call, and then do so while running. Any thoughts of how to do
this?

Thanks! -Setra
Feb 26 2014
parent reply "Adam D. Ruppe" <destructionator gmail.com> writes:
You'd do it the same way you do in C. On Windows, call 
LoadLibrary, FreeLibrary, and GetProcAddress or the COM 
functions. On Linux, the family of functions is dlopen, dlsym, 
and dlclose.

Knowing the types to pass the functions is gonna be tricky and 
this needs to be right to avoid crashes. On Windows with 
scripting language, this is often done through COM automation: 
the IDispatch interface. With regular C functions, you really 
just have to know the prototypes ahead of time... it won't be 
fully dynamic, you load the library at run time but know how to 
use it at compile time.
Feb 26 2014
parent reply "Mike James" <foo bar.com> writes:
On Wednesday, 26 February 2014 at 14:41:02 UTC, Adam D. Ruppe 
wrote:
 You'd do it the same way you do in C. On Windows, call 
 LoadLibrary, FreeLibrary, and GetProcAddress or the COM 
 functions. On Linux, the family of functions is dlopen, dlsym, 
 and dlclose.

 Knowing the types to pass the functions is gonna be tricky and 
 this needs to be right to avoid crashes. On Windows with 
 scripting language, this is often done through COM automation: 
 the IDispatch interface. With regular C functions, you really 
 just have to know the prototypes ahead of time... it won't be 
 fully dynamic, you load the library at run time but know how to 
 use it at compile time.
That's the way I do it but I was wondering. Is it better to load all the functions from the DLL at the start of the program or load them when required and keep having to check if they're loaded before each use? -<Mike>-
Feb 26 2014
parent reply "John Colvin" <john.loughran.colvin gmail.com> writes:
On Wednesday, 26 February 2014 at 17:58:46 UTC, Mike James wrote:
 On Wednesday, 26 February 2014 at 14:41:02 UTC, Adam D. Ruppe 
 wrote:
 You'd do it the same way you do in C. On Windows, call 
 LoadLibrary, FreeLibrary, and GetProcAddress or the COM 
 functions. On Linux, the family of functions is dlopen, dlsym, 
 and dlclose.

 Knowing the types to pass the functions is gonna be tricky and 
 this needs to be right to avoid crashes. On Windows with 
 scripting language, this is often done through COM automation: 
 the IDispatch interface. With regular C functions, you really 
 just have to know the prototypes ahead of time... it won't be 
 fully dynamic, you load the library at run time but know how 
 to use it at compile time.
That's the way I do it but I was wondering. Is it better to load all the functions from the DLL at the start of the program or load them when required and keep having to check if they're loaded before each use? -<Mike>-
Normally one loads it at the start. In the case of an optional feature depending on a shared library, you can always set a global flag (probably shared/__gshared) to say whether the load was successful and use that later on to check.
Feb 26 2014
parent reply "Setra" <roederharpo hushmail.com> writes:
I have found the Dlang wiki article on how to do it. 
http://dlang.org/dll-linux.html

I am using the following code.

[code]
import core.stdc.stdio;
import core.stdc.stdlib;
import core.sys.posix.dlfcn;

extern (C) int dll();

int main() {
   printf("+main()\n");

   void *lh = dlopen("libdll.so", RTLD_LAZY);
   if (!lh) {
     fprintf(stderr, "dlopen error: %s\n", dlerror());
     exit(1);
   }
   printf("libdll.so is loaded\n");

   int function(int x) fn = cast(int function(int x))dlsym(lh, 
"dll");

   printf("dll() function is found\n");

   fn(72);

   printf("unloading libdll.so\n");
   dlclose(lh);

   printf("-main()\n");
   return 0;
}
[\code]

[code]
import core.stdc.stdio;
import std.stdio;

extern (C) int dll(int x) {
   printf("dll()\n");
	writeln(x);
   return 0;
}
[\code]

I am compiling it the way to guide recomends it. It compiles 
however for some reason when it runs the value returned by the 
dll is incorrect? Does anyone know why? It always returns the 
same value even when I change the parameter.
Thanks!
Feb 27 2014
parent reply "Marc =?UTF-8?B?U2Now7x0eiI=?= <schuetzm gmx.net> writes:
On Thursday, 27 February 2014 at 14:38:11 UTC, Setra wrote:
 I am compiling it the way to guide recomends it. It compiles 
 however for some reason when it runs the value returned by the 
 dll is incorrect? Does anyone know why? It always returns the 
 same value even when I change the parameter.
What do you mean by "returns"? You're not actually printing the return value of fn(). Are you referring to the value printed in dll()? For me this works (openSUSE 13.1, DMD git master): # dmd main1.d -L-ldl # dmd -c dll.d -fPIC # dmd -oflibdll.so dll.o -shared -defaultlib= # LD_LIBRARY_PATH=. ./main1 +main() libdll.so is loaded dll() function is found dll() 72 unloading libdll.so -main() (I had to remove the "import std.stdio" and change the writeln() to printf("%d", x) to get it to link because of an undefined symbol.)
Feb 27 2014
parent reply "Setra" <roederharpo hushmail.com> writes:
By returns I ment what it wrote to the screen. I realise now that 
that was a bad word to use... Every time I run it I get the value 
134515512. Here is my compile script (Note I edited my code to 
use the same method of printing that you did and got the same 
result):

dmd -c dll.d -fPIC
dmd -oflibdll.so dll.o -shared -defaultlib=libphobos2.so 
-L-rpath=/path/to/where/shared/library/is

dmd -c main.d
dmd main.o -L-ldl -defaultlib=libphobos2.so 
-L-rpath=.:/path/to/where/shared/library/is -map

./main

Thanks!
Feb 27 2014
parent reply "Setra" <roederharpo hushmail.com> writes:
Marc would you be willing to post your compile script? Or did you 
just use what the wiki said to?
Feb 28 2014
parent reply "Marc =?UTF-8?B?U2Now7x0eiI=?= <schuetzm gmx.net> writes:
On Friday, 28 February 2014 at 16:44:02 UTC, Setra wrote:
 Marc would you be willing to post your compile script? Or did 
 you just use what the wiki said to?
I used exactly what I quoted in my post: dmd main1.d -L-ldl dmd -c dll.d -fPIC dmd -oflibdll.so dll.o -shared -defaultlib= LD_LIBRARY_PATH=. ./main1 But what you just wrote works correctly, too. Are you sure you're actually using the correct object files? I.e. try deleting all output files first. Sometimes it's really trivialities like this :-) Or it's an architectural problem? I'm on x86_64, maybe you're compiling 32bit code (although of course that should work, but you can never know)? Which version of DMD are you using? Otherwise you could try disassembling things or single-step through the call with a debugger. But maybe someone else knows something better?
Feb 28 2014
parent reply "Setra" <roederharpo hushmail.com> writes:
I am using  dmd_2.065.0-0_i386.deb on a 32 bit machine.
Feb 28 2014
next sibling parent "Tolga Cakiroglu" <tcak pcak.com> writes:
On Friday, 28 February 2014 at 17:29:09 UTC, Setra wrote:
 I am using  dmd_2.065.0-0_i386.deb on a 32 bit machine.
Hi Setra, I am trying to make external libraries work as well, but having problems continuously. Could you test that following function in your library and call it from main programme. I am having "Segmentation Fault" with it, and wondering if other people has the same thing. class A{ public this(){ writeln("Created"); } } extern(C) void foo(){ Object obj = new Object(); A objA = new A(); char[] c = new char[ 1024 ]; }
Feb 28 2014
prev sibling parent reply "Marc =?UTF-8?B?U2Now7x0eiI=?= <schuetzm gmx.net> writes:
On Friday, 28 February 2014 at 17:29:09 UTC, Setra wrote:
 I am using  dmd_2.065.0-0_i386.deb on a 32 bit machine.
Ah, I can reproduce it with 32bit executables: # dmd -m32 main1.d -L-ldl # dmd -m32 -c dll.d -fPIC # dmd -m32 -oflibdll.so dll.o -shared -defaultlib= # LD_LIBRARY_PATH=. ./main1 +main() libdll.so is loaded dll() function is found dll() 134684056 unloading libdll.so -main() # file ./main1 ./main1: ELF 32-bit LSB executable, Intel 80386, version 1 (SYSV), dynamically linked (uses shared libs), for GNU/Linux 2.6.16, BuildID[sha1]=a0eb9b8af3a95d0c4fddd2daf7a476aabed4b53e, not stripped Might be a compiler bug...
Feb 28 2014
parent reply "Marc =?UTF-8?B?U2Now7x0eiI=?= <schuetzm gmx.net> writes:
On Friday, 28 February 2014 at 18:05:13 UTC, Marc Sch├╝tz wrote:
 On Friday, 28 February 2014 at 17:29:09 UTC, Setra wrote:
 I am using  dmd_2.065.0-0_i386.deb on a 32 bit machine.
Ah, I can reproduce it with 32bit executables: # dmd -m32 main1.d -L-ldl # dmd -m32 -c dll.d -fPIC # dmd -m32 -oflibdll.so dll.o -shared -defaultlib= # LD_LIBRARY_PATH=. ./main1 +main() libdll.so is loaded dll() function is found dll() 134684056 unloading libdll.so -main() # file ./main1 ./main1: ELF 32-bit LSB executable, Intel 80386, version 1 (SYSV), dynamically linked (uses shared libs), for GNU/Linux 2.6.16, BuildID[sha1]=a0eb9b8af3a95d0c4fddd2daf7a476aabed4b53e, not stripped Might be a compiler bug...
No, it isn't. The problem is here: int function(int x) fn = cast(int function(int x))dlsym(lh, "dll"); This should be: extern(C) int function(int x) fn = cast(int function(int x))dlsym(lh, "dll"); The calling conventions for C and D are different on x86, but happen to agree on x86_64 (at least the parts that are relevant here). Btw, the forward declaration on top "extern (C) int dll();" isn't used anywhere.
Feb 28 2014
parent "Setra" <roederharpo hushmail.com> writes:
Thanks! That was perfect!
Feb 28 2014