How to use loadlibrary and getprocaddress from gfortran? - fortran

I'm trying to learn how to call a function in a fortran dll from a fortran executable on windows. I'm working with gfortran 4.7 and photran in eclipse.
My test dll has a single function in hello.f90:
hello.f90
subroutine hello
implicit none
print *, "Hello World!"
end subroutine hello
with the following makefile:
all:
gfortran -Wall -c hello.f90
gfortran -shared -o hello.dll hello.o
Dependency Walker confirms that the function "hello_" is exported.
Now I'm trying to build a program that calls it dynamically. I've built the following based on examples I've found online, but it doesn't compile:
main.f90
program main
implicit none
integer :: p
pointer (q, hello)
p = loadlibrary("hello.dll")
q = getprocaddress(p, "hello_")
call hello
end program main
makefile
all:
gfortran -Wall -pedantic -fcray-pointer main.f90
The error message is that function LoadLibrary (and getprocaddress) has no IMPLICIT type. I suspect that means those functions aren't defined and I need to include their headers somehow. Is that right? I've found a declaration for loadlibrary in c:\mingw\include\winbase.h
cheers,
Marc

LoadLibrary and GetProcAddress are Windows API routines. Like any non-intrinsic function, you need to declare the type of those functions and, given the nature of those API's, you also need to go further and provide the full interface.
If you are compiling for 32 bit Windows then those API's use the stdcall calling convention. You need to use gfortran source directive extensions to designate that.
(On 64 bit Windows stdcall is defined to be the same as the C calling convention - the source directive has no effect.)
For calling convention control, if I change your DLL code to:
SUBROUTINE hello() BIND(C, NAME='hello')
IMPLICIT NONE
PRINT *, 'Hello'
END SUBROUTINE hello
then the following main program will load the resulting DLL and execute that procedure.
PROGRAM Main
USE, INTRINSIC :: ISO_C_BINDING, ONLY: &
C_F_PROCPOINTER, C_FUNPTR, C_INTPTR_T, &
C_NULL_CHAR, C_CHAR, C_ASSOCIATED
IMPLICIT NONE
INTERFACE
FUNCTION LoadLibrary(lpFileName) BIND(C,NAME='LoadLibraryA')
USE, INTRINSIC :: ISO_C_BINDING, ONLY: C_INTPTR_T, C_CHAR
IMPLICIT NONE
CHARACTER(KIND=C_CHAR) :: lpFileName(*)
!GCC$ ATTRIBUTES STDCALL :: LoadLibrary
INTEGER(C_INTPTR_T) :: LoadLibrary
END FUNCTION LoadLibrary
FUNCTION GetProcAddress(hModule, lpProcName) &
BIND(C, NAME='GetProcAddress')
USE, INTRINSIC :: ISO_C_BINDING, ONLY: &
C_FUNPTR, C_INTPTR_T, C_CHAR
IMPLICIT NONE
!GCC$ ATTRIBUTES STDCALL :: GetProcAddress
TYPE(C_FUNPTR) :: GetProcAddress
INTEGER(C_INTPTR_T), VALUE :: hModule
CHARACTER(KIND=C_CHAR) :: lpProcName(*)
END FUNCTION GetProcAddress
END INTERFACE
ABSTRACT INTERFACE
SUBROUTINE hello_intf() BIND(C)
IMPLICIT NONE
END SUBROUTINE hello_intf
END INTERFACE
INTEGER(C_INTPTR_T) :: module_handle
TYPE(C_FUNPTR) :: proc_address
PROCEDURE(hello_intf), BIND(C), POINTER :: my_proc
!****
module_handle = LoadLibrary(C_CHAR_'hello.dll' // C_NULL_CHAR)
IF (module_handle == 0) STOP 'Unable to load DLL'
proc_address = GetProcAddress( module_handle, &
C_CHAR_'hello' // C_NULL_CHAR )
IF (.NOT. C_ASSOCIATED(proc_address)) &
STOP 'Unable to obtain procedure address'
CALL C_F_PROCPOINTER(proc_address, my_proc)
CALL my_proc
END PROGRAM Main

Related

flang can not find iso_c_bindings while encapsulating a C function from a static library

I have a static library named mylib.a written in C and I am trying to do my first call to C from Fortran.
I want to invoke in Fortran the following function contained in the C library:
double get_step(double value);
I try the following Fortran code:
! TEST FORTRAN WRAPPER
module test
use, intrinsic :: iso_c_binding
! Interface to C routine
! double get_step(double value);
interface
real(c_double) function _get_step(value) bind(C, 'get_step')
use, intrinsic :: iso_c_binding, only : c_double
real(c_double) :: value
end function
end interface
end module
I try to compile it like this:
$ flang test.f90 mylib.a
F90-F-0004-Unable to open MODULE file iso_c_binding.mod (test.f90: 5)
F90/x86-64 FreeBSD Flang - 1.5 2017-05-01: compilation aborted
What am I doing wrong?
Can I use the same name get_step for the Fortran function?

Combining submodules, procedure arguments and polymorphic types

I am currently trying to split my code into modules and submodules. I have a number of procedures which take procedures as arguments, and in some cases the arguments of those procedure arguments are polymorphic. When splitting these procedures into submodules my code no longer compiles.
As a minimum working example, I have a module file m.f90:
module m
implicit none
! Interface to `foo`, which simply calls `lambda(input)`.
interface foo
module subroutine foo(input,lambda)
implicit none
class(*), intent(in) :: input
procedure(TestLambda) :: lambda
end subroutine
end interface
! Interface to `bar`, which simply calls `lambda(input)`
interface bar
module subroutine bar(input,lambda)
implicit none
class(*), intent(in) :: input
procedure(TestLambda) :: lambda
end subroutine
end interface
! Interface defining the `lambda` arguments of `foo` and `bar`.
interface TestLambda
subroutine TestLambda(input)
implicit none
class(*), intent(in) :: input
end subroutine
end interface
end module
and a submodule file m_sub.f90:
submodule (m) m_sub
contains
! Implementation of `foo`.
module subroutine foo(input,lambda)
implicit none
class(*), intent(in) :: input
procedure(TestLambda) :: lambda
call lambda(input)
end subroutine
! Implementation of `bar`.
! This is identical to `foo`, except that the interface is implicit.
module procedure bar
call lambda(input)
end procedure
end submodule
and, for completeness, a program file main.f90:
program test
use m
implicit none
write(*,*) 'Hello, World!'
call test_lambda(1)
call foo(1, test_lambda)
call bar(1, test_lambda)
contains
subroutine test_lambda(input)
implicit none
class(*), intent(in) :: input
write(*,*) 'Hello, World!'
end subroutine
end program
I am compiling this using CMake, with a CMakeLists.txt file:
cmake_minimum_required(VERSION 3.5 FATAL_ERROR)
enable_language(Fortran)
set(CMAKE_Fortran_MODULE_DIRECTORY ${CMAKE_BINARY_DIR}/mod/)
if("${CMAKE_Fortran_COMPILER_ID}" STREQUAL "GNU")
set(CMAKE_Fortran_FLAGS "-W -Wall -Wextra -pedantic -fcheck=all -std=f2008")
elseif("${CMAKE_Fortran_COMPILER_ID}" STREQUAL "Intel")
set(CMAKE_Fortran_FLAGS "-stand f08 -warn all")
endif()
project(test)
add_executable(test main.f90 m.f90 m_sub.f90)
If I compile with gfortran 10.1.0 then foo compiles and runs fine on its own, but trying to compile bar gives the error
26 | call lambda(input)
| 1
Error: Explicit interface required for polymorphic argument at (1)
If I instead compile with ifort 2021.1 I get the compile-time warning (for both foo and bar)
warning #8889: Explicit declaration of the EXTERNAL attribute is required. [LAMBDA]
call lambda(input)
-----------^
And then the linker error (for both foo and bar)
m_sub.f90.o: In function `m_mp_foo_':
undefined reference to `lambda_'
As far as I can tell, what I'm doing is valid Fortran. So am I seeing compiler bugs / unimplemented features? Or am I trying to do something which isn't allowed by the standard?

Syntax error with TYPE(*) with Intel Fortran Compiler 2015 (Mac OS X)

When compiling the following Fortran program using IFORT 2015 (for Mac OS X):
MODULE X
USE, INTRINSIC :: iso_c_binding, ONLY: c_intptr_t
IMPLICIT NONE
INTERFACE
INTEGER(c_int) FUNCTION process(variable) BIND(C, name = "_process")
USE, INTRINSIC :: iso_c_binding, ONLY: c_int
!DEC$ ATTRIBUTES NO_ARG_CHECK :: variable
!GCC$ ATTRIBUTES NO_ARG_CHECK :: variable
TYPE(*), INTENT(IN) :: variable
END FUNCTION
END INTERFACE
END MODULE
... it gives the following error:
example.f90(13): error #5082: Syntax error, found ',' when expecting one of: , <END_OF_STATEMENT> ;
TYPE(*), INTENT(IN) :: variable
-------^
This is how I compile the Fortran program (example.f90):
ifort example.f90 -free -m64 -c -fPIC -02 -o wrapper.o
The assumed-type declaration type(*) is a feature of Fortran 2018 (or TS29113 addition to Fortran 2008) that is first supported by Intel Fortran Compiler in release 16.0.
You should use a later version of the compiler or rewrite the code to avoid using this feature.

Is this linker error in 32 bit Windows gFortran a bug?

The following code compiles to 64 bit but the use of Fortran Generic Interfaces seems to confuse the gcc attribute in 32 bit (required to call the 32 bit STDCALL API). The code is a stripped down version of the f03gl project which I am attempting to build for Windows (I couldn't create a smaller repro).
A rework of the code (shown at the bottom) without the generic interface and passing arguments direct does compile and work. I'm hoping I won't have to rewrite all of the calls to work around this issue.
The error is;
callglutbad.o:callglutbad.f90:(.text+0x27): undefined reference to `glutInit'
callglut.f90 (fails 32 bit compilation)
MODULE GlutModule
USE ISO_C_BINDING
IMPLICIT NONE
PUBLIC glutInit
INTERFACE glutInit
MODULE PROCEDURE glutInit_f03
END INTERFACE glutInit
INTERFACE
SUBROUTINE glutInit_gl(pargc, argv) BIND(C,NAME="glutInit")
IMPORT
#ifdef x86
!GCC$ ATTRIBUTES stdcall :: glutInit
#endif
INTEGER(C_INT) :: pargc
TYPE(C_PTR), INTENT(IN) :: argv
END SUBROUTINE glutInit_gl
END INTERFACE
CONTAINS
SUBROUTINE glutInit_f03()
INTEGER(C_INT) :: argcp=1
TYPE(C_PTR), DIMENSION(1), TARGET :: argv=C_NULL_PTR
CHARACTER(C_CHAR), DIMENSION(1), TARGET :: empty_string=C_NULL_CHAR
argv(1)=C_LOC(empty_string)
CALL glutInit_gl(argcp, C_LOC(argv))
END SUBROUTINE
END MODULE GlutModule
program main
USE GlutModule
PRINT *,"Calling glutInit"
call glutInit()
PRINT *,"Called glutInit"
end program main
build.bat
REM 32 bit
#setlocal
#SET PATH=%PATH%;C:\msys64\mingw32\bin\
gfortran -Dx86 -cpp -c callglut.f90 -o callglut.o
gcc callglut.o -o callglut32.exe ..\x86\lib\freeglut.lib -lgfortran
#endlocal
REM 64 bit
#setlocal
#SET PATH=%PATH%;C:\msys64\mingw64\bin\
gfortran -cpp -c callglut.f90 -o callglut.o
gcc callglut.o -o callglut64.exe ..\x64\lib\freeglut.lib -lgfortran
#endlocal
The generic interface is used to call glutInit without parameters from Fortran (the parameters are filled in by the proxy subroutine).
callglut.f90 (compiles and runs on both platforms)
MODULE GlutModule
USE ISO_C_BINDING
IMPLICIT NONE
PUBLIC glutInit
INTERFACE
SUBROUTINE glutInit(pargc, argv) BIND(C,NAME="glutInit")
IMPORT
#ifdef x86
!GCC$ ATTRIBUTES stdcall :: glutInit
#endif
INTEGER(C_INT) :: pargc
TYPE(C_PTR), INTENT(IN) :: argv
END SUBROUTINE glutInit
END INTERFACE
END MODULE GlutModule
program main
USE GlutModule
PRINT *,"Calling glutInit"
call glutInit(0,C_NULL_PTR)
PRINT *,"Called glutInit"
end program main
Am I seeing a compiler bug with the attribute statement not being applied to a generic interface or is it something that I am doing? I suspect this and the 32 bit STDCALL name mangling from glutInit to _glutInit#8 is not being performed.
I am using gfortran under msys2 (GNU Fortran (Rev2, Built by MSYS2 project) 7.1.0) on 64 bit Windows with both 32 and 64 bit compilers.
It doesn't make sense to apply the STDCALL attribute to an identifier that is only a generic name. From the compiler's perspective, a generic name is a common label for a number of procedures. While the individual specific procedures may have different calling conventions, the label for them does not.
If you want to tell the compiler that a specific procedure has a certain calling convention, use the name that the compiler knows the specific procedure by, in the relevant declaration.
!GCC$ ATTRIBUTES stdcall :: glutInit_gl

ieee_arithmetic intrinsic module in gfortran

I need to use gfortran to compile a library that is dependent on ieee_arithmetic. However, it is found that gfortran can not identify this module.
For example with the code a.f90
program test
use,intrinsic :: ieee_arithmetic
real :: x
read *, x
if (ieee_is_nan(x)) then
print *, "Nan"
else
print *, "Not NaN"
end if
end program test
I have the following message when compiling
$ gfortran a.f90
a.f90:2.19:
use,intrinsic :: ieee_arithmetic
1
Fatal Error: Can't find an intrinsic module named 'ieee_arithmetic' at (1)
How can I let gfortran know where the ieee_arithmetic intrinsic module is?
ifort is found to be able to use the ieee_arithmetic module. But I wish to make gfortran work for this case.
The IEEE modules are supported as of GFortran version 5. See https://gcc.gnu.org/gcc-5/changes.html If you are using an older version, you should see the error message you have shown in your post.