www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - How To Call D Code from Objective C?

reply Mike McKee <volomike gmail.com> writes:
Is there a way to call D code from Objective C on OSX?

Here's something cool I was thinking about doing:

* I've learned how to make Cocoa UI Objective C apps on OSX.

* I've learned how to load the native OSX webkit widget in a 
Cocoa app. It provides a rich user interface using ordinary HTML, 
CSS, and Javascript/jQuery. It's much better than anything Qt has 
with its widgets or QML. It's much better than anything Apple 
allows with its other Aqua Cocoa widgets. I can make any kind of 
widget you can think of, colored and styled anyway I want, using 
webkit.

* I've learned how to enable a bridge between Javascript to 
Objective C. This lets me make an Objective C function that 
Javascript can call. So, if I need my webkit app to do something 
powerful like show a file chooser dialog, I can call an object 
called "objc" and so something like:

var sFilePath = objc.chooseFile();

* Now all I want to do is to bridge Objective C to D. That way, 
my webkit pages can call Objective C, which can then call D. I 
can put much of the heavy lifting work in D for my app. If I need 
to call an Apple Foundation Class library, I can have D call back 
to Objective C just for that special thing.
Dec 10 2015
next sibling parent reply Rikki Cattermole <alphaglosined gmail.com> writes:
On 11/12/15 7:01 PM, Mike McKee wrote:
 Is there a way to call D code from Objective C on OSX?

 Here's something cool I was thinking about doing:

 * I've learned how to make Cocoa UI Objective C apps on OSX.

 * I've learned how to load the native OSX webkit widget in a Cocoa app.
 It provides a rich user interface using ordinary HTML, CSS, and
 Javascript/jQuery. It's much better than anything Qt has with its
 widgets or QML. It's much better than anything Apple allows with its
 other Aqua Cocoa widgets. I can make any kind of widget you can think
 of, colored and styled anyway I want, using webkit.

 * I've learned how to enable a bridge between Javascript to Objective C.
 This lets me make an Objective C function that Javascript can call. So,
 if I need my webkit app to do something powerful like show a file
 chooser dialog, I can call an object called "objc" and so something like:

 var sFilePath = objc.chooseFile();

 * Now all I want to do is to bridge Objective C to D. That way, my
 webkit pages can call Objective C, which can then call D. I can put much
 of the heavy lifting work in D for my app. If I need to call an Apple
 Foundation Class library, I can have D call back to Objective C just for
 that special thing.
D.learn is more appropriate but here in the "easy" answer (ignoring extern(Objective-C)). Create a c wrapper. Example: https://github.com/Devisualization/window/blob/master/platforms/macosx/devisualization/window/window.d#L227 https://github.com/Devisualization/window/blob/master/cocoa_library/project/dwc-osx/dwc_cocoa.h https://github.com/Devisualization/window/blob/master/cocoa_library/project/dwc-osx/window.m
Dec 10 2015
parent reply Mike McKee <volomike gmail.com> writes:
Okay, so what has been shared is that the first step is to get C 
to call D, because if I can do that, then I can get Obj C to call 
C since there's no direct bridge to D. Obj C has a way to call C.

So, I found this article:

http://www.prowiki.org/wiki4d/wiki.cgi?DcalledFromC

I also found it's not quite up to date with the latest DMD. Here 
are some things I didn't have to do:

* If you scroll to the bottom, it talks about needing a main() in 
D. That's no longer necessary. So, the file in the example 
Dmaindummy.d is no longer required. This meant that I could edit 
Cmain.c and remove the print line there because it was irrelevant.

* One needs to use phobos2 instead of phobos. On Ubuntu 14.04, I 
did apt-get install dmd to get the D compiler, and then did 
apt-cache search libphobos to see which one was the latest it 
had, and then in my case I did apt-get install libphobos2-69, but 
your situation may vary.

* This changes the compilation a little, and I lowercased the 
filenames to make it easier for me:

$ dmd -c dfunc.d
$ gcc cmain.c dfunc.o -o ctest -lphobos2 -lpthread -lm

I then could call ctest with:

$ ./ctest

Note on OSX, the GCC line is a little different. If you've 
installed dmd from homebrew, it doesn't write to /usr/lib (that's 
locked down now) but does write to /usr/local/lib and 
/usr/local/include. So, you have to tell GCC to call that path 
with the -L parameter because it doesn't do that by default for 
some strange reason on OSX:

$ gcc cmain.c dfunc.o -o ctest -L/usr/local/lib -lphobos2 
-lpthread -lm

