Compaq Visual Fortran 6.6, Dynamic-Link Library (DLL) and Module - dll

I need to create and use dynamic-link library (DLL) for Fortran application using Compaq Visual Fortran 6.6. The following code works just fine:
PROGRAM AMAIN1
IMPLICIT NONE
REAL(8):: A,B,S
A = 1D0
B = 2D0
CALL SUBRO1(A,B,S)
PRINT*, 'S = ', S
END PROGRAM AMAIN1
SUBROUTINE SUBRO1(A,B,S)
!DEC$ ATTRIBUTES DLLEXPORT :: SUBRO1
IMPLICIT NONE
REAL(8):: A,B,S
S = A + B
RETURN
END SUBROUTINE SUBRO1
The result is correct:
S = 3.00000000000000
Press any key to continue
However, if I implement the same algorithm using the module, I get inconsistent result (i.e. zero):
PROGRAM AMAIN2
USE MODUL2
A = 1D0
B = 2D0
CALL SUBRO2
PRINT*, 'S = ', S
END PROGRAM AMAIN2
MODULE MODUL2
IMPLICIT NONE
REAL(8):: A,B,S
END MODULE MODUL2
SUBROUTINE SUBRO2
!DEC$ ATTRIBUTES DLLEXPORT :: SUBRO2
USE MODUL2
S = A + B
RETURN
END SUBROUTINE SUBRO2
The result is incorrect:
S = 0.000000000000000E+000
Press any key to continue
As can be seen above, DLL contains only subprogram in both cases (SUBRO1 and SUBRO2, respectively). I have built DLL and LIB files from the visual development environment. The second case (with the use of module) represents the structure of my large source-code so I need to resolve this issue. Any advice would be greatly appreciated.
BTW, the same algorithm without using the DLL works well and gives correct result:
PROGRAM AMAIN3
USE MODUL3
A = 1D0
B = 2D0
CALL SUBRO3
PRINT*, 'S = ', S
END PROGRAM AMAIN3
MODULE MODUL3
IMPLICIT NONE
REAL(8):: A,B,S
END MODULE MODUL3
SUBROUTINE SUBRO3
USE MODUL3
S = A + B
RETURN
END SUBROUTINE SUBRO3
The result is correct:
S = 3.00000000000000
Press any key to continue

You need to add:
!DEC$ ATTRIBUTES DLLEXPORT :: A,B,S
to the module, so that the main program can see the module variables from the DLL. Otherwise A, B and S are local variables.
Edit: January 16, 2019
I was able to log in to Bakhbergen's PC and eventually figured out the problem.
In CVF 6.6C (and the later Intel compilers), when you USE a module that has a DLLEXPORT directive, that turns into a DLLIMPORT, hence my advice above. But it wasn't always this way, and the version he has doesn't have that behavior. Before that change (which my memory says I lobbied for), you had to supply a separately compiled .mod where the source had DLLIMPORT instead of DLLEXPORT. When I did this, the program worked. I don't remember exactly which update had this change.
So what he needs to do is have two versions of MODUL2.f90, one with DLLEXPORT and one with DLLIMPORT. The DLLEXPORT version gets built into the DLL. The DLLIMPORT version would just be compiled (/c) and only the .mod used, not the ,obj, when linking the main program. Messy, I know, which is why we changed it.

Related

Importing a .dll in C into Fortran

