- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
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!
Link Copied
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
First of all, since you're using a DLL you're not "linking" and this eliminates many possible issues.
I don't know Qt, but I see that you declare the argument as "int i". In C, that would mean pass-by-value, and you specified REFERENCE in Fortran, so attempting to access the argument would get you an error. What if you remove REFERENCE from the ATTRIBUTES directive?
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
Thanks very much for the quick reply Steve,
I attempted the change you made and came across error #8039 that I couldn't use INTENT(OUT) types with "C" attribute. Here is the function I am trying to export (as I said, it is simply a trial to make sure the application is communicating with the dll):
@SUBROUTINE SQ(a,asquare)
!DEC$ ATTRIBUTES C, REFERENCE, MIXED_STR_LEN_ARG, DLLEXPORT, ALIAS:"SQ" :: SQ
integer, intent(in) :: a ! input
integer, intent(out) :: asquare ! output
asquare = a**2
END SUBROUTINE SQ @
This is one option to reference the function, but I would prefer to prototype it in a header file, so that it is loaded at run time. Could you please indicate to me how I would prototype the function (@ extern "C" { int SQ(int) }@ etc). Again I apologise for such a banal question.
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
It looks to me as if you declare the procedure in Qt as a function returning an int and taking one int argument by value, but the Fortran code is a subroutine with two integers. This isn't going to match.
Assuming you call the "square" routine as a function in Qt, I'll suggest the following alternative Fortran source:
function SQ (a) result(asquare) use, intrinsic :: ISO_C_BINDING !DEC$ ATTRIBUTES C, DLLEXPORT,ALIAS:"SQ" :: SQ integer(C_INT) :: asquare integer(C_INT), intent(IN) :: a asquare = a**2 end function SQ
For the benefit of others, I'm not using BIND(C) here because there's no way to force the name to be just "SQ" without decoration. The ATTRIBUTES C sets the argument convention to be by value.
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
But don't you want the C-style name decoration because the function is prototyped as a C function? There is about one incompatibility I can think of between MinGW and ifort in that for 32-bit code, if a function returns a struct that is too big or complicated to get returned in EAX, then the two systems will leave ESP in a different state on return.
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
In this case, the name is referenced from the DLL, as one would do in GetProcAddress (or from VB). No decoration is applied.
That's a good thing to know about function returns, though it doesn't apply in this particular case.
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
You had me scared for a minute there, Steve, and I thought I might have to use a *.DEF file to get the names right, but...
! SQ.f90 ! Compiled with ! gfortran -shared SQ.f90 -oSQ.dll ! or ! ifort SQ.f90 /dll module M implicit none contains function SQ(a) bind(C,name='SQ') use, intrinsic :: ISO_C_BINDING implicit none !DEC$ ATTRIBUTES DLLEXPORT :: SQ !GCC$ ATTRIBUTES DLLEXPORT :: SQ integer(C_INT) SQ integer(C_INT),value :: a SQ = a**2 end function SQ end module M
// Cprog.c // Compiled with // gcc Cprog.c -oCprog #include <stdio.h> #include <windows.h> int main() { HMODULE dll_handle; int (*SQ)(int); int a; // Check for bitness -- this would be easy in 64-bit mode. printf("This is a %d-bit program\n",8*sizeof(HANDLE)); dll_handle = LoadLibrary("SQ.dll"); printf("dll_handle = %p\n",dll_handle); SQ = (void *)GetProcAddress(dll_handle,"SQ"); printf("SQ = %p\n",SQ); a = 17; printf("SQ(%d) = %d\n", a, SQ(a)); return 0; }
And whether SQ.f90 is compiled with ifort or gfortran, I still get
C:\>Cprog This is a 32-bit program dll_handle = 727A0000 SQ = 727A1010 SQ(17) = 289
So the undecorated name seems to be getting passed in either case.
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
==> Repeat Offender: Seeing as I have no idea what neither EAX or ESP are, ill continue on silently nodding my head...
==> Steve Lionel: Perfect, your changes worked flawlessly Steve. Now that I am sure that the .dll is at least communicating, I can continue on and try to take the next step with my implementation. I am trying to create an implicite dynamic link to the library, so I wish to define the prototypes for my functions in a header file. I am trying to access exactly the function you have defined above, expect define it in a header and then access the function in my code.
#ifndef LINK_1 #define LINK_1 #include <iostream>
#if defined DLL_EXPORT #define DECLDIR __declspec(dllexport) #else #define DECLDIR __declspec(dllimport) #endif extern "C" { int SQ(int a); } #endif // LINK_1
As far as I can tell, I don't need _stdcall because I havnt changed anything in the Intel Fortran compiler. Is the
__declspec
only necessary for Windows dlls? I have removed the whole DECLDIR section and regardless of this I get the error:
"Undefined reference to SQ". Could you provide me with some guidance here. Two notes:
1) MingW is the compiler for Qt, and I am supposing that incompatibility between g++ and Inteö Fortran means I just have to cross my fingers and hope it works and:
2) I plan to call a set of SUBROUTINES from my .cpp file. In order to do this I will need to define the input parameters as Fortran types, is it not going to be better then to call ISO_C_BINDING from within my C++ application and then pass the values into the fortran modules as fortran types.
If we get through this I owe you Intel guys a beer!
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
J S. : I refrained from answering earlier since I do not know anything about QT. Now I see that, perhaps, that is no disqualification, since the real issue is to write a source code in Fortran that will be compiled with IFort into a 32-bit DLL, which will be called from C code compiled with 32-bit Mingw gcc. If so, here is a working example (I used MinGW32 GCC 4.9.2 and IFort 15.0.3, but combinations of other versions of these 32-bit compilers should work).
Note that I have not used the C-interoperability features of Fortran, nor have I used any DEC$ directives. As a result, the code will probably not build on any operating system other than some version of Windows.
The C caller (cmain.c):
#include <stdio.h> int main(){ extern int SQ(int *); int x,y; x=13; y=SQ(&x); printf("%d %d\n",x,y); }
The Fortran DLL source code (sq.f90):
function sq(x) result(y) integer x,y y=x*x return end function
Building and running:
ifort /LD sq.f90 /link /export:SQ gcc cmain.c sq.lib -o cmain D:\Mixed>cmain 1.300000e+001 1.690000e+002
One point that you seem to have missed is that in Intel Fortran, the default linkage is CREF (cdecl+ref), so you have to declare "int SQ(int *)" as the prototype instead of "int SQ(int)".
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
As shown in Quote #7 above, dynamically linking should go through OK, but now you are trying to achieve the linkage to the procedures at link time, rather than at run time. This is easy because ifort by default creates a *.LIB file when it compiles to a *.DLL file. Just include the *.LIB file in the gcc command that does the linking step and everything should be OK. Just tested this with the SQ.dll and SQ.lib that ifort built in Quote #7:
// test2.c // compile with // gcc test2.c SQ.lib -otest2 #include <stdio.h> int SQ(int); int main() { int a; a = 17; printf("SQ(%d) = %d\n",a, SQ(a)); return 0; }
and it works. Don't have to worry about EAX or ESP, subroutines should be safe, and the only functions you need worry about are the ones that return a user-defined type (struct in C).
While we are on the subject of VBA (Quote #6) I tried switching to STDCALL, and everything went through OK with ifort, but gfortran seems to have a bug that requires a *.DEF file to work around. My reference was https://msdn.microsoft.com/EN-US/library/office/bb687850.aspx where it seems to indicate that using __declspec(dllexport), which we hope that !DEC$ ATTRIBUTES DLLEXPORT should be emulating, handles the name decoration issues correctly.
! std.f90 ! Compiled with ! gfortran -shared std.f90 std.def -ostd.dll ! or ! ifort std.f90 /dll module M implicit none contains function SQ(a) bind(C,name='SQ') use, intrinsic :: ISO_C_BINDING implicit none !DEC$ ATTRIBUTES STDCALL,DLLEXPORT :: SQ !GCC$ ATTRIBUTES STDCALL,DLLEXPORT :: SQ integer(C_INT) SQ integer(C_INT),value :: a SQ = a**2 end function SQ end module M
// stdprog.c // Compiled with // gcc stdprog.c -ostdprog #include <stdio.h> #include <windows.h> int main() { HMODULE dll_handle; int (__stdcall *SQ)(int); int a; // Check for bitness -- this would be easy in 64-bit mode. printf("This is a %d-bit program\n",8*sizeof(HANDLE)); dll_handle = LoadLibrary("std.dll"); printf("dll_handle = %p\n",dll_handle); SQ = (void *)GetProcAddress(dll_handle,"SQ"); printf("SQ = %p\n",SQ); a = 17; printf("SQ(%d) = %d\n", a, SQ(a)); return 0; }
As remarked above, this works fine with ifort, but gfortran needed that workaround with a *.DEF file
; std.def
LIBRARY std
EXPORTS
SQ = SQ@4
As can be seen, gfortran is stripping off the leading underscore but not the trailing at sign and byte count.
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
Thankyou for the quick answer mecej4. Ok perhaps at this stage I should provide a much broader description of my task, I don't want to be moving towards progressively more confusing questions without you at least having a good overview of what I am actually trying to do.
I have an application developed in Qt (C++) which I compile with 32-bit Mingw gcc. I have an extra program, written in .f90 (which has been configured to be compiled with Intel Fortran on VS2012) which I am attempting to make a dynamic library out of, so that I can access its functionality. The fortran code is executed from the command line and if of the type PROGRAM. This PROGRAM USE's then a number of elsewhere defined modules and typedefs to input to and CALL four major subroutines. Each subroutine has a number of (very very many) dependencies and modules/subroutines associated with them.
My plan is to essentially "remove" the Program.f90 main file and emulate it within my main code so that I can interrmittently manipulate/extract the data between steps.
This includes:
- Write an extra module which allows me to export and declare the input types in my main program.
- Export each of the four major subroutines and prototype them in my main program.
- Call these four subroutines when desired from within my main program
It could very well be that my understanding is insufficient or flawed, but as far as I can tell, it will be simplest to create the new modules necessary within the code, and export them along with the four major subroutines into a dll so that I can execute them from within my main code.
I have already compiled a simplified form of the dll, and as we saw above, this CAN (in a very simple way) communicate with my application. The task is now trying to export and import the subroutines from my program.
IF THERE IS ANY CONFUSION ABOUT WHAT I AM DOING; OR ANY GAPING FLAWS IN MY LOGIC; PLEASE DO NOT HESITATE TO STATE THIS NOW, I only started on this a few weeks ago and I am new to mixed-language programming.
Com
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
Well, I tried the *.DLL built with ifort in Excel VBA and it worked, but the version built with gfortran and the *.DEF file didn't. I don't know what the problem is now with gfortran, but at least it can be seen that BIND(C) is OK for ifort, which is good in my opinion because it's closer to the standard than !DEC$ ATTRIBUTES C.
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
R.O,:
There is a similar problem with getting MSVC to give us the kind of name decoration that we want. I ran into that several months ago, and no longer remember the details well, but I think that it will only permit a limited number of combinations of @nn, prefix underscore, suffix underscore and symbol case (upper/lower).
Since symbol renaming cannot be done using <intname=extname> in .DEF files for prebuilt DLLs for which source code is not available, remaining mismatches had to be fixed on the Fortran side.
My old fallback solution is to write an assembler file containing a macro to do the necessary redecoration, and invoke the macro with something like
cnv dgr29, DGR29
as often as needed. This macro results in creating an procedure entry "dgr29", at which point there is a jump to the external entry DGR29.
J S.:
The problem with the code extracts in #8 is that you #define-d DECDIR, but the definition does nothing because there is no place later in the code where DECDIR is used.
I think that the issues that you described in #11 should be handled easily along the lines outlined in #9. To move things along, post the Fortran interface of one of the routines that you intend to put into the DLL, and post the corresponding C prototype that you will put in the caller. Pick any non-trivial subroutine for this purpose. In particular, if you wish to pass strings between Fortran and C, you could choose that at the test case.
If you wish to pass multidimensional arrays, that will be a bit more complicated, because these are not 100 percent compatible.
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
I found the problem with gfortran: I don't have the gfortran bin directory on path normal %Path%, I only set it up for a special command prompt, so Excel reports file not found when it's actually gfortran's run time library *.DLL that can't be found, not the std.dll file that gfortran built. Putting gfortran's bin directory on my default %Path% permits std.dll built with gfortran to now work with gfortran.
Back to the original topic: since the task consists of porting subroutines, the incompatibility mentioned in Quote #5 is not an issue. Since BIND(C) rather than the !DEC$ ATTRIBUTES C, DLLEXPORT,ALIAS:"SQ" :: SQ of Quote #4 has been tested and works as in Quote #7 I think you should go that way... perhaps I can get Steve's vote of confidence on this? Of course this depends on the data types that are passed to the subroutines. If any user-defined types can be cast as BIND(C), life should be really easy. If not, the C code is going to have some problems digesting them anyhow, and they might have to be redesigned a bit.
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
Thanks R.O. This is definitely a lot for me to work with!
I have tried defining the function you gave me above within a larger module as :
PUBLIC :: SQ
and I get the compilation error:
error #8143: The BIND(C) attribute for this symbol conflicts with a DEC$ ATTRIBUTES ALIAS, DECORATE, STDCALL, C, [NO_]MIXED_STR_LEN_ARG or REFERENCE attribute for this symbol. [SQ] ...
I'm guessing this is a result of not defining it withing its own module as you have done...I tried removing a few items but that didn't particulary help. Also, does your approach mean that if I wish to create the linkage to the procedures at link time, rather than at run time, I have no need for header files for the imported functions?
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
Steve, in Quote #7 you will observe that my C main program computes and prints out the bitness as 32-bit and also printing out the pointers with %p format they are printing out as 32-bit pointers. I tried to be real careful to use only 32-bit tools, installing a new version of 32-bit gfortran on my computer along the way as well. If you go back to the link I posted in Quote #10, the name decoration properties work as described there, as far as I can tell, except for the bug noted in gfortran for STDCALL procedures in Quote #10. Try building a little *.DLL using my Fortran code from Quote #7 or Quote #10 above and just look at the *.DLL with DUMPBIN /EXPORTS. Or you could try the full example from Quote #7 or Quote #10 and test it with whatever C compiler you have available -- gcc shouldn't be necessary for these examples to work.
I was kind of surprised that everything went through so smoothly like this with BIND(C). That means it's possible for code that interoperates like this to look more like normal f2003 code.
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
In Quote #3 we see:
!DEC$ ATTRIBUTES C, REFERENCE, MIXED_STR_LEN_ARG, DLLEXPORT, ALIAS:"SQ" :: SQ
But in Quote #15,
error #8143: The BIND(C) attribute for this symbol conflicts with a DEC$ ATTRIBUTES ALIAS, DECORATE, STDCALL, C, [NO_]MIXED_STR_LEN_ARG or REFERENCE attribute for this symbol. [SQ]
My guess is that in the actual code, you have still the ATTRIBUTES from Quote #3. The C attribute is the default for BIND(C), so it isn't needed. Also REFERENCE is the default, with no way to change it except by adding the VALUE attribute to individual arguments as needed (the standard Fortran VALUE attribute, not the !DEC$ ATTRIBUTES VALUE attribute!). The ALIAS:"SQ" is taken care of by the NAME='SQ' suffix in the BIND(C) clause.
You have to get rid of MIXED_STR_LEN_ARG because BIND(C) doesn't allow for CHARACTER arguments with LEN other than 1. If your actual subroutines do have such arguments, let us know and there is a simple way to work around this limitation. Thus with BIND(C), the only attribute you should specify is
!DEC$ ATTRIBUTES DLLEXPORT :: SQ
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
Gentlemen I state once again, thankyou for all of your help! I continue with the issues outlined previously:
==> RO: The error was in fact my use of STDCALL in the attributes which was causing the issue, I had removed the other attributes. I'm unsure if this is a problem, I am compiling with: Runtime Library: Multithreaded and Calling convention default. With all code exactly as yours in my main program I get the error:
invalid conversion from 'void*' to 'int(__attribute__((__stdcall__))*)(int)' [-fpermissive]
==> mecej4: The main PROGRAM file opens with:
USE FAST_IO_Subs ! all of the ModuleName and ModuleName_types modules are inherited from FAST_IO_Subs IMPLICIT NONE ! Local variables: REAL(DbKi), PARAMETER :: t_initial = 0.0_DbKi ! Data for the glue code: TYPE(FAST_ParameterType) :: p_FAST TYPE(FAST_OutputFileType) :: y_FAST TYPE(FAST_MiscVarType) :: m_FAST TYPE(FAST_ModuleMapType) :: MeshMapData TYPE(ElastoDyn_Data) :: ED
... there is a list of about 14 of these variables (and example of which I will provide later) and each of these are fed into four SUBROUTINES. An example of which is given by:
CALL FAST_InitializeAll(t_initial, n_t_global, p_FAST, y_FAST, m_FAST, ED, SrvD, AD, IfW, HD, SD, MAPp, FEAM, MD, IceF, IceD, MeshMapData, ErrStat, ErrMsg )
My logic is to initialize each of the parameters (p_FAST, y_FAST etc) in my main program (Qt) and then pass these into the four subroutines ( which are declared in my dll) and that way I can control when and how the subroutines are called.
Actually passing initializing arguments to these parameters is taken care of in the subroutines, they are all assigned values in the subroutine FAST_InitializeAll, so I needn't worry about EVERY declared type, I just need to be able to initialize, say "ED" within Qt.
Now in terms of the declared types: The -ElastoDyn_Data- type for example is defined as:
TYPE, PUBLIC :: ElastoDyn_Data TYPE(ED_ContinuousStateType) , DIMENSION(1:2) :: x TYPE(ED_DiscreteStateType) , DIMENSION(1:2) :: xd ... TYPE(ED_InputType) :: u ! System inputs [-] ... TYPE(ED_OutputType) , DIMENSION(:), ALLOCATABLE :: Output TYPE(ED_InputType) , DIMENSION(:), ALLOCATABLE :: Input REAL(DbKi) , DIMENSION(:), ALLOCATABLE :: InputTimes END TYPE ElastoDyn_Data
And in further detail, TYPE(ED_InputType) is defined as:
TYPE, PUBLIC :: ED_InputType TYPE(MeshType) , DIMENSION(:), ALLOCATABLE :: BladeLn2Mesh ... REAL(ReKi) , DIMENSION(:,:,:), ALLOCATABLE :: TwrAddedMass REAL(ReKi) , DIMENSION(1:6,1:6) :: PtfmAddedMass ... REAL(ReKi) :: HSSBrTrqC END TYPE ED_InputType
My logic is this time to define, say, an export function in in the fortran code such as:
function GIVEmeED() implicit none !DEC$ ATTRIBUTES DLLEXPORT :: GIVEmeED !GCC$ ATTRIBUTES DLLEXPORT :: GIVEmeED type(ED_InputType) GIVEmeED type(ED_InputType) :: VAR GIVEmeED = VAR end function GIVEmeED
I understand this probably still won't work, but we realise now the issue of not being able to use BIND(C) on everything, that requires redefning EVERYTHING and most likely modifying a number of subroutines.
Now you have an idea of the scale of the implementation I am planning. Is my logic flawed? Will there by major issues passing information between the two programs and/or am I going to be able to allow the subroutines to act upon the parameters.
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
I think perhaps it would be better, from this point of view to avoid BIND(C), as this means trying to define every variable with C calling convention, whereas it may be much easier to include iso_c_binding in my main program and define my fortran variables there.
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
The issue with STDCALL is a bit of a problem. You appear of have an old version of Intel Fortran, because STDCALL has been allowed in BIND(C) procedures for a year now (2? did time fly by that fast?) You copied my example from Quote #10, which was intended just to show that STDCALL could work, but my assumption until now was that you wanted to stick with the default calling convention instead. If you want to switch to STDCALL you will have to upgrade your compiler of use !DEC$ ATTRIBUTES throughout.
ALLOCATABLEs in user-defined types is going to be a problem because C isn't going to readily be able to read or write them. Such user-defined types aren't interoperable so you can't give procedures with them as arguments the BIND property which is going to make interfacing ugly. Maybe you can get by with C knowing the addresses of instances of such types as opaque pointers and have a couple of Fortran helper functions available to extract the addresses of the arrays in the ALLOCATABLEs and return them to C.
The error the you showed was an issue about casting pointers in C. memcpy() is the sanctioned way to sanitize this casting in current C language dogma. I have an updated set of examples. First is the Fortran file that makes the *.DLL and perhaps *.LIB file.
! SQ.f90 ! Compiled with ! gfortran -shared SQ.f90 -oSQ.dll -Wl,--out-implib,SQ.lib ! or ! ifort SQ.f90 /dll module M implicit none contains function SQ(a) bind(C,name='SQ') use, intrinsic :: ISO_C_BINDING implicit none !DEC$ ATTRIBUTES DLLEXPORT :: SQ !GCC$ ATTRIBUTES DLLEXPORT :: SQ integer(C_INT) SQ integer(C_INT),value :: a SQ = a**2 end function SQ end module M
The above works in all combinations except when compiled with gfortran as shown in combination with fprog2.f90 compiled with ifort. Accordingly, we will compile this with ifort as shown. For dynamically linking with C, I provide an updated example with a header file because you asked for it, and also sanitization with memcpy().
// pSQ.h typedef int(*pSQ)(int);
// Cprog1.c // Compiled with // gcc Cprog1.c -oCprog1 #include <stdio.h> #include <windows.h> #include "pSQ.h" int main() { HMODULE dll_handle; pSQ SQ; int a; FARPROC temp; // Check for bitness -- this would be easy in 64-bit mode. printf("This is a %d-bit program\n",8*sizeof(HANDLE)); dll_handle = LoadLibrary("SQ.dll"); printf("dll_handle = %p\n",dll_handle); temp = GetProcAddress(dll_handle,"SQ"); memcpy(&SQ,&temp,sizeof(temp)); printf("SQ = %p\n",SQ); a = 17; printf("SQ(%d) = %d\n", a, SQ(a)); return 0; }
Output:
This is a 32-bit program dll_handle = 64DB0000 SQ = 64DB1010 SQ(17) = 289
Now for linking via a library with C, also with a header file,
// SQ.h int SQ(int a);
// Cprog2.c // compile with // gcc Cprog2.c SQ.lib -oCprog2 #include <stdio.h> #include "SQ.h" int main() { int a; // Check for bitness -- this would be easy in 64-bit mode. printf("This is a %d-bit program\n",8*sizeof(intptr_t)); a = 17; printf("SQ(%d) = %d\n",a, SQ(a)); return 0; }
Output:
This is a 32-bit program SQ(17) = 289
Fortran with dynamic linking
! fprog1.f90 ! Compile with ! ifort fprog1.f90 program P ! use M use ISO_C_BINDING use IFWIN implicit none abstract interface function SQ1(a) bind(C) use ISO_C_BINDING implicit none integer(C_INT) SQ1 integer(C_INT), value :: a end function SQ1 end interface ! procedure(SQ), pointer :: pSQ procedure(SQ1), pointer :: pSQ type(C_FUNPTR) cpSQ integer(HANDLE) H integer(C_INT) a write(*,'(*(g0))') 'This is a ',bit_size(H),'-bit program' H = LoadLibrary('SQ.dll'//achar(0)) write(*,'(z0)') H cpSQ = transfer(GetProcAddress(H,'SQ'//achar(0)),cpSQ) write(*,'(z0)') transfer(cpSQ,0_C_INTPTR_T) call C_F_PROCPOINTER(cpSQ,pSQ) a = 17 write(*,*) a,pSQ(a) end program P
Output:
This is a 32-bit program 64DB0000 64DB1010 17 289
Fortran linking via a library
! fprog2.f90 ! Compile with ! ifort fprog2.f90 SQ.lib ! or ! gfortran fprog2.f90 SQ.lib -ofprog2 program P use ISO_C_BINDING implicit none interface function SQ(a) bind(C,name='SQ') use ISO_C_BINDING implicit none integer(C_INT) SQ integer(C_INT), value :: a end function SQ end interface integer(C_INT) a write(*,'(*(g0))') 'This is a ',bit_size(0_C_INTPTR_T),'-bit program' a = 17 write(*,*) a,SQ(a) end program P
Output:
This is a 32-bit program 17 289
One problem I noticed is the in the Fortran dynamically linked example (fprog1.f90 above) if you try to use the interface from module M to prototype function SQ by uncommenting the use M line and then using the first declaration for pSQ:
! fprog1.f90 ! Compile with ! ifort fprog1.f90 program P use M use ISO_C_BINDING use IFWIN implicit none abstract interface function SQ1(a) bind(C) use ISO_C_BINDING implicit none integer(C_INT) SQ1 integer(C_INT), value :: a end function SQ1 end interface procedure(SQ), pointer :: pSQ ! procedure(SQ1), pointer :: pSQ type(C_FUNPTR) cpSQ integer(HANDLE) H integer(C_INT) a write(*,'(*(g0))') 'This is a ',bit_size(H),'-bit program' H = LoadLibrary('SQ.dll'//achar(0)) write(*,'(z0)') H cpSQ = transfer(GetProcAddress(H,'SQ'//achar(0)),cpSQ) write(*,'(z0)') transfer(cpSQ,0_C_INTPTR_T) call C_F_PROCPOINTER(cpSQ,pSQ) a = 17 write(*,*) a,pSQ(a) end program P
You get a strange error at link time
Microsoft (R) Incremental Linker Version 10.00.30319.01 Copyright (C) Microsoft Corporation. All rights reserved. -out:fprog1.exe -subsystem:console fprog1.obj fprog1.obj : error LNK2019: unresolved external symbol PSQ referenced in functio n _MAIN__ fprog1.exe : fatal error LNK1120: 1 unresolved externals
The error seems to have been triggered by the !DEC$ ATTRIBUTES DLLEXPORT for SQ in SQ.f90 and then the first use of pSQ in fprog1.f90. I'm not sure that my code is consistent with ifort's documented behavior for prototyping procedure pointers, but even if it is there is no way that I should be getting an error at link time rather than at compile time. Curiously, gfortran throws an error like 'confused by previous errors, bailing' but with no previous errors on analogous code.
- Subscribe to RSS Feed
- Mark Topic as New
- Mark Topic as Read
- Float this Topic for Current User
- Bookmark
- Subscribe
- Printer Friendly Page