Intel® Fortran Compiler
Build applications that can scale for the future with optimized code designed for Intel® Xeon® and compatible processors.
28489 Discussions

Linking IVF Compiled DLL to Qt Application

J_S_1
Beginner
3,904 Views

Hi everybody,
This is my first attempt at library assembly, so please have mercy! Let me also start by saying to Steve Lionel: Wow, you answer a lot, I hope they are paying you well there at Intel!

I am trying to link an external library (written in .f90 and compiled with IVF on VS2012) to my Qt application (32bit based on Qt 5.5.0- MinGW 4.9.2).

I have a few questions:

1) Is this futile? Some of the research I have found suggests that IVF and MinGW are ABI incompatible. I really want to stay with the MinGW compiler in Qt because basically everything else we are doing with the software uses this.

2) It would be of advantage to be able to load the library only when called upon (which would only represent a fraction of cases). For this reason I have been attempting to use QLibrary but keep getting Segmentation faults when I try to call SUBROUTINES defined in my DLL (with resolve("my_function")):

I have defined my external call routines with:

 @ !DEC$ ATTRIBUTES C, REFERENCE, MIXED_STR_LEN_ARG, DLLEXPORT,   ALIAS:"sub_name" :: sub_name @
and import them using:

   @typedef int (*MyPrototype)(int i);
    MyPrototype Square = (MyPrototype) DLLname.resolve("sub_name"); @

3) Is there any way to check if the library has, in fact, loaded? Of course calling a subroutine would accomplish this, but that isn't working, and when I import the library DLLname.load() returns a positive result. and resolve.("sub_name") has a memoryaddress allocated to it. Does this suggest a type fault? Ie. passing the wrong identifier in to the fortran code.

Once again, thanks for reading and please feel free to take apart any flaws in logic I have, I'm not (yet) a programmer!

0 Kudos
46 Replies
IanH
Honored Contributor II
394 Views

J S. wrote:

My computer is about to die gentlemen so I will try to quickly write a response.

