基于ISO_C_BINDING实现Fortran版C结构体malloc/free等效逻辑及dlopen插件机制Hello World组件开发咨询
Got it, let's build out your Fortran plugin to match the C API exactly, using iso_c_binding to ensure full compatibility with your dlopen-based plugin manager. Here's a complete, working implementation, followed by a breakdown of key details:
Complete Fortran Plugin Code
module f_alg_mod use, intrinsic :: iso_c_binding, only : c_int, c_ptr, c_char, c_null_char, c_f_pointer, c_associated implicit none (type, external) ! Match the C Alg_t struct exactly using C binding type, bind(C) :: Alg_t type(c_ptr) :: name ! Corresponds to C's const char* type(c_ptr) :: svcLocator ! Corresponds to C's void* end type Alg_t ! Declare C standard library functions we need for memory and I/O interface function c_malloc(size) bind(C, name="malloc") import :: c_ptr, c_int integer(c_int), intent(in) :: size type(c_ptr) :: c_malloc end function c_malloc subroutine c_free(ptr) bind(C, name="free") import :: c_ptr type(c_ptr), intent(in) :: ptr end subroutine c_free end interface contains ! Equivalent to c_alg_new: allocates and initializes the Alg_t struct function f_alg_new(name, svcLocator) result(ctx_ptr) bind(C, name="f_alg_new") type(c_ptr), intent(in) :: name, svcLocator type(c_ptr) :: ctx_ptr type(Alg_t), pointer :: ctx ! Allocate memory using C's malloc to match the C API's memory model ctx_ptr = c_malloc(sizeof(Alg_t, c_int)) if (.not. c_associated(ctx_ptr)) return ! Convert the opaque C pointer to a Fortran pointer for easy field access call c_f_pointer(ctx_ptr, ctx) ! Assign input values to the struct fields ctx%name = name ctx%svcLocator = svcLocator end function f_alg_new ! Equivalent to c_alg_del: frees the allocated struct subroutine f_alg_del(self) bind(C, name="f_alg_del") type(c_ptr), intent(in) :: self call c_free(self) end subroutine f_alg_del ! Equivalent to c_alg_ini: prints initialization messages function f_alg_ini(self) result(err) bind(C, name="f_alg_ini") type(c_ptr), intent(in) :: self integer(c_int) :: err type(Alg_t), pointer :: ctx character(len=:), allocatable :: fort_name ! Convert the void* pointer back to our Alg_t struct pointer call c_f_pointer(self, ctx) ! Convert C's null-terminated string to a Fortran allocatable string call c_str_to_fortran(ctx%name, fort_name) ! Print initialization output (using Fortran's native print for simplicity) print '(A, A, A)', ">>> initialize [", trim(fort_name), "]..." print '(A, A, A)', ">>> initialize [", trim(fort_name), "]... [done]" err = 0 ! Return success code deallocate(fort_name) end function f_alg_ini ! Equivalent to c_alg_exe: prints execution messages function f_alg_exe(self) result(err) bind(C, name="f_alg_exe") type(c_ptr), intent(in) :: self integer(c_int) :: err type(Alg_t), pointer :: ctx character(len=:), allocatable :: fort_name call c_f_pointer(self, ctx) call c_str_to_fortran(ctx%name, fort_name) print '(A, A, A)', ">>> execute [", trim(fort_name), "]..." print '(A, A, A)', ">>> execute [", trim(fort_name), "]... [done]" err = 0 deallocate(fort_name) end function f_alg_exe ! Equivalent to c_alg_fin: prints finalization messages function f_alg_fin(self) result(err) bind(C, name="f_alg_fin") type(c_ptr), intent(in) :: self integer(c_int) :: err type(Alg_t), pointer :: ctx character(len=:), allocatable :: fort_name call c_f_pointer(self, ctx) call c_str_to_fortran(ctx%name, fort_name) print '(A, A, A)', ">>> finalize [", trim(fort_name), "]..." print '(A, A, A)', ">>> finalize [", trim(fort_name), "]... [done]" err = 0 deallocate(fort_name) end function f_alg_fin ! Helper: Convert C null-terminated string to Fortran allocatable string subroutine c_str_to_fortran(c_str, fort_str) type(c_ptr), intent(in) :: c_str character(len=:), allocatable, intent(out) :: fort_str character(kind=c_char), pointer :: char_ptr(:) integer :: str_len if (.not. c_associated(c_str)) then fort_str = "" return end if ! Calculate length of the C string call c_f_pointer(c_str, char_ptr, [huge(0)]) str_len = 0 do while (char_ptr(str_len+1) /= c_null_char) str_len = str_len + 1 end do ! Allocate and copy the string to Fortran format allocate(character(len=str_len) :: fort_str) fort_str = transfer(char_ptr(1:str_len), fort_str) end subroutine c_str_to_fortran end module f_alg_mod ! Dummy program unit (required by some Fortran compilers for shared library builds) program dummy use f_alg_mod implicit none end program dummy
Key Details & Explanations
1. Matching the C Struct Layout
Your initial struct used a fixed-length character array for name, which doesn't align with C's const char*. Instead, we use type(c_ptr) to hold the C string pointer directly—this ensures the struct layout is identical to the C version, which is critical for interoperability.
2. Exporting Exact Symbol Names
Every function exposed to the plugin manager uses bind(C, name="f_alg_new") (and similar for other functions). This forces Fortran to use the exact symbol name your plugin manager looks for, avoiding compiler-generated suffixes like _ that would break dlsym lookups.
3. Memory Management Consistency
We bind directly to C's malloc and free to handle memory allocation/deallocation. This ensures the plugin manages its own memory, as required by your system—your plugin manager only needs to pass the opaque pointer between functions.
4. C String Handling
The c_str_to_fortran helper converts C's null-terminated strings to Fortran allocatable strings, making it easy to work with the name field in native Fortran print statements. We avoid using C's fprintf here because Fortran's print is simpler and avoids linking complexities.
5. Compiling the Plugin
To compile this into a shared library compatible with dlopen:
gfortran -shared -fPIC -o libfalg.so f_alg.f90
This generates libfalg.so, which your existing plugin manager code can load directly with dlopen and look up symbols like "f_alg_new" or "f_alg_ini" without modifications.
内容的提问来源于stack exchange,提问作者sbinet