This then made it find /usr/local/lib/libphobos2.a with that 
-lphobos2 parameter.

So, then it worked on OSX too.

And now to figure out how to make a C library instead of an 
executable, and then how to load that compiled library in 
Objective C, load a header (.h file) in Objective C for that C 
function, and call the C function, which then calls the D 
function.

I'll update you if I figure all that out...
Dec 10 2015
parent Jacob Carlborg <doob me.com> writes:
On 2015-12-11 08:29, Mike McKee wrote:

 $ gcc cmain.c dfunc.o -o ctest -L/usr/local/lib -lphobos2 -lpthread -lm
I recommend using the D compiler to compile the final application. Then you don't need to worry about all these extra flags and paths. -- /Jacob Carlborg
Dec 10 2015
prev sibling parent reply Jacob Carlborg <doob me.com> writes:
On 2015-12-11 07:01, Mike McKee wrote:
 Is there a way to call D code from Objective C on OSX?
You can call Objective-C from D [1]. For the other way around, you currently need to use the API for the Objective-C runtime [2].
 Here's something cool I was thinking about doing:

 * I've learned how to make Cocoa UI Objective C apps on OSX.

 * I've learned how to load the native OSX webkit widget in a Cocoa app.
 It provides a rich user interface using ordinary HTML, CSS, and
 Javascript/jQuery. It's much better than anything Qt has with its
 widgets or QML. It's much better than anything Apple allows with its
 other Aqua Cocoa widgets. I can make any kind of widget you can think
 of, colored and styled anyway I want, using webkit.

 * I've learned how to enable a bridge between Javascript to Objective C.
 This lets me make an Objective C function that Javascript can call. So,
 if I need my webkit app to do something powerful like show a file
 chooser dialog, I can call an object called "objc" and so something like:

 var sFilePath = objc.chooseFile();
Are these Objective-C functions or C functions?
 * Now all I want to do is to bridge Objective C to D. That way, my
 webkit pages can call Objective C, which can then call D. I can put much
 of the heavy lifting work in D for my app. If I need to call an Apple
 Foundation Class library, I can have D call back to Objective C just for
 that special thing.
You can call the Foundation classes directly from D. [1] http://dlang.org/spec/objc_interface.html [2] https://developer.apple.com/library/mac/documentation/Cocoa/Reference/ObjCRuntimeRef/ -- /Jacob Carlborg
Dec 10 2015
parent reply Mike McKee <volomike gmail.com> writes:
I found a way to call C from in Objective C. The big trick is to 
rename a .m file to a .mm file. So, I think there's probably a 
way for me to link a compiled C dylib into Objective C and then 
load its .h header file so that Objective C can call those C 
functions.

I'll be using GCC to statically combine the D's .o code in with 
the C code.

So, I'm thinking the process is like this:

1. Create a D function d_test() that takes a string input, 
concatenates on "-response", and returns the result. Compile as 
dtest.o with "dmd -c dtest.d".

2. Create a C function c_test() that takes a string input, calls 
d_test() and passes it the string, and then returns the response 
from d_test() out of c_test() as a string result. Compile as 
ctest.dylib with "gcc -dynamiclib -o ctest.dylib ctest.c dtest.o 
-L/usr/local/lib -lphobos2 -lpthread -lm".

3. In Xcode IDE, add this ctest.dylib linked library. Then, 
create a ctest.h for the function declaration.

4. In Objective C, import ctest.h in my main.mm file and then 
call with something like NSLog("RESULT=%s",c_test("request"));  
That should create a debugger line that reads: 
RESULT=request-response.

The problem is that I don't know the entire way that I should 
create dtest.d and ctest.c.
Dec 11 2015
next sibling parent reply Mike McKee <volomike gmail.com> writes:
So I'm having trouble figuring out the D and C code.

Created dfunc.d with this:

extern (C) string dfunc(string s) {
         return s ~ "response";
}

Then compiled:

$ dmd -c dfunc.d

This created dfunc.o without error.

Next, I created C code like so:

extern char * dfunc(char *);
char * c_dfunc(char *s) {
   return dfunc(s);
}

But when I try to compile on Linux (as a test -- I could have 
done this slightly differently on OSX), it throws this error:

$ gcc -dynamiclib -o cfunc.o cfunc.c dfunc.o -L/usr/local/lib 
-lphobos2 -lpthread -lm
cc1: warning: unrecognized gcc debugging option: y [enabled by 
default]
cc1: warning: unrecognized gcc debugging option: n [enabled by 
default]
cc1: warning: unrecognized gcc debugging option: m [enabled by 
default]
cc1: warning: unrecognized gcc debugging option: i [enabled by 
default]
cc1: warning: unrecognized gcc debugging option: c [enabled by 
default]
cc1: warning: unrecognized gcc debugging option: l [enabled by 
default]
cc1: warning: unrecognized gcc debugging option: i [enabled by 
default]
cc1: warning: unrecognized gcc debugging option: b [enabled by 
default]
/usr/bin/ld: 
/usr/lib/debug/usr/lib/i386-linux-gnu/crt1.o(.debug_info): 
relocation 0 has invalid symbol index 11
/usr/bin/ld: 
/usr/lib/debug/usr/lib/i386-linux-gnu/crt1.o(.debug_info): 
relocation 1 has invalid symbol index 12
/usr/bin/ld: 
/usr/lib/debug/usr/lib/i386-linux-gnu/crt1.o(.debug_info): 
relocation 2 has invalid symbol index 2
/usr/bin/ld: 
/usr/lib/debug/usr/lib/i386-linux-gnu/crt1.o(.debug_info): 
relocation 3 has invalid symbol index 2
/usr/bin/ld: 
/usr/lib/debug/usr/lib/i386-linux-gnu/crt1.o(.debug_info): 
relocation 4 has invalid symbol index 11
/usr/bin/ld: 
/usr/lib/debug/usr/lib/i386-linux-gnu/crt1.o(.debug_info): 
relocation 5 has invalid symbol index 13
/usr/bin/ld: 
/usr/lib/debug/usr/lib/i386-linux-gnu/crt1.o(.debug_info): 
relocation 6 has invalid symbol index 13
/usr/bin/ld: 
/usr/lib/debug/usr/lib/i386-linux-gnu/crt1.o(.debug_info): 
relocation 7 has invalid symbol index 13
/usr/bin/ld: 
/usr/lib/debug/usr/lib/i386-linux-gnu/crt1.o(.debug_info): 
relocation 8 has invalid symbol index 12
/usr/bin/ld: 
/usr/lib/debug/usr/lib/i386-linux-gnu/crt1.o(.debug_info): 
relocation 9 has invalid symbol index 13
/usr/bin/ld: 
/usr/lib/debug/usr/lib/i386-linux-gnu/crt1.o(.debug_info): 
relocation 10 has invalid symbol index 13
/usr/bin/ld: 
/usr/lib/debug/usr/lib/i386-linux-gnu/crt1.o(.debug_info): 
relocation 11 has invalid symbol index 13
/usr/bin/ld: 
/usr/lib/debug/usr/lib/i386-linux-gnu/crt1.o(.debug_info): 
relocation 12 has invalid symbol index 13
/usr/bin/ld: 
/usr/lib/debug/usr/lib/i386-linux-gnu/crt1.o(.debug_info): 
relocation 13 has invalid symbol index 13
/usr/bin/ld: 
/usr/lib/debug/usr/lib/i386-linux-gnu/crt1.o(.debug_info): 
relocation 14 has invalid symbol index 13
/usr/bin/ld: 
/usr/lib/debug/usr/lib/i386-linux-gnu/crt1.o(.debug_info): 
relocation 15 has invalid symbol index 13
/usr/bin/ld: 
/usr/lib/debug/usr/lib/i386-linux-gnu/crt1.o(.debug_info): 
relocation 16 has invalid symbol index 13
/usr/bin/ld: 
/usr/lib/debug/usr/lib/i386-linux-gnu/crt1.o(.debug_info): 
relocation 17 has invalid symbol index 13
/usr/bin/ld: 
/usr/lib/debug/usr/lib/i386-linux-gnu/crt1.o(.debug_info): 
relocation 18 has invalid symbol index 13
/usr/bin/ld: 
/usr/lib/debug/usr/lib/i386-linux-gnu/crt1.o(.debug_info): 
relocation 19 has invalid symbol index 13
/usr/bin/ld: 
/usr/lib/debug/usr/lib/i386-linux-gnu/crt1.o(.debug_info): 
relocation 20 has invalid symbol index 13
/usr/bin/ld: 
/usr/lib/debug/usr/lib/i386-linux-gnu/crt1.o(.debug_info): 
relocation 21 has invalid symbol index 22
/usr/bin/ld: 
/usr/lib/debug/usr/lib/i386-linux-gnu/crt1.o(.debug_line): 
relocation 0 has invalid symbol index 2
/usr/lib/gcc/i686-linux-gnu/4.8/../../../i386-linux-gnu/crt1.o: 
In function `_start':
(.text+0x18): undefined reference to `main'
collect2: error: ld returned 1 exit status
Dec 11 2015
next sibling parent reply Mike McKee <volomike gmail.com> writes:
Well, part of it was that I needed to do -shared on Linux instead 
of -dynamiclib. On OSX, I have to use -dynamiclib.