Thanks to both of you but it appears in both cases I need to also provide the string length as an input argument (also in comment #35). In my particular case the return string will always have 1025 characters, so I think I just need to pass a pointer to the function for a character array with 1025 characters. Would this then be

char* b [1025]

? And is it possible to not declare the length and allow C++ to figure this out for itself? I will take another look at both of your posts tomorrow, thankyou for taking a look.

If you want an array of 1025 characters, then get rid of the star between the `char` and the `b`.  Otherwise you've got an array of size 1025 of pointer to char.

If C++ has to set aside some storage for the string, then it needs to know the size of the string in some way so it can set aside the appropriate amount of storage. 

 

0 Kudos
gib
New Contributor II
394 Views

There was an error in what I wrote before.  Instead of len(string) it should have been len(trim(string)), i.e. length with trailing blanks removed.  Because of the blanks I didn't see that the string was wrong when I printed it out.

It is easy to do without the buflen argument, although I'm not sure it's a good idea.  You might want to leave it there to check on the C side that the Fortran side is doing what you expect.  See the code below, in which the terminating null is added in the subroutine.  It is your responsibility to make sure that buflen+1 <= 1024.

The key point is that there is no need to allocate space on the C side, because it has been allocated on the Fortran side.  Take note of what Ian pointed out, that the 'string' variable needs to be saved, either by giving it the SAVE attribute or by making it a global variable.  If for some reason you wanted a copy of the string on the C side (e.g. because 'string' in the subroutine may change) you can do that, of course:

char bcopy[1024];

strcpy(bcopy,b);   // or use the safe version of strcpy

extern "C" void get_string(char **);
...
    char *b;
    get_string(&b);
// Note that the string is already null-terminated in the Fortran subroutine
subroutine get_string(bufptr) bind(C)
!DEC$ ATTRIBUTES DLLEXPORT :: get_string
use, intrinsic :: iso_c_binding
type(c_ptr) :: bufptr
integer :: buflen
character*(1024), save :: string

string = 'A test string'
buflen = len(trim(string))
string(buflen+1:buflen+1) = char(0)    ! null-terminate string for C
bufptr = c_loc(string)
end subroutine

 

0 Kudos
J_S_1
Beginner
394 Views

Hello again Ladies and Gentlemen!

The advice you gave me was fantastic, I am extremely thankful, I even put an Aknowledgement in the paper I published for you, I will post the address when the paper gets published.

As for now I have a simple question: I wish to declare a prototype function within C++ (which corresponds to an exported function in FOrtran)which has arrays of doubles as arguments. Such as:

extern "C" typedef void (function)(double Forces[][][]) 

The size of the arrays are not known at compile time. Until now I have solved the problem simply be defining a set array size

Forces[][3][18]

For example. I now need the capability to pass arrays of arbitrary size into a fortran subroutine. The array size being passed to Fortran from C++ is guaranteed to be correct everytime, as these parameters are defined earlier in the program, I simply need to know how to define the variables in the prototype function and how to "package" them when I pass them to Fortran. This is accomplished easily for array of dimension 1 by simply defining the argument as a pointer to the first element of an array, and correctly allocating the array size at run time:

extern "C" typedef void (function)(double* Force)
double ForceDummy[18]={0};

F_function(ForceDummy) ;

This however does not work as well for array of dimension 2,3, or 4 (I need arrays up to deminsion 4). I have already tried with dynamics allocation such as std::vector and then providing vector.data() as the argument, but alas thus far no luck.

Any help would be appreciated.

0 Kudos
mecej4
Honored Contributor III
394 Views

C and Fortran follow different conventions for storing arrays of rank higher than 1. Fortran stores a 2-D array by columns, C stores a 2-D array by rows, and similarly for 3-D arrays, etc. Although you may wish to regard arrays of 2 and higher dimensions as computer representations of the corresponding mathematical entities, when an array is being used as a subroutine/function argument, you have to pass the base address of the array + information to map the 2-D, etc., array into an equivalent 1-D array.

Here is an example, for use with Intel Fortran. An array is created in C as a native 2-D array. The array is passed to Fortran for printing. Note that the subscripts are interchanged in the Fortran subroutine as compared to the C caller.

C also allows you to implement a 2-D array of dynamic size as a 1-D array of pointers to 1-D arrays (of type float, etc.). This representation is quite difficult to use with Fortran and is probably not worth the trouble.

To make the code portable, you can use the ISO-C interoperability features of Fortran 2003.

The C program:

#include <stdio.h>
extern void PMAT(float *A, int *m, int *n);

main(){
int i=2,j=3; float M[2][3] = {{1.1,1.2,1.3},{2.1,2.2,2.3}};
PMAT((float *)M,&i,&j);
}

The subroutine:

subroutine pmat(A,m,n)
integer i,j
real A(n,m)
!
do i=1,m
   write(*,'(3F6.1)')(A(j,i),j=1,n)
end do
return
end

 

0 Kudos
FortranFan
Honored Contributor II
394 Views

J S. wrote:

..  As for now I have a simple question: I wish to declare a prototype function within C++ (which corresponds to an exported function in FOrtran)which has arrays of doubles as arguments. Such as:

extern "C" typedef void (function)(double Forces[][][]) 

The size of the arrays are not known at compile time. .. For example. I now need the capability to pass arrays of arbitrary size into a fortran subroutine. The array size being passed to Fortran from C++ is guaranteed to be correct everytime, as these parameters are defined earlier in the program, I simply need to know how to define the variables in the prototype function and how to "package" them when I pass them to Fortran. This is accomplished easily for array of dimension 1 by simply defining the argument as a pointer to the first element of an array, and correctly allocating the array size at run time:.. This however does not work as well for array of dimension 2,3, or 4 (I need arrays up to deminsion 4). I have already tried with dynamics allocation such as std::vector and then providing vector.data() as the argument, but alas thus far no luck. ..

@J S.,

Depending on what is going on in your C++ program, this may not be such a simple question.  To elaborate further on mecej4's comments in Message #46, one thing to keep in mind is that contiguous storage sequence makes interoperation of arrays between Fortran and C type of processors much easier and cleaner.  Have you looked into your C++ code and analyzed how memory allocation is performed for the dynamic arrays?  If I recall correctly,C++ std namespace doesn't include multidimensional arrays (matrices and so forth) even though it has std::vector class for 1D capability.  So how are you setting up "array of dimension 2,3, or 4" (or planning to)?  Also, is the Fortran interface fixed or changeable?  Note the prototype you show above involving "double Forces[][][]" is likely not going to work.

By the way, can you explain why you need arrays of higher ranks?  Is it because of how Fortran code does things (toward numerical computations) or how C++ code is setup or both?  You may know this - if it is just because of computations on the Fortran side, Fortran interface can simply receive data in the form of type(c_ptr) and using c_f_pointer intrinsic, you can transform the data into arrays of different shapes; no need then to work with multidimensional arrays on C++ side.

0 Kudos
Reply