Call a function in a shared library: Difference between revisions

add fortran
(add fortran)
Line 253:
end;
end;</lang>
 
=={{header|Fortran}}==
Works on Linux with GNU gcc and gfortran 5.1.1
This is a slightly modified version of [[Call a foreign-language function]] task.
 
A simple "C" function add_n in add_n.c
<lang c>
double add_n(double* a, double* b)
{
return *a + *b;
}
</lang>
 
compile it
 
gcc -c -shared -fPIC add_n.c
 
We can also use fortran function in the shared library which should be, however, implemented using C interoperability module.
 
File add_nf.f90
<lang fortran>
function add_nf(a,b) bind(c, name='add_nf')
use, intrinsic :: iso_c_binding
implicit none
real(c_double), intent(in) :: a,b
real(c_double) :: add_nf
 
add_nf = a + b
end function add_nf
</lang>
Compile it
 
gfortran -c -shared -fPIC add_nf.f90
 
 
create shared library shared_lib_new.so containing two functions "add_n" and "add_nf"
 
gcc -shared -fPIC add_nf.o add_n.o -o shared_lib_new.so
 
Using C binding load shared_lib_new.so and call functions add_n, add_nf dynamically.
 
File shared_lib_new_test.f90
<lang fortran>
!-----------------------------------------------------------------------
!module dll_module
!-----------------------------------------------------------------------
module dll_module
use iso_c_binding
implicit none
private ! all by default
public :: os_type, dll_type, load_dll, free_dll, init_os_type, init_dll
! general constants:
! the number of bits in an address (32-bit or 64-bit).
integer, parameter :: bits_in_addr = c_intptr_t*8
! global error-level variables:
integer, parameter :: errid_none = 0
integer, parameter :: errid_info = 1
integer, parameter :: errid_warn = 2
integer, parameter :: errid_severe = 3
integer, parameter :: errid_fatal = 4
 
integer :: os_id
 
type os_type
character(10) :: endian
character(len=:), allocatable :: newline
character(len=:), allocatable :: os_desc
character(1) :: pathsep
character(1) :: swchar
character(11) :: unfform
end type os_type
 
type dll_type
integer(c_intptr_t) :: fileaddr
type(c_ptr) :: fileaddrx
type(c_funptr) :: procaddr
character(1024) :: filename
character(1024) :: procname
end type dll_type
 
! interface to linux API
interface
function dlopen(filename,mode) bind(c,name="dlopen")
! void *dlopen(const char *filename, int mode);
use iso_c_binding
implicit none
type(c_ptr) :: dlopen
character(c_char), intent(in) :: filename(*)
integer(c_int), value :: mode
end function
 
function dlsym(handle,name) bind(c,name="dlsym")
! void *dlsym(void *handle, const char *name);
use iso_c_binding
implicit none
type(c_funptr) :: dlsym
type(c_ptr), value :: handle
character(c_char), intent(in) :: name(*)
end function
 
function dlclose(handle) bind(c,name="dlclose")
! int dlclose(void *handle);
use iso_c_binding
implicit none
integer(c_int) :: dlclose
type(c_ptr), value :: handle
end function
end interface
 
contains
 
 
!-----------------------------------------------------------------------
!Subroutine init_dll
!-----------------------------------------------------------------------
subroutine init_dll(dll)
implicit none
type(dll_type), intent(inout) :: dll
dll % fileaddr = 0
dll % fileaddrx = c_null_ptr
dll % procaddr = c_null_funptr
dll % filename = " "
dll % procname = " "
end subroutine init_dll
 
!-----------------------------------------------------------------------
!Subroutine init_os_type
!-----------------------------------------------------------------------
subroutine init_os_type(os_id,os)
implicit none
integer, intent(in) :: os_id
type(os_type), intent(inout) :: os
 
select case (os_id)
case (1) ! Linux
 
os % endian = 'big_endian'
os % newline = achar(10)
os % os_desc = 'Linux'
os % pathsep = '/'
os % swchar = '-'
os % unfform = 'unformatted'
 
case (2) ! MacOS
 
os % endian = 'big_endian'
os % newline = achar(10)
os % os_desc = 'MacOS'
os % pathsep = '/'
os % swchar = '-'
os % unfform = 'unformatted'
 
case default
 
end select
 
end subroutine init_os_type
 
!-----------------------------------------------------------------------
!Subroutine load_dll
!-----------------------------------------------------------------------
subroutine load_dll (os, dll, errstat, errmsg )
! this subroutine is used to dynamically load a dll.
 
 
type (os_type), intent(in) :: os
type (dll_type), intent(inout) :: dll
integer, intent( out) :: errstat
character(*), intent( out) :: errmsg
 
integer(c_int), parameter :: rtld_lazy=1
integer(c_int), parameter :: rtld_now=2
integer(c_int), parameter :: rtld_global=256
integer(c_int), parameter :: rtld_local=0
 