It compiles now on the C code, but when I created a cmain.c to 
test the library, it gave me a segmentation fault when trying to 
run this compiled program, even though I copied gfunc.o and 
cfunc.o to /usr/lib on Linux.

// cmain.c
#include <stdio.h>
extern char * c_dfunc(char *s);

void main() {
         printf("%s\n",c_dfunc("request"));
}
Dec 11 2015
parent Jacob Carlborg <doob me.com> writes:
On 2015-12-11 10:18, Mike McKee wrote:
 Well, part of it was that I needed to do -shared on Linux instead of
 -dynamiclib. On OSX, I have to use -dynamiclib.

 It compiles now on the C code, but when I created a cmain.c to test the
 library, it gave me a segmentation fault when trying to run this
 compiled program, even though I copied gfunc.o and cfunc.o to /usr/lib
 on Linux.

 // cmain.c
 #include <stdio.h>
 extern char * c_dfunc(char *s);

 void main() {
          printf("%s\n",c_dfunc("request"));
 }
There are several issues: 1. The D function needs to use C compatible types, see my answer to your previous post 2. You need to initialize the D runtime from the C side 3. If you allocate data in D using the GC and pass it to the C side, you need to make sure there's a reference to it on the D side. Otherwise the GC could collect the memory 4. Dynamic libraries are not yet supported on OS X I recommend asking these question in the learning forum. As a first step you need to learn how to interact between D and C. You can get a lot of answers for that on the learning forum, tutorials, documentation, books and so on. -- /Jacob Carlborg
Dec 11 2015
prev sibling parent Jacob Carlborg <doob me.com> writes:
On 2015-12-11 09:51, Mike McKee wrote:
 So I'm having trouble figuring out the D and C code.

 Created dfunc.d with this:

 extern (C) string dfunc(string s) {
          return s ~ "response";
 }

 Then compiled:

 $ dmd -c dfunc.d

 This created dfunc.o without error.

 Next, I created C code like so:

 extern char * dfunc(char *);
 char * c_dfunc(char *s) {
    return dfunc(s);
 }
"string" in D is not the same as "char*" in C. "string" is an alias to an array of immutable characters. An array in D consists of the length of the array and a pointer to the data. The interface of the function needs to only contain C types. Something like this: extern (C) char* dfunc(char* s); If you want to use the D string operations, like ~, you need to convert it to a D string, do the concatenation, and then convert it back to a C string [2] [3]. See [3] for more information. [3] http://dlang.org/spec/interfaceToC.html -- /Jacob Carlborg
Dec 11 2015
prev sibling parent Jacob Carlborg <doob me.com> writes:
On 2015-12-11 09:20, Mike McKee wrote:

 I found a way to call C from in Objective C. The big trick is to rename
 a .m file to a .mm file.
.mm means Objective-C++. That is, combining C++ and Objective-C in the same file. Objective-C can call C functions directly with no problems. A global function in a .m file is a C function. NSLog, for example is a regular C function.
 So, I think there's probably a way for me to
 link a compiled C dylib into Objective C and then load its .h header
 file so that Objective C can call those C functions.

 I'll be using GCC to statically combine the D's .o code in with the C code.

 So, I'm thinking the process is like this:

 1. Create a D function d_test() that takes a string input, concatenates
 on "-response", and returns the result. Compile as dtest.o with "dmd -c
 dtest.d".

 2. Create a C function c_test() that takes a string input, calls
 d_test() and passes it the string, and then returns the response from
 d_test() out of c_test() as a string result. Compile as ctest.dylib with
 "gcc -dynamiclib -o ctest.dylib ctest.c dtest.o -L/usr/local/lib
 -lphobos2 -lpthread -lm".
This step is not necessary. You can call the D function directly without needing a wrapper.
 3. In Xcode IDE, add this ctest.dylib linked library. Then, create a
 ctest.h for the function declaration.

 4. In Objective C, import ctest.h in my main.mm file and then call with
 something like NSLog("RESULT=%s",c_test("request")); That should create
 a debugger line that reads: RESULT=request-response.

 The problem is that I don't know the entire way that I should create
 dtest.d and ctest.c.
-- /Jacob Carlborg
Dec 11 2015