- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
I'm new to this forum and was not sure where to post this question. I've got some questions regarding setting up a JNI to access a Fortran dll.
I've built a Fortran dll from f77 from UNIX for the PC using the Intel compiler. Now I need to access it from my Java code via C++ wrapper code. I've searched online and most examples show you how to take a simple piece of Fortran code and nothing as complex as I really need. My basic issue is that the Fortran code I used to make the dll is maintained by someone else. So I am not really authorized to make any changes to it. I'd like to be able to do everything I need inside the wrapper C++ code and simply call the dll to perform the necessary calculations.
That being said here are my questions:
1. Is there some kind of table so I can match up the datatypes between the Java/C/Fortran codes? The Fortran code is expecting reals, real arrays, int, int arrays, character*1, and character*(*). I'm not sure how to specify these in the extern line in the C++ code. Do I pass by reference or by value?
extern "C" void WIND(float*,float[],float[][],const char*....) ??
Java is of course passing floats, 2d float arrays, and strings. I can't find an example that explains how to declare variables for the function prototype for the Fortran subroutine.
I know the conversion from Java to C++ code has items such as jstring, jobjectArray, and jfloatArrays which make sense and works fine for me.
2. Assuming I can correctly call my Fortran subroutine from the dll, how to I get values back. The subroutine accepts several input variables and returns several output variables. I understand that I have to convert the java arrays into native arrays to pass to Fortan and vice versa, once the Fortan code has returned the output values, I must convert those arrays back into something I can pass back to Java.
I have Fortran subroutines and not Fortran functions so will I still be able to get back a bunch of ouptut variables the same way such as this?
WIND(inX,inY,inZ,outA,outB,outC)
3. Assuming I can get back outA, outB, and outC does this mean I have to write getter/setter methods inside the C++ code so my java code can get back all of my data since Java limits me to one object for returning? What's the best way to do this? Should I just make global variables in the C++ code that get set once I get my data back from the Fortran dll call? Then I could tell Java to call the getter methods after the initial run of the calculations have been completed?
Any suggestions any one can give me with be very much appreciated. I have gotten a JNI working for some other C++ code so I just need help on how to extend this to work for a Fortran dll that I'm not allowed to alter.
Link Copied
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
If you cannot alter the dll, but can build it from source, you could add some wrapper functions in Fortran that take C types for input and output and internally making the calls the fortran functions you cannot modify. You could then call these wrappers from C and that solves the type compatibility issues. For java to c, you mention you can only return one object, so use an object that contains all of the data you need to return (e.g. a struct).
I can't help with the code, as my knowledge of the c/fortran interoperability features are a bit lacking, though others here are very knowledgable on that subject.
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
Samantha: Please specify the interface(s) to the Fortran DLL routine(s), following which I or someone else will reply with matching C function prototypes, etc. Giving you a more general reply would involve about as many pages as the mixed-language programming chapter of the Intel Fortran user guide.
You may obtain the interfaces in several ways: (i) show the subroutine declaration, and the declarations of the variables in its argument list; the author of the Fortran code is responsible for giving this to you, in order to make her/his routine callable; (ii) use the automatic interface generation capability of the IFort compiler or third party utilities such as Olagnon's F90AIB, available at http://www.ifremer.fr/ditigo/molagnon/fortran90/ ; this will have to be run by someone with access to the source code for the DLL.
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
Okay here's the subroutine call and variable declarations from Fortran:
subroutine wind (nl, cpcord, rel, alt, sp, seas, idtsel, unit, totdst, ews, ewd, standv, ews50p, fname, ialtfg, idl, altl, spdl, avalt, avspd, distlg, ierr)
integer idtsel(5), unit, ialtfg(6), idl, nl, ierr
real rel(3), spdl(251), sp(6), altl(251), alt(6), cpcord(2,nl), standv(6,5), ews(36,5), ews50p(12,5), distlg(251), avalt, avspd, totdst, ewd(36,5)
character*1 seas
character*(*) fname
The input variables are: alt, altl, cpcord, fname, ialtfg, idl, idtsel, nl, rel, seas, sp, spdl, standv, unit.
The output variables are avalt, avspd, distlg, ierr, ewd, ews, ews50p, and totdst.
Java needs to pass the inputs into the C++ code and the C++ needs to do all the necessary conversions to pass them into the Fortran dll call. I need to get the values back and covert them back into the appropriate things for Java. I'd like to figure out how to do this manually so I can understand the process for future as we have some other Fortran code from UNIX I need to interface with as well.
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
Here is the first attempt. Warnings: the code is just a skeleton, and entirely based on the description that you gave. Note, in particular, that I have no basis for assigning values to the input variables, so I have left them undefined. Also, all the arguments in the call that are arrays are to be allocated in advance, my code is going to pass uninitialized pointers. You may try linking the C code that I give to the real Fortran DLL; if the linking succeeds, please do not attempt to run the resulting program!
[bash]ifort /MD /Od /c wind.f
icl /MD /Od cwind.c wind.obj
cwind
Wind returned ierr = 0
[/bash]
Note that I have said nothing about Java. I think that it is best that you avoid the Java side until you get your C/C++ wrappers and your third party Fortran DLL working together.
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
Thanks for the help with the C code. That is my sticky spot right now. Will post back here when I've got something working.
Oh I just thought of one more thing. For the 2d arrays should they be float** or is it still just float*? I noticed that my 1d arrays were called jfloatArrays but that 2d arrays were called jobjectArrays when I ran javah to create my *.h file for the JNI.
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
Samantha G. wrote:Declaring them as float** will probably not work. Given "float **a", a would be an array of pointers to 1-D arrays of type float. Fortran does not use such arrays; it does not use C pointers unless you use the Fortran 2003 C-Interoperability features. Since your Fortran DLL was probably not written using C-Interoperability, you will have to pass the addresses of contiguous memory blocks to it for those arguments that are arrays.
For the 2d arrays should they be float** or is it still just float*?
C has a rather relaxed (cavalier?) attitude about pointers. You can usually get away with casting pointers of the wrong type to pointers of the correct type in order to match declared prototypes for functions.
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
I'm not sure I understand what you mean by that.
I know for passing a 1d array I can just use something like this to convert my incoming Java jfloatArray into a pointer to that array.
jfloat* spPtr = env->GetFloatArrayElements(speed,0);
and then I can pass this pointer through to the wind Fortran call.
What do I need to do to translate the 2d array into something Fortran can read it? I was told that 2d arrays are stored differently coming from Java than Fortran expects.
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
Sorry, my acquaintance with Java is too slight to help you there; you will need help from someone else on the Java-C connection.
In general, it is not enough to simply find a compatible pointer to pass. The target of the pointer must contain variables of types that are binary-compatible with Fortran and have the same memory layout of multidimensional arrays as Fortran.
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
Then do you know if I can take the jobjectArray from Java and change it into a C 2D array if I can pass a pointer to that array into Fortran? Are the C and Fortran arrays compatible in terms in memory layout?
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
Samantha G. wrote:
Are the C and Fortran arrays compatible in terms in memory layout?
The major differences are that Fortran will have a descriptor in addition to the data, wheras C will just have data and that Fortran uses column-major ordering and C uses row-major for elements of the array.
I am still convinced the cleanest way to do this is to write a F03 module using C-Interoperability to sit between the F77 and C code.
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
Samantha G. wrote:
Then do you know if I can take the jobjectArray from Java and change it into a C 2D array if I can pass a pointer to that array into Fortran?
I am not competent to answer Java questions of that type. I do not even know what a jobjectArray is, although if needed I could do some searching and reading about it.
Are the C and Fortran arrays compatible in terms in memory layout?1-D arrays are. For native 2-D arrays of fixed size, C uses row-major storage order. Fortran uses column-major storage order. For adjustable size 2-D arrays, C often uses an array of pointers. Such arrays are not compatible with Fortran. Compatibility is obtained by mapping the C 2-D array to a C 1-D array, which is compatible with a Fortran 1-D array, which can then be mapped to a Fortran 2-D array.
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
Okay somebody at work suggested I do an inplace matrix transpose like the one here: http://www.geeksforgeeks.org/inplace-m-x-n-size-matrix-transpose/
I've written a routine now to take my (MxN) jobjectarrays from Java and put them into C matrices and do the inplace tranpose so the numbers are in the correct slots for Fortran to read them. Essentially changing the row major matrix in C to a column major matrix in Fortran.
So that part is working but now my C code is giving me strange errors. I'm trying to pass my integer values and integer arrays from Java into my Fortran call. It's saying I'm doing an invalid conversion from jint* to an int* which I don't understand.
For the Fortran extern declaration statement I need to use the int* declaration for my int values and my int arrays. The usual procedure for passing 1D arrays is to take the java array and create a pointer to that array and then pass that pointer array to Fortran. I do this with a statement like this:
jint* ialtfgPtr = env->GetIntArrayElements(altitudeCompError,0);
Where I then do my Fortran call:
wind(.....,ialtfgPtr,....) that corresponds to my declaration statement at the top of extern "C" void wind(....,int *ialtfgPtr,....)
jint is supposed to be a typedef for int32_t. Is this incompatible for Fortran?
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
You problem is that ialtfgPtr is of type jint* and your function wind() is delcared to take type int*. If the underlying types are the same, change your call:
wind(.....,ialtfgPtr,....)
to
wind(.....,(int*) ialtfgPtr,....)
To cast your jint pointer to an int pointer so your argument matches your definition.
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
Thanks Casey that was my problem. So now I finally got the inputs from Java correctly converted to pass to my Fortran dll call.
So now that I'm building it's saying I have an undefined reference to WIND. So now I'm confused. I declare the fortran function prototype and then pass in the correct variables to the Fortran dll call.
I'm loading the Fortran dll from inside Java. Is there something else I need to do inside the C++ code? Or am I thinking about this wrong. Do I need to do something to load the dll in C++? Or is it a linker option during build time instead?
This is how I'm building the C++ code now. I"m building the C++ into a dll for Java called "WindTempJNI.dll". The actual Fortran dll is called "wndtmp.dll".
g++ -m64 -IC:\Progra~1\Java\jdk1.7.0_21\include -IC:\Progra~1\Java\jdk1.7.0_21\include\win32 -c -o WindTempJNI.o src/com_boeing_apt_wndtmp_WindTemp.cpp
g++ -m64 -Wl,--add-stdcall-alias -mwindows -shared -o WindTempJNI.dll WindTempJNI.o
Should I build a C++ dll that includes the Fortran dll and then just have Java use the C++ dll? So it should be Java only talks to C++ and C++ only talks to Fortran so Java never knows about the Fortran dll.
Samantha
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
The c++ will have to be linked against the fortran dll for WIND() to resolve. If the java is only talking to c++, it does not need to know about the fortran dll. I agree with your final statement, you should build a c++ dll that is linked against the fortran dll, and the java should interface with the c++ dll only.
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
I don't supposed you guys know how to do this or have a link to an example of how to do the bulid do you?
Do I build before the fact or do I load the dll at runtime?
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
The link below has some info on this (in a gcc/cygwin/windows environment suggested by your previous post):
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
Let me see if I can help move this a bit farther along. I built a DLL incorporating Samantha's CPP file and my Fortran file as follows.
[bash]ifort /MD /Od /c wind.f
icl /Od /LD /I. com-boeing-apt-wndtmp-windtemp.cpp wind.obj
[/bash]
The DLL got built, and exports the symbol _Java_com_boeing_apt_wndtmp_WindTemp_windJNI@96. Note that there is no need to build two DLLs, although that could be done. Please try using this DLL from your Java code.
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
Hi Guys,
Just thought I'd give you an update of where I am. I think I've finally figured out this TurDuckIn of a problem of going from Java -> C++ -> Fortran.
I am trying to build my C++ dll using both Visual Studio and G++. I got both to build and tried running them. Of course both are causing Java to crash in two different places. I'm still debugging the problem but at least I can see the end in sight.
Here are my findings for my G++ compiler:
I found out for this to work I have to build my Fortran library as a static library *.lib and not as a *.dll. I then take this *.lib file and put in into my link command. The hang up of course is the use of the Intel libraries of the Fortran compiler. I had to tell the Intel compiler not use them with /nolibdir. After that I had to manually find the specific Intel libraries on my computer and put them into my link statement like this:
g++ -m64 $(CPFLAGS) -o WindTempJNI.dll WindTempJNI.o wndtmplib.lib libifcoremd.lib libmmds.lib libirc.lib
The code is crashing when it calls the Fortran method from my *.lib file. I suspect I miss still be missing a library in the linker.
For the Visual Studio compiler:
The Intel compiler integrates into my Visual Studio so it make the build process easier. The compiler build the C++ dll quite easily. Of course it's having problems with my in place Matrix transpose method inside my C++ file. I'm not sure why it's giving me an unhandled exception from this method in my C code. On the bright side, when I tell it not to transpose my matrices and send it into Fortran, the code can run without crashing. So it seems it's talking to the Fortran all right. I still need to verify that I get numbers back from my Fortran call.
Thanks for the help everyone. Will let you know if I ever get this thing to work right and give me the correct numbers back.
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
Well the good news is that now the Visual Studio C++ dll build is running without errors. I just needed to increase my heapsize to handle the in matrix transpose for my larger arrays. The bad news is that there is no change in the numbers. I pass in values that are zero and I am getting zero values back instead of nonzero values. Does anyone see anything obviously wrong?
WIND(nl,cpcoord,relPtr,altPtr,spPtr,seas,(int*) idtselPtr,unit,&totalDistance,ews,ewd,standv,ews50p,
fname,(int*) ialtfgPtr,idl,altlPtr,spdlPtr,&averageAltitude,&averageSpeed,distlgPtr,ierr ,&seaslen,&fnamelen);
//Outputs****************
std::cout<<"totdst "<<totalDistance<<std::endl;
std::cout<<"ews "<<ews[0]<<std::endl;
std::cout<<"ewd "<<ewd[0]<<std::endl;
std::cout<<"ews50p "<<ews50p[0]<<std::endl;

- Subscribe to RSS Feed
- Mark Topic as New
- Mark Topic as Read
- Float this Topic for Current User
- Bookmark
- Subscribe
- Printer Friendly Page