将函数指针从 Fortran 传递到 C,与 c_f_procpointer 相反
Passing function pointers from Fortran to C, reverse of c_f_procpointer
我正在尝试使用基于 this 的 Fortran 在 Linux 中加载动态库,但我想添加对 dladdr
的支持。基本上我的代码是:
procedure(proto), pointer :: my_func
type(c_funptr) :: funptr
funptr = dlsym(handle,'myfunction')
if (c_associated(funptr)) then
call c_f_procpointer(funptr, my_func)
else
my_func => myfunction
end if
write(6,*) dladdr(???,info)
缺少许多细节,但重要的部分是我想传递给 dladdr
由 my_func
表示的实际函数,它是指向其他函数的过程指针: dlsym
或明确的 Fortran 关联。所以我的问题是我不知道在 ???
.
中放什么
如果我使用 funptr
或 c_funloc(myfunction)
,我会成功(非零 return 值),所以至少 dladdr
调用有效。但是我不能使用 my_func
(gfortran 编译器抱怨错误的参数类型),并且 c_loc(my_func)
和 c_funloc(my_func)
都失败(零 return 值)。我认为问题是我必须"dereference" my_func
一次,然后得到那个C函数指针。我该怎么做?
完整代码:
dlfcn.f90
MODULE DLFCN
USE ISO_C_BINDING
IMPLICIT NONE
PRIVATE
PUBLIC :: DLOpen, DLSym, DLClose, DLError, DLAddr ! DL API
! Valid modes for mode in DLOpen:
INTEGER, PARAMETER, PUBLIC :: RTLD_LAZY=1, RTLD_NOW=2, RTLD_GLOBAL=256, RTLD_LOCAL=0
! Obtained from the output of the previously listed C program
INTERFACE ! All we need is interfaces for the prototypes in <dlfcn.h>
FUNCTION DLOpen(file,mode) RESULT(handle) BIND(C,NAME="dlopen")
! void *dlopen(const char *file, int mode);
USE ISO_C_BINDING
CHARACTER(C_CHAR), DIMENSION(*), INTENT(IN) :: file
! C strings should be declared as character arrays
INTEGER(C_INT), VALUE :: mode
TYPE(C_PTR) :: handle
END FUNCTION
FUNCTION DLSym(handle,name) RESULT(funptr) BIND(C,NAME="dlsym")
! void *dlsym(void *handle, const char *name);
USE ISO_C_BINDING
TYPE(C_PTR), VALUE :: handle
CHARACTER(C_CHAR), DIMENSION(*), INTENT(IN) :: name
TYPE(C_FUNPTR) :: funptr ! A function pointer
END FUNCTION
FUNCTION DLClose(handle) RESULT(status) BIND(C,NAME="dlclose")
! int dlclose(void *handle);
USE ISO_C_BINDING
TYPE(C_PTR), VALUE :: handle
INTEGER(C_INT) :: status
END FUNCTION
FUNCTION DLError() RESULT(error) BIND(C,NAME="dlerror")
! char *dlerror(void);
USE ISO_C_BINDING
TYPE(C_PTR) :: error
END FUNCTION
! dladdr is a Glibc extension, not POSIX
FUNCTION DLAddr(funptr,info) RESULT(output) BIND(C,NAME="dladdr")
! int dladdr(void *addr, Dl_info *info)
USE ISO_C_BINDING
TYPE(C_FUNPTR) :: funptr ! A function pointer
TYPE(C_PTR) :: info
INTEGER(C_INT) :: output
END FUNCTION
END INTERFACE
END MODULE
test.f90
program test
use iso_c_binding
use dlfcn
implicit none
abstract interface
function proto(s,i)
character(len=*) :: s
integer :: i
integer :: proto
end function proto
end interface
procedure(proto), pointer :: my_func
character(kind=c_char,len=1024) :: libname,funname
type(c_ptr) :: handle=c_null_ptr
type(c_funptr) :: funptr=c_null_funptr
type(c_ptr) :: info
libname = 'libblas.so'
funname = 'xerbla_'
handle = dlopen(trim(libname)//c_null_char, int(ior(rtld_global,rtld_lazy),kind=c_int))
if (c_associated(handle)) then
write (6,*) trim(libname),' loaded'
else
write(6,*) 'error loading '//trim(libname)
end if
funptr = dlsym(handle,trim(funname)//c_null_char)
if (c_associated(funptr)) then
call c_f_procpointer(funptr, my_func)
else
my_func => myfunction
end if
write(6,*) dladdr(funptr,info)
write(6,*) dladdr(c_funloc(myfunction),info)
write(6,*) dladdr(c_funloc(my_func),info)
!write(6,*) dladdr(my_func,info)
contains
function myfunction(srname,info)
character(len=*) :: srname
integer :: info
integer :: myfunction
myfunction = 0
end function myfunction
end program test
编译:
gfortran dlfcn.f90 test.f90 -ldl -o test
运行:
$ ./test
libblas.so loaded
1
1
0
如果我启用注释掉的行,编译器会说:
write(6,*) dladdr(my_func,info)
1
Error: Invalid procedure argument at (1)
这是有道理的,因为 my_func
是一个 Fortran 过程指针,而不是 dladdr
期望的 C 指针。
Fortran 源代码中 DLAddr 的接口与相应 C 函数的记录接口不匹配。两个参数都需要 VALUE 属性。
还值得注意的是 C_FUNLOC 的参数(这是您在 ??? 中需要的)需要是一个可互操作的过程,即具有 BIND 属性的过程。 Fortran 代码中过程指针声明中使用的接口中缺少此属性(如果您希望它具有相同的特性,my_function
中也缺少此属性)。
我正在尝试使用基于 this 的 Fortran 在 Linux 中加载动态库,但我想添加对 dladdr
的支持。基本上我的代码是:
procedure(proto), pointer :: my_func
type(c_funptr) :: funptr
funptr = dlsym(handle,'myfunction')
if (c_associated(funptr)) then
call c_f_procpointer(funptr, my_func)
else
my_func => myfunction
end if
write(6,*) dladdr(???,info)
缺少许多细节,但重要的部分是我想传递给 dladdr
由 my_func
表示的实际函数,它是指向其他函数的过程指针: dlsym
或明确的 Fortran 关联。所以我的问题是我不知道在 ???
.
如果我使用 funptr
或 c_funloc(myfunction)
,我会成功(非零 return 值),所以至少 dladdr
调用有效。但是我不能使用 my_func
(gfortran 编译器抱怨错误的参数类型),并且 c_loc(my_func)
和 c_funloc(my_func)
都失败(零 return 值)。我认为问题是我必须"dereference" my_func
一次,然后得到那个C函数指针。我该怎么做?
完整代码:
dlfcn.f90
MODULE DLFCN
USE ISO_C_BINDING
IMPLICIT NONE
PRIVATE
PUBLIC :: DLOpen, DLSym, DLClose, DLError, DLAddr ! DL API
! Valid modes for mode in DLOpen:
INTEGER, PARAMETER, PUBLIC :: RTLD_LAZY=1, RTLD_NOW=2, RTLD_GLOBAL=256, RTLD_LOCAL=0
! Obtained from the output of the previously listed C program
INTERFACE ! All we need is interfaces for the prototypes in <dlfcn.h>
FUNCTION DLOpen(file,mode) RESULT(handle) BIND(C,NAME="dlopen")
! void *dlopen(const char *file, int mode);
USE ISO_C_BINDING
CHARACTER(C_CHAR), DIMENSION(*), INTENT(IN) :: file
! C strings should be declared as character arrays
INTEGER(C_INT), VALUE :: mode
TYPE(C_PTR) :: handle
END FUNCTION
FUNCTION DLSym(handle,name) RESULT(funptr) BIND(C,NAME="dlsym")
! void *dlsym(void *handle, const char *name);
USE ISO_C_BINDING
TYPE(C_PTR), VALUE :: handle
CHARACTER(C_CHAR), DIMENSION(*), INTENT(IN) :: name
TYPE(C_FUNPTR) :: funptr ! A function pointer
END FUNCTION
FUNCTION DLClose(handle) RESULT(status) BIND(C,NAME="dlclose")
! int dlclose(void *handle);
USE ISO_C_BINDING
TYPE(C_PTR), VALUE :: handle
INTEGER(C_INT) :: status
END FUNCTION
FUNCTION DLError() RESULT(error) BIND(C,NAME="dlerror")
! char *dlerror(void);
USE ISO_C_BINDING
TYPE(C_PTR) :: error
END FUNCTION
! dladdr is a Glibc extension, not POSIX
FUNCTION DLAddr(funptr,info) RESULT(output) BIND(C,NAME="dladdr")
! int dladdr(void *addr, Dl_info *info)
USE ISO_C_BINDING
TYPE(C_FUNPTR) :: funptr ! A function pointer
TYPE(C_PTR) :: info
INTEGER(C_INT) :: output
END FUNCTION
END INTERFACE
END MODULE
test.f90
program test
use iso_c_binding
use dlfcn
implicit none
abstract interface
function proto(s,i)
character(len=*) :: s
integer :: i
integer :: proto
end function proto
end interface
procedure(proto), pointer :: my_func
character(kind=c_char,len=1024) :: libname,funname
type(c_ptr) :: handle=c_null_ptr
type(c_funptr) :: funptr=c_null_funptr
type(c_ptr) :: info
libname = 'libblas.so'
funname = 'xerbla_'
handle = dlopen(trim(libname)//c_null_char, int(ior(rtld_global,rtld_lazy),kind=c_int))
if (c_associated(handle)) then
write (6,*) trim(libname),' loaded'
else
write(6,*) 'error loading '//trim(libname)
end if
funptr = dlsym(handle,trim(funname)//c_null_char)
if (c_associated(funptr)) then
call c_f_procpointer(funptr, my_func)
else
my_func => myfunction
end if
write(6,*) dladdr(funptr,info)
write(6,*) dladdr(c_funloc(myfunction),info)
write(6,*) dladdr(c_funloc(my_func),info)
!write(6,*) dladdr(my_func,info)
contains
function myfunction(srname,info)
character(len=*) :: srname
integer :: info
integer :: myfunction
myfunction = 0
end function myfunction
end program test
编译:
gfortran dlfcn.f90 test.f90 -ldl -o test
运行:
$ ./test
libblas.so loaded
1
1
0
如果我启用注释掉的行,编译器会说:
write(6,*) dladdr(my_func,info)
1
Error: Invalid procedure argument at (1)
这是有道理的,因为 my_func
是一个 Fortran 过程指针,而不是 dladdr
期望的 C 指针。
Fortran 源代码中 DLAddr 的接口与相应 C 函数的记录接口不匹配。两个参数都需要 VALUE 属性。
还值得注意的是 C_FUNLOC 的参数(这是您在 ??? 中需要的)需要是一个可互操作的过程,即具有 BIND 属性的过程。 Fortran 代码中过程指针声明中使用的接口中缺少此属性(如果您希望它具有相同的特性,my_function
中也缺少此属性)。