Do you know how to be able to call C/C++ functions from Java? Wrappers...you have to create these things called "wrappers". Basically what it does is connects your C/C++ codes and your Java codes through some shared objects. "Shared objects? What in the world is that?? How am I supposed to know how to make 'shared objects'??" you ask? I had the same question. Luckily, there are some very nice fellows out there that provide us with a piece of software that is called an interface compiler, which... you guessed it, CREATES THOSE SHARED OBJECTS (and other necessary files) for us to use. Salute to them, I say! One good example of it (and the one I am using) is called SWIG (www.swig.org).
Unfortunately, I don't remember how to install SWIG on my Ubuntu 6.10 system. But I would like to share the problem I experienced in getting my Java code to call the C/C++ functions using SWIG.
To test, I used the example in the SWIG website tutorial page (www.swig.org/tutorial.html). So here goes:
You will have to write the codes for 3 files:
1. The C/C++ code (*.c) which contains the functions you wish to call from another language (in my case, Java).
2. The interface file (*.i) which is the input file for SWIG.
3. The other language code (in my case, *.java) which will call the C/C++ functions in *.c file in 1.
All the other files (shared objects, additional .c files, .java files) will be generated by SWIG. Please refer to the SWIG tutorial page for other languages, as I don't have any experience with SWIG and Python or Perl for example.
First, create your C/C++ file example.c:
/* File : example.c */Ok, nothing specific to point out there. It's just a straight forward C code, copied directly from the SWIG tutorial page. In fact, I'm pretty sure there's nothing in there that's specified for preparing it for the SWIG compilation. This next file, however, IS important; it is the interface file that SWIG will use to create the files necessary to make the wrapper.
#include
double My_variable = 3.0;
int fact(int n) {
if (n <= 1) return 1; else return n*fact(n-1); } int my_mod(int x, int y) { return (x%y); } char *get_time() { time_t ltime; time(<ime); return ctime(<ime); }
/* example.i */So, if I understand it correctly, the interface file (example.i) declares all the variables and functions used in the C/C++ file (example.c). Another way to do it, is to write the header file there instead of the variables and functions one-by-one. So here's another example for the interface file using header file from the same SWIG tutorial page:
%module example
%{
/* Put header files here or function declarations like below */
extern double My_variable;
extern int fact(int n);
extern int my_mod(int x, int y);
extern char *get_time();
%}
extern double My_variable;
extern int fact(int n);
extern int my_mod(int x, int y);
extern char *get_time();
/* example.i */So, you can use either type of interface file (depends on whether or not you have the header (*.h) file). As you can see, in both formats, you have to declare the variables/functions or header file twice: once within the % and curly brackets ({}), and another just preceded with the % sign (for the one with the header file). Note that when declaring the variables/functions, they must be preceded with the extern keyword. Here's a little thing about the extern keyword...
%module example
%{
/* Includes the header in the wrapper code */
#include "header.h"
%}
/* Parse the header file to generate wrappers */
%include "header.h"
According to one definition:
External References: extern
- If a variable is declared (with global scope) in one file but referenced in another, the
extern
keyword is used to inform the compiler of the variable's existence:- In declare.c:
int farvar;
- In use.c:
{
extern int farvar;
int a;
a = farvar * 2;
}
- In declare.c:
- Note that the
extern
keyword is for declarations, not definitions- An
extern
declaration does not create any storage; that must be done with a global definition
- An
And another:
extern
The extern keyword is used to inform the compiler about variables declared outside of the current scope. Variables described by extern statements will not have any space allocated for them, as they should be properly defined elsewhere.
Extern statements are frequently used to allow data to span the scope of multiple files.
(from: http://www.cppreference.com/keywords/extern.html)
So, it has got something to do for variables that are being used in one code (e.g. iUseVariableA.c), which are declared in another code (e.g. iDeclareVariableA.h or iDeclareVariableA.i). Is it something like "universal" (across several codes/files) variable for the codes that are being compiled? I don't know how to explain it appropriately, so if anyone can help me explain it in plain english, that'll be awesome.
Ok, let's move on to the SWIG-ing (note to self: got to find a better way to say it... that sounded weird). So, the third, and final file you have to make is the other language (in my case, Java) where you have to call the C/C++ functions.
/* main.java */See how you kind of calls the getMy_variable(), fact(int), and get_time() functions that were defined in the example.c by doing example.getMy_variable(), example.fact(5), example.get_time()? Seems pretty straightforward.
public class main {
public static void main(String argv[]) {
System.loadLibrary("example");
System.out.println(example.getMy_variable());
System.out.println(example.fact(5));
System.out.println(example.get_time());
}
}
Now come the exciting part... compilation!
Here's how it was explained in SWIG tutorial page for compiling for Java module (all these are done in Terminal):
$ swig -java example.iUh-oh... yeah, after reading more carefully, I realized that this example was for Cygwin/Windows. You can figure it out by the .../win32 include directory. And more importantly, see the third line, where the output is example.dll, a Windows Dynamic Linked Library (you know it's the output file because it was preceded by the '-o' command). So, what about for Ubuntu (Linux) users like me? Fear not! There is salvation, yet.
$ gcc -c example.c example_wrap.c -I/c/jdk1.3.1/include -I/c/jdk1.3.1/include/win32
$ gcc -shared example.o example_wrap.o -mno-cygwin -Wl,--add-stdcall-alias -o example.dll
After digging deeper into the SWIG website (and unfortunately, couldn't find exactly what I was looking for through searching the forums), I found this documentation for Linux:
$ swig -java example.i(from: http://www.dabeaz.com/cgi-bin/wiki.pl?SwigFaq/Java)
$ gcc -fpic -c example.c example_wrap.c -I/usr/java/j2sdk1.4.2/include -I/usr/java/j2sdk1.4.2/include/linux
$ gcc -shared example.o example_wrap.o -o libexample.so
$ javac main.java
$ java main
So, first you do (everything here are done in Terminal):
$ swig -java example.i
which tells the SWIG compiler to create a Java module, with the input interface file: example.i
Cool. If it works, SWIG then creates several files in the same directory:
example.java
example.class
example.o
exampleJNI.java
exampleJNI.class
example_wrap.c
example_wrap.o
For the most part, you won't be modifying these files. You can try, but DO IT AT YOUR OWN RISK; make sure you know what you're doing :) As a side note: if you've never done Java programming, the *.class files are the compiled *.java files. So the source codes are in *.java files.
Allrighty, next let's compile the *.c files that we have: example.c and example_wrap.c (one of the result files from SWIG compilation).
$ gcc -fpic -c example.c example_wrap.c -I/usr/java/j2sdk1.4.2/include -I/usr/java/j2sdk1.4.2/include/linux
I don't know if it will make any difference if you use g++ or other C/C++ compiler rather than gcc. I used gcc and it worked fine. So, the thing to keep in mind is the -I include paths; you have to find for yourself where those java***/include and java***/include/linux are located. Mine are located in: /usr/lib/jvm/java-1.5.0-sun/include and /usr/lib/jvm/java-1.5.0-sun/include/linux, respectively, since I used Java SDK 1.5 (which I installed through Synaptic Package Manager). So my actual command looked like this:
$ gcc -fpic -c example.c example_wrap.c -I /usr/lib/jvm/java-1.5.0-sun/include -I /usr/lib/jvm/java-1.5.0-sun/include/linux
Don't forget the .../linux include directory, otherwise it will give some nasty errors. OK, that should compile nicely. Next step, compile and create the shared object.
$ gcc -shared example.o example_wrap.o -o libexample.soNothing to be changed here. So basically the compiler will take the object file (note the .o extension) example.o and example_wrap.o, and creates the output file which is the shared object libexample.so (i.e. library -- hence the .so extension). IMPORTANT: Note that for Linux, the output file must start with lib*** (in this case: lib+example = libexample.so), otherwise it won't work.
So now you'll have these files:
example.java
example.class
example.o
exampleJNI.java
exampleJNI.class
example_wrap.c
example_wrap.o
libexample.so
Can you see where we're going with this? We now have a example.class.... which means, now we can have a main Java routine (method, function, whatever) that instantiates (creates) the object named 'example' and thus can access the variables and/or functions (methods) of the object 'example', which variables and functions are actually defined in example.c.
So, let's create that Java routine that calls those lovely functions in example.c (again, this is from the SWIG tutorial page):
/* main.java */In case you're wondering, yeah, I already shown this code. But now it should be clearer; you can notice that we're calling the example functions by: "example.
public class main {
public static void main(String argv[]) {
System.loadLibrary("example");
System.out.println(example.getMy_variable());
System.out.println(example.fact(5));
System.out.println(example.get_time());
}
}
$ javac main.javaNow, here's one part where I had some problems. I got these error messages:
Exception in thread "main" java.lang.UnsatisfiedLinkError: no example in java.library.path
Unfortunately, I didn't do the troubleshooting systematically, that I couldn't really tell which steps fixed the problem, and which don't. But, there is one more step that was not explicitly said in the SWIG tutorial. There are some conversations in some forums (alas, I forgot the link) that said you HAVE TO set the LD_LIBRARY_PATH to where your .so file is. You can do:
export LD_LIBRARY_PATH=
(Don't use the brackets "<>") Apparently, that error is one that most commonly occurs. I think it should be safe to set that path before you compile the .java file, but my experience with it is that I set that path AFTER I compiled the example.i with swig, gcc the *.c files, and the gcc -shared stuff, and I kept getting error messages during compilation. So I deleted all the files that were generated by SWIG, and recompiled the whole thing from scratch. After the javac compilation was successful, I ran the main.class with:
$ java mainAnd it worked!
$ java main
3.0
120
Fri Nov 9 00:17:37 2007
Hooray!!! Unfortunately, I'm still not sure what's the proper procedure for an error-free, trouble-free, headache-free compilation.
Another thing I want to add is, be careful when you're writing your Java program in packages. I gave packages names on my main.java, example.java, and exampleJNI.java, and here's the error that I got:
$ javac main.java
main.java:8: cannot find symbol
symbol : variable example
location: class facerecognition.main
System.out.println(example.getMy_variable());
^
main.java:9: cannot find symbol
symbol : variable example
location: class facerecognition.main
System.out.println(example.fact(5));
^
main.java:10: cannot find symbol
symbol : variable example
location: class facerecognition.main
System.out.println(example.get_time());
^
3 errors
Unfortunately, I have limited knowledge in compiling Java from the Terminal (I've been using Eclipse). I think if using packages, compiling requires additional arguments:
$ javac -package [package name] [main program].java
I think. If anyone can fill me in on that, I'll really appreciate it.
So there you have it, my experience making wrappers for C/C++ for Java using SWIG. Now I have to create a GUI in Java for application using OpenCV...
Some useful links:
http://www.swig.org/Doc1.3/Java.html
http://www.dabeaz.com/cgi-bin/wiki.pl?SwigFaq/Java
7 comments:
Thank you very much my friend !!!
This helped me a lot for my project....i'd been trying it out since two days...it did'nt work out properly for me...
i was really tired searching for help...
you came like an angel...!! :)
thanks again friend !!
I'm running a simple code in *test.c* that uses the libnfc library:
#include "libnfc.h"
#include
#include
#include
static dev_info* pdi;
int connectRFID() {
pdi = nfc_connect();
if (pdi != INVALID_DEVICE_INFO) {
nfc_initiator_init(pdi);
// Drop the field for a while
nfc_configure(pdi,DCO_ACTIVATE_FIELD,false);
// Let the reader only try once to find a tag
nfc_configure(pdi,DCO_INFINITE_SELECT,false);
// Configure the CRC and Parity settings
nfc_configure(pdi,DCO_HANDLE_CRC,true);
nfc_configure(pdi,DCO_HANDLE_PARITY,true);
// Enable field so more power consuming cards can power themselves up
nfc_configure (pdi,DCO_ACTIVATE_FIELD,true);
return 1;
} else return 0;
}
// Disconnects the tag
void disconnectRFID() {
nfc_disconnect(pdi);
}
My *test.i* is has follows:
%module test
%{
#include "libnfc.h"
extern dev_info* pdi;
extern int connectRFID();
extern void disconnectRFID();
%}
extern dev_info* pdi;
extern int connectRFID();
extern void disconnectRFID();
By running (in cygwin):
$ swig -java test.i
$ gcc -c test.c test_wrap.c -I/c/'Program Files'/Java/jdk1.6.0_18/include -I/c/'Program Files'/Java/jdk1.6.0_18/include/win32
$ gcc -shared test.o test_wrap.o -mno-cygwin -Wl,--add-stdcall-alias -o test.dll
I'm getting after the last input:
test.o:test.c:(.text+0x7): undefined reference to `_nfc_connect'
test.o:test.c:(.text+0x26): undefined reference to `_nfc_initiator_init'
test.o:test.c:(.text+0x43): undefined reference to `_nfc_configure'
test.o:test.c:(.text+0x60): undefined reference to `_nfc_configure'
test.o:test.c:(.text+0x7d): undefined reference to `_nfc_configure'
test.o:test.c:(.text+0x9a): undefined reference to `_nfc_configure'
test.o:test.c:(.text+0xb7): undefined reference to `_nfc_configure'
test.o:test.c:(.text+0xdf): undefined reference to `_nfc_disconnect'
test_wrap.o:test_wrap.c:(.text+0x91): undefined reference to `_pdi'
test_wrap.o:test_wrap.c:(.text+0xb3): undefined reference to `_pdi'
collect2: ld returned 1 exit status
That is referring to the functions in the libnfc libraries, which I have in the */usr/include* of the cygwin folder.
What can I be doing wrong?
Hello . I followed the same tutorial. But I want to call c++ function and not C. Used same method and all worked fine. But when I run the java file, I get run time error:
java: symbol lookup error: /home/libxx.so: undefined symbol: fact
It locates one method and gives o/p : 3 though!!
Hey Mathias,
Thank you so much for your help.
I was so frustrated with this SWIG think, I wanted to kill myself. Thanks alot dude...!!!
Thanks man! include and include/linux were the ones i was missing.
Thanks, it was very helpful.
Simple and Clear Explanation ..... Thanks
Post a Comment