I would like to call a .dll written in C into my Fortran code. I know that similar questions has been asked but the answers do not seem to work for me. I tried to read the file like follows:
use, intrinsic :: ISO_C_Binding
[...]
! ------------------------- including the controller-.dll -------------------------------
interface
subroutine Controller ( data, status, infile, out, msg) bind (C, NAME='Controller .dll')
use, intrinsic :: ISO_C_Binding
real(C_FLOAT), intent(inout) :: data(*)
integer(C_INT), intent(inout) :: status
character(kind=C_CHAR), intent(in) :: infile(*)
character(kind=C_CHAR), intent(in) :: out(*)
character(kind=C_CHAR), intent(inout) :: msg(*)
end subroutine controller
end interface
The syntax seems to be right since I do not get any error messages. But when I try to call the subroutine:
call Controller(ctrl_data,ctrl_status,ctrl_infile,ctrl_out,ctrl_msg)
I get the error message "error LNK2019: Reference to unlisted external symbol "Controller" in function "MAIN_STRUCTURE". main_structure.obj"
Furthermore, I am not entirely sure where to put the Contoller.dll. To make sure it can be found it is in the same folder as the source code as well as in the working repository.
I am using the ifort compiler and Visual Studio 2019.
I look forward to all responses. Thank you in advance.
David
I see three issues:
It looks unlikely that the C routine you're trying to call is named "Controller.dll". name in bind(C,name="...") should be the name of the function you're trying to call from the DLL, not the name of the DLL file.
If the library is to be statically linked to your program, you need an IMPORT library, not the .dll file. see How to generate an import library (LIB-file) from a DLL? on how you can export the definitions and build an import library file from a .DLL file
Finally, you need to ensure that the import library is properly set in your project, as LNK2019 error means the linker cannot find it. If all of the above items are OK, you need to ensure the path to the import library file is added to the project's dependencies (in Visual Studio: Properties->Linker->Input->Additional Dependencies)

Calling C dll from Fortran

How to call C DLL from Fortran? I don't code in Fortran but I need to do some testing on it.
Let say in the test.dll have functions:
Open()
Close()
How can I tell Fortran to use the test.dll?
I mean like C# we may use
[DllImport("test.dll")]
static extern uint Open();
I could not find any example that can help me. I would also prefer if you could provide a compiler that you use.
Update
I use Plato compiler.
I tried to load test.dll using this method. However, it popped up error message saying Error 29, Call to missing routine: _LOADLIBRARY
Here is the code. I found it online. So not sure if I'm doing it correctly.
program test
integer :: p
pointer :: q
p = loadlibrary ("test.dll"C) ! the C at the end says -
! add a null byte as in C
q = getprocaddress (p, "Open"C)
end
EDIT
Sorry. The DLL is using C and not C++

FORTRAN DLL calling convention from MapleSim / Modelica

