stdlib_load_library#
Introduction#
The stdlib_load_library module loads shared libraries (.so, .dll, .dylib) at run time and resolves the symbols they export, in the spirit of numpy.ctypeslib.load_library. It is the building block for plugin architectures, for optional back-ends that must not be a link-time dependency, and for calling into libraries whose location is only known when the program runs.
Two layers are involved: the platform naming conventions (lib prefix, .so/.dll/.dylib suffix) are resolved in Fortran through stdlib_system, while the OS calls themselves (dlopen and dlsym on POSIX systems, LoadLibraryExW and GetProcAddress on Windows) live in the companion C file stdlib_load_library_c.c.
Error handling follows the usual stdlib convention: all procedures that can fail take an optional err argument of state_type. When it is present the error is returned through it, otherwise the program stops with an error message.
Note#
Symbols exported by the loaded library should be declared bind(C, name="...")
so that their names do not depend on the compiler that built it.
Warning#
Intrinsic assignment of a shared_library_type copies the OS handle, so closing one copy invalidates the procedure pointers obtained from any copy. The type has no finalizer on purpose: unloading is always explicit.
library_filename - Platform file name of a library#
Status#
Experimental
Description#
Returns the file name that a library would have on the current platform, e.g. fft becomes libfft.so on Linux, libfft.dylib on macOS and fft.dll on Windows. A name that already starts with lib is not decorated twice.
Syntax#
fname = library_filename (name)
Arguments#
name: Shall be a character(*) undecorated library name. Trailing blanks are ignored. This is an intent(in) argument.
Return value#
Returns a character(:), allocatable file name.
Example#
! Demonstrate run-time loading of a shared library
program example_load_library
use, intrinsic :: iso_c_binding, only: c_double, c_f_procpointer
use stdlib_load_library, only: shared_library_type, load_library, &
library_filename, shared_library_suffix
use stdlib_error, only: state_type
implicit none
abstract interface
!> Interface of the C function `double cos(double)`
function cos_t(x) bind(C) result(y)
import :: c_double
real(c_double), value :: x
real(c_double) :: y
end function cos_t
end interface
type(shared_library_type) :: libm
type(state_type) :: err
procedure(cos_t), pointer :: ccos
character(len=:), allocatable :: name
! Platform naming conventions, without loading anything
print *, "shared library suffix : ", shared_library_suffix()
print *, "library 'fft' is named: ", library_filename("fft")
! The C math functions live in a different library on each platform
select case (shared_library_suffix())
case (".dll")
name = "msvcrt" ! bare name: standard Windows search order
case (".dylib")
name = "libSystem.B.dylib" ! in the dyld shared cache, not on disk
case default
name = "libm.so.6"
end select
! With `err` present, a failure is returned instead of stopping the program
libm = load_library(name, err=err)
if (err%error()) then
! No C runtime under this name here: nothing else to demonstrate
print *, err%print()
error stop
end if
print *, "loaded : ", libm%filename()
! Resolve `cos` at run time and call it through a procedure pointer
if (libm%has_symbol("cos")) then
call c_f_procpointer(libm%symbol("cos"), ccos)
print *, "cos(0.0) = ", ccos(0.0_c_double)
end if
! Unloading is always explicit: there is no finalizer
call libm%close(err)
if (err%error()) print *, err%print()
end program example_load_library