View previous topic :: View next topic |
Author |
Message |
StamK
Joined: 12 Oct 2016 Posts: 159
|
Posted: Tue Jun 20, 2017 11:28 am Post subject: How to load a Fortran function from C++ (instructions) |
|
|
In the 32bit world it was possible for the linker to create a dll AND a lib file (via the archive command).
This was particular useful as it allowed a Qt or VC++ program to call a Fortran function directly using
Code: |
extern "C" void FortranFunction(...)
|
Now however it is necessary to use LoadLibraryA if using VC++, as follows:
Code: |
#include "stdafx.h"
#include "windows.h"
//extern "C" void VERYSIMPLE(int* input); //old way, with .lib file present
extern "C" {
typedef void(*VERYSIMPLE_ptr)(int* input);
}
void VERYSIMPLE(int* input)
{
auto hdl = LoadLibraryA("fortran.dll");
if (hdl)
{
auto the_func = reinterpret_cast< VERYSIMPLE_ptr >(GetProcAddress(hdl, "VERYSIMPLE"));
if (the_func)
the_func(input);
else
printf("no function\n");
FreeLibrary(hdl);
}
else
printf("no library\n");
}
int _tmain(int argc, _TCHAR* argv[])
{
int input1 = 12;
VERYSIMPLE(&input1);
system("pause");
return 0;
}
|
Last edited by StamK on Tue Jun 20, 2017 11:37 am; edited 1 time in total |
|
Back to top |
|
|
StamK
Joined: 12 Oct 2016 Posts: 159
|
Posted: Tue Jun 20, 2017 11:31 am Post subject: |
|
|
If using Qt with MSVC, then it is possible to use LoadLibraryA as above, or use Qt's QLibrary.
Code: |
#include <QLibrary>
void QW_GET_VALC(int* IWIN, int* IFLD, char* CH)
{
typedef void (*MyPrototype)(int* IWIN, int* IFLD, char* C);
MyPrototype myFunction = (MyPrototype) QLibrary::resolve("llib1", "QW_GET_VALC");
if (!myFunction)
{
qWarning()<<"\nError with function QW_GET_VALC";
}
if (myFunction)
myFunction( IWIN, IFLD, CH);
}
|
|
|
Back to top |
|
|
StamK
Joined: 12 Oct 2016 Posts: 159
|
Posted: Tue Jun 20, 2017 11:35 am Post subject: |
|
|
If passing a string from C++ to Fortran, be aware that you need to add in C++ the string size (strlen) as an extra int parameter at the end (as per Silverfrost's documentation).
In VC++ this works fine, but NOT in Qt. Not sure where the bug is (if it is Qt or Silverfrost), but a temporary solution is to create a VC++ DLL that calls the Fortran DLL, and Qt will call the VC++ DLL. |
|
Back to top |
|
|
PaulLaidler Site Admin
Joined: 21 Feb 2005 Posts: 7934 Location: Salford, UK
|
Posted: Tue Jun 20, 2017 12:48 pm Post subject: |
|
|
StamK
It isn't necessary to use LoadLibrary when calling a C++ function from 64 bit FTN95.
The standard approach is to use extern "C" and export the functions from the DLL. Then load the DLL via SLINK64.
In the Fortran code you should declare the DLL functions using C_EXTERNAL and, when passing strings, use STRING, INSTRING or OUTSTRING. |
|
Back to top |
|
|
StamK
Joined: 12 Oct 2016 Posts: 159
|
Posted: Tue Jun 20, 2017 1:13 pm Post subject: |
|
|
Hi Paul, thanks for the useful input - but this is for Fortran calling a C++ DLL, while I have described a C++ executable loading a Fortran DLL. |
|
Back to top |
|
|
PaulLaidler Site Admin
Joined: 21 Feb 2005 Posts: 7934 Location: Salford, UK
|
Posted: Tue Jun 20, 2017 3:34 pm Post subject: |
|
|
OK. I missed that.
Microsoft have a lib.exe app that creates mylib.lib from mylib.dll and mylib.def. |
|
Back to top |
|
|
StamK
Joined: 12 Oct 2016 Posts: 159
|
Posted: Wed Jun 21, 2017 4:59 pm Post subject: |
|
|
I had tried that lib.exe command and it didn't work at first, but I found out that Qt was failing for another reason.
I can confirm that Qt can access the DLL with the LIB file created that way.
The script below removes the extra lines, cleans the rows and adds EXPORTS at the beginning of the DEF file, which is then used to create the LIB file. Assuming that the script is called libcreate.bat and the DLL file is called LIBRARIES.DLL, then it can be called as
Code: | libcreate.bat LIBRARIES |
Code:
Code: |
@echo off
if "%1"=="" goto blank
call del %1.def
for /f "usebackq tokens=4,* delims=_ " %%i in (`dumpbin /exports "%1.dll"`) do echo %%i_%%j >> %1.def
call more +9 "%1.def" > "%1.def.new"
move /y "%1.def.new" "%1.def" >nul
copy %1.def temp.def
echo.EXPORTS>%1.def
type temp.def>>%1.def
del temp.def
lib /def:"%1.def" /out:"%1.lib" /MACHINE:X64
goto DONE
:BLANK
echo No DLL file name present
:DONE
|
|
|
Back to top |
|
|
StamK
Joined: 12 Oct 2016 Posts: 159
|
Posted: Fri Jun 30, 2017 3:43 pm Post subject: |
|
|
If the Fortran subroutine/function is inside a module, then it will be necessary to use the LoadLibraryA method as Silverfrost exports it as MODULENAME!FORTRAN_SUBROUTINE and cannot be found via extern "C" (or at least I am not able to put a name with an exclamation mark). Unless there is a way to tell Silverfrost to export in a different name?
Thanks |
|
Back to top |
|
|
PaulLaidler Site Admin
Joined: 21 Feb 2005 Posts: 7934 Location: Salford, UK
|
Posted: Fri Jun 30, 2017 9:14 pm Post subject: |
|
|
SLINK64 has an "alias" option that is described in the help file at FTN95->x64 platform->Using the 64 bit linker. It might be useful in this context. The "map" option may also be useful in that it may show the internal representation of the module function. |
|
Back to top |
|
|
|