errstat = errid_none
errmsg = ''
 
select case (os%os_desc)
case ("Linux","MacOS")
! load the dll and get the file address:
dll%fileaddrx = dlopen( trim(dll%filename)//c_null_char, rtld_lazy )
if( .not. c_associated(dll%fileaddrx) ) then
errstat = errid_fatal
write(errmsg,'(i2)') bits_in_addr
errmsg = 'the dynamic library '//trim(dll%filename)//' could not be loaded. check that the file '// &
'exists in the specified location and that it is compiled for '//trim(errmsg)//'-bit systems.'
return
end if
 
! get the procedure address:
dll%procaddr = dlsym( dll%fileaddrx, trim(dll%procname)//c_null_char )
if(.not. c_associated(dll%procaddr)) then
errstat = errid_fatal
errmsg = 'the procedure '//trim(dll%procname)//' in file '//trim(dll%filename)//' could not be loaded.'
return
end if
 
case ("Windows")
errstat = errid_fatal
errmsg = ' load_dll not implemented for '//trim(os%os_desc)
 
case default
errstat = errid_fatal
errmsg = ' load_dll not implemented for '//trim(os%os_desc)
end select
return
end subroutine load_dll
 
!-----------------------------------------------------------------------
!Subroutine free_dll
!-----------------------------------------------------------------------
subroutine free_dll (os, dll, errstat, errmsg )
 
! this subroutine is used to free a dynamically loaded dll
type (os_type), intent(in) :: os
type (dll_type), intent(inout) :: dll
integer, intent( out) :: errstat
character(*), intent( out) :: errmsg
 
integer(c_int) :: success
 
errstat = errid_none
errmsg = ''
 
select case (os%os_desc)
case ("Linux","MacOS")
 
! close the library:
success = dlclose( dll%fileaddrx )
if ( success /= 0 ) then
errstat = errid_fatal
errmsg = 'the dynamic library could not be freed.'
return
else
errstat = errid_none
errmsg = ''
end if
 
case ("Windows")
 
errstat = errid_fatal
errmsg = ' free_dll not implemented for '//trim(os%os_desc)
 
case default
errstat = errid_fatal
errmsg = ' free_dll not implemented for '//trim(os%os_desc)
end select
 
return
end subroutine free_dll
end module dll_module
 
 
 
!-----------------------------------------------------------------------
!Main program
!-----------------------------------------------------------------------
program test_load_dll
use, intrinsic :: iso_c_binding
use dll_module
implicit none
 
! interface to our shared lib
abstract interface
function add_n(a,b)
use, intrinsic :: iso_c_binding
implicit none
real(c_double), intent(in) :: a,b
real(c_double) :: add_n
end function add_n
end interface
 
type(os_type) :: os
type(dll_type) :: dll
integer :: errstat
character(1024) :: errmsg
type(c_funptr) :: cfun
procedure(add_n), pointer :: fproc
 
call init_os_type(1,os)
call init_dll(dll)
 
dll%filename="/full_path_to/shared_lib/shared_lib_new.so"
! name of the procedure in shared_lib
! c version of the function
dll%procname="add_n"
 
write(*,*) "address: ", dll%procaddr
 
call load_dll(os, dll, errstat, errmsg )
write(*,*)"load_dll: errstat=", errstat
write(*,*) "address: ", dll%procaddr
 
call c_f_procpointer(dll%procaddr,fproc)
 
write(*,*) "add_n(2,5)=",fproc(2.d0,5.d0)
 
call free_dll (os, dll, errstat, errmsg )
write(*,*)"free_dll: errstat=", errstat
 
! fortran version
dll%procname="add_nf"
 
call load_dll(os, dll, errstat, errmsg )
write(*,*)"load_dll: errstat=", errstat
write(*,*) "address: ", dll%procaddr
 
call c_f_procpointer(dll%procaddr,fproc)
 
write(*,*) "add_nf(2,5)=",fproc(2.d0,5.d0)
 
call free_dll (os, dll, errstat, errmsg )
write(*,*)"free_dll: errstat=", errstat
 
 
end program test_load_dll
 
</lang>
Compile test program
 
gfortran shared_lib_new_test.f90 -ldl -o shared_lib_new_test.x
 
{{out}}
./shared_lib_new_test.x
<pre>
address: 0
load_dll: errstat= 0
address: 47476893497132
add_n(2,5)= 7.0000000000000000
free_dll: errstat= 0
load_dll: errstat= 0
address: 47476893497088
add_nf(2,5)= 7.0000000000000000
free_dll: errstat= 0
</pre>
 
Finally, using C language interoperability you can call every foreign-language function in fortran if you are able to write some additional wrapper function in C language.
 
 
=={{header|J}}==
Anonymous user