Modelica modeling language supports calling functions from external FORTRAN DLLs, however, MapleSim only includes the support of calling functions from C DLLs even though it says it has full support for the Modelica language, and I have no choice but to use this FORTRAN DLL (I can't reprogram it in C nor I can use other Modelica evironments than MapleSim), so I'm not in the undocumented features realm.
I tried forcing MapleSim to include the FORTRAN DLL and created a custom component but its not working. This is the code I got when I opened the custom component: [relevant portion only]
model ExternalCode
function GETPSAT
input Real TC;
output Real PC;
external "C" PC = GETPSAT(TC)
annotation (
Library = "C:/Path/To/My/DLL/FORTRAN.dll", __Maplesoft_callconv = "stdcall");
end GETPSAT;
equation
(PC) = GETPSAT(TC);
annotation (
experiment(__Maplesoft_engine = 2));
end ExternalCode;
I know I should change external "C" to external "FORTRAN", but what should I should I do with __Maplesoft_callconv = "stdcall"? ie: what is the call convention for FORTRAN functions? (I know nothing on the subject of calling conventions).
Note the parent evnironment of MapleSim 6.1 (Maple 17) supports importing external functions from FORTRAN so I think there is a possibility MapleSim will support it despite it being undocumented.
edit: By the way, the DLL was compiled with Compaq visual fortran (I don't remember the version)
edit2: The function in the FORTRAN DLL is exported as follows:
FUNCTION GETPSAT(TC)
!DEC$ ATTRIBUTES ALIAS:'GETPSAT' :: GETPSAT
!DEC$ ATTRIBUTES DLLEXPORT :: GETPSAT
!DEC$ ATTRIBUTES VALUE :: TC
GETPSAT=PSAT11(TC)
RETURN
END
edit3: I don't know it this helps, but the same function can be called from C# like this:
[DllImport("C:\\Path\\To\\My\\DLL\\FORTRAN.dll")]
static extern float GETPSAT(float T);
This particular Fortran function should behave like a completely normal C function because of the attributes it has. Do not add any external(FORTRAN) or similar. Its name should be GETPSAT and it accepts 1 float TC by value.
Use the same "stdcal" attribute, it is default for the DEC, Compaq and Intel Fortran https://software.intel.com/sites/products/documentation/hpc/mkl/mkl_userguide_win/GUID-E74229B0-7389-46A6-9FCF-91CD6CD5B0E4.htm

Linking fortran module: "undefined reference"

I'm trying to write some functions/subroutines in a module that call another function in the same module and running into linker errors. A toy example displaying the same behavior:
!in test.f
module m1
implicit none
contains
real function mult(a, b)
real :: a
real :: b
mult = a * b
return
end function mult
real function sq(a)
real :: a, mult
sq = mult(a, a)
return
end function sq
end module m1
program main
use m1
write(*,*) sq(2.0)
end program
When I try to compile this, I run into trouble:
[christopher#archlinux metropolis]$ gfortran -ffree-form test.f
/tmp/ccpzdTLE.o: In function `__m1_MOD_sq':
test.f:(.text+0x20): undefined reference to `mult_'
collect2: error: ld returned 1 exit status
On the other hand, compiling only (gfortran -c -ffree-form test.f -Wall) runs with no complaint.
Now this looks for all the world like a compiler error---in the module it comes up with a reference to mult_ when it really ought to com up with __m1_MOD_sq---but I have a very hard time believing that this is a compiler bug rather than me doing something stupid.
DDG didn't turn up anything useful. Most of the similar problems ocurred in splitting the module off from one main file. In those cases, things worked when the module was in the same file as the program, which is not the case here. I looked at a number of pages on modules in Fortran and didn't see anything relevant.
Can anyone point me to appropriate documentation or, better yet, explain what's going on and how I can fix it?
You don't need to declare function mult in function sq, i.e., there is no need for "real :: mult". sq already "knows" about mult since it is in the same module. The interface of mult is known to sq since they are in the same module. The interface of mult and sq are known to the main program since it uses the module. Having both the module providing the interface and the declaration is confusing the compiler.

why do omp functions not work when constants are declared in a module?

i have a module 'gvars' defined for my global variable declarations. when i define
integer :: nthreads, max_threads, tid, omp_get_max_threads, omp_get_num_threads, omp_get_thread_num inside of my gvars module, the call maxthreads = omp_get_max_threads() in my main routine gives me the following error upon compilation:
maxthreads = omp_get_max_threads()
1
Error: Unclassifiable statement at (1)
but when i include the integer :: definitions above inside my main routine, it compiles just fine and gives me the desired results. if i even go as far as to define nthreads = -1 inside my gvars module, i am able to print out the correct value in my main routine so i know it is being included and defined correctly, it's just that for some reason i cannot have it as a return value from openmp functions.
why would this be?
is there any other way to keep these values as global variables and still define them in my main routine instead of a module?
if it matters, i am using gfortran to compile
The problem is not with the declaration of maxthreads, but with the declaration, on the same line, of omp_get_max_threads. As haraldkl showed, you need to use omp_lib instead, to automatically get access to the declarations of these functions.
(If for some reason you really don't want to do it that way, you can also add the statement external :: omp_get_max_threads, ... to the module.)
Not really an answer, but I do not know how else to put the code in here. Sorry...
module gvars
integer :: maxthreads
end module gvars
program test
use gvars
use omp_lib
implicit none
maxthreads = omp_get_max_threads()
end program test
compiled with:
gfortran -fopenmp test.f90
Where gfotran -v gives:
gcc version 4.4.5 (GCC)