创建一个返回char *的C函数的FORTRAN接口

现在,我已经对此持续了大约一个星期,并且在论坛之后搜索了论坛,以便清楚地解释如何从C向FORTRAN发送char *。 为了让事情更令人沮丧,从FORTRAN向C发送char *参数是直截了当的……

从FORTRAN向C发送char *参数(这很好):

// The C header declaration (using __cdecl in a def file): extern "C" double GetLoggingValue(char* name); 

来自FORTRAN:

 ! The FORTRAN interface: INTERFACE REAL(8) FUNCTION GetLoggingValue [C, ALIAS: '_GetLoggingValue'] (name) USE ISO_C_BINDING CHARACTER(LEN=1, KIND=C_CHAR), DIMENSION(*), INTENT(IN) :: name END FUNCTION GetLoggingValue END INTERFACE ! Calling the function: GetLoggingValue(user_name) 

当尝试使用类似逻辑从C返回char *时,我遇到问题后出现问题。 我觉得应该尝试的一个尝试是:

 // The C declaration header (using __cdecl in a def file): extern "C" const char* GetLastErrorMessage(); 

和FORTRAN接口:

 INTERFACE FUNCTION GetLastErrorMessage [C, ALIAS: '_GetLastErrorMessage'] () USE ISO_C_BINDING CHARACTER(LEN=1, KIND=C_CHAR), DIMENSION(255), :: GetLastErrorMessage END FUNCTION GetLastErrorMessage END INTERFACE 

(我不能真正使用DIMENSION(*),所以我已经超大到255.)

应该返回一个指向255个C风格字符数组的指针 – 但如果确实如此,我就无法将其转换为有意义的字符串。 在实践中,它返回一组随机字符,从Wingdings到’bell’字符……

我也试图回归:

  • 指向CHARACTER的指针(LEN = 255,KIND = C_CHAR)。
  • 字面上的字符(LEN = 255,KIND = C_CHAR)。
  • 一个INTEGER(C_SIZE_T),并试图将其精炼成指向字符串数组的指针。
  • 一个人物。
  • 等等

如果有人能给我一个如何做到这一点的例子,我将非常感激……

最好的祝福,

麦克风

对于C交互,动态长度的字符串总是有点棘手。 一种可能的解决方案是使用指针。

首先是一个简单的例子,你必须将一个空字符终止的字符串移交给一个C函数。 如果你真的只传入了字符串,你必须确保用c_null_char来完成它,因此这个方向非常简单。 以下是LuaFortran界面中的示例:

 subroutine flu_getfield(L, index, k) type(flu_State) :: L integer :: index character(len=*) :: k integer(kind=c_int) :: c_index character(len=len_trim(k)+1) :: c_k c_k = trim(k) // c_null_char c_index = index call lua_getfield(L%state, c_index, c_k) end subroutine flu_getfield 

而lua_getfield的界面如下:

 subroutine lua_getfield(L, index, k) bind(c, name="lua_getfield") use, intrinsic :: iso_c_binding type(c_ptr), value :: L integer(kind=c_int), value :: index character(kind=c_char), dimension(*) :: k end subroutine lua_getfield 

而C代码接口是:

 void lua_getfield (lua_State *L, int idx, const char *k) 

现在是一个更复杂的案例,我们必须处理来自C的具有动态长度的返回字符串。 到目前为止我发现的最便携的解决方案是使用指针。 这是一个带指针的示例,其中字符串由C-Routine(也来自上面提到的Aotus库)给出:

 function flu_tolstring(L, index, len) result(string) type(flu_State) :: L integer :: index integer :: len character,pointer,dimension(:) :: string integer :: string_shape(1) integer(kind=c_int) :: c_index integer(kind=c_size_t) :: c_len type(c_ptr) :: c_string c_index = index c_string = lua_tolstring(L%state, c_index, c_len) len = int(c_len,kind=kind(len)) string_shape(1) = len call c_f_pointer(c_string, string, string_shape) end function flu_tolstring 

其中lua_tolstring具有以下接口 :

 function lua_tolstring(L, index, len) bind(c, name="lua_tolstring") use, intrinsic :: iso_c_binding type(c_ptr), value :: L integer(kind=c_int), value :: index integer(kind=c_size_t) :: len type(c_ptr) :: lua_tolstring end function lua_tolstring 

最后,这里尝试阐明如何将c_ptr解释为Fortran字符串:假设您有一个指向字符串的c_ptr:

 type(c_ptr) :: a_c_string 

它的长度由具有以下类型的len变量给出:

 integer(kind=c_size_t) :: stringlen 

您希望在Fortran中指向字符串的指针中获取此字符串:

 character,pointer,dimension(:) :: string 

所以你做了映射:

 call c_f_pointer(a_c_string, string, [ stringlen ]) 

感谢Heraldkl给我解决这个非常令人沮丧的问题。 我发布了我最终实现的内容,它将指针转换为接口的角色,这意味着最终的应用程序可以调用C函数而无需了解指针转换:

C函数:

 // The C declaration header (using __cdecl in a def file): extern "C" const char* GetLastErrorMessage(); 

FORTRAN接口模块:

 MODULE mINTERFACES USE ISO_C_BINDING INTERFACE FUNCTION GetLastErrorMessagePtr [C, ALIAS: '_GetLastErrorMessage'] () USE ISO_C_BINDING TYPE(C_PTR) :: GetLastErrorMessagePtr END FUNCTION GetLastErrorMessagePtr END INTERFACE CONTAINS ! this must be after all INTERFACE definitions FUNCTION GetLastErrorMessage() USE ISO_C_BINDING CHARACTER*255 :: GetLastErrorMessage CHARACTER, POINTER, DIMENSION(:) :: last_message_array CHARACTER*255 last_message INTEGER message_length CALL C_F_POINTER(GetLastErrorMessagePtr(), last_message_array, [ 255 ]) DO 10 i=1, 255 last_message(i:i+1) = last_message_array(i) 10 CONTINUE message_length = LEN_TRIM(last_message(1:INDEX(last_message, CHAR(0)))) GetLastErrorMessage = last_message(1:message_length-1) END FUNCTION GetLastErrorMessage 

并从FORTRAN程序调用此函数:

 USE MINTERFACES PRINT *, "--> GetLastErrorMessage: '", TRIM(GetLastErrorMessage()), "'" 

我再次感谢heraldkl提供这个解决方案 – 我不知道如何在没有他的输入的情况下做到这一点。

这个post有点旧,但由于我有类似的问题(可能还有其他人会),我还是会发一个答案。

如果由于某种原因,C字符串为空,则上面发布的代码将导致分段错误。 此外,不需要返回255-chars字符串(在使用之前可能需要修剪),因为Fortran 2003/2008支持返回可分配实体的函数。 使用上面发布的所有信息,我最终得到了以下函数,它获取了一个C字符串(指针),并返回相应的Fortran字符串; 如果C字符串为null,则返回“NULL”,类似于在类似情况下打印的C的“(null)”:

 function C_to_F_string(c_string_pointer) result(f_string) use, intrinsic :: iso_c_binding, only: c_ptr,c_f_pointer,c_char,c_null_char type(c_ptr), intent(in) :: c_string_pointer character(len=:), allocatable :: f_string character(kind=c_char), dimension(:), pointer :: char_array_pointer => null() character(len=255) :: aux_string integer :: i,length call c_f_pointer(c_string_pointer,char_array_pointer,[255]) if (.not.associated(char_array_pointer)) then allocate(character(len=4)::f_string); f_string="NULL"; return end if aux_string=" " do i=1,255 if (char_array_pointer(i)==c_null_char) then length=i-1; exit end if aux_string(i:i)=char_array_pointer(i) end do allocate(character(len=length)::f_string) f_string=aux_string(1:length) end function C_to_F_string 

我一直在努力使用这些互操作性function。 我认为你的界面应该声明

 CHARACTER(KIND=C_CHAR),DIMENSION(*) :: getlasterrormessage 

并且,当您调用该函数时,您传递一个相应的Fortran字符变量,其长度等于或大于您希望返回的C字符数组的长度。

由于您似乎拥有英特尔Fortran,请查看提供的代码示例,他们为此提供了一个完整的示例。

我想你知道你发布的东西在语法上是不正确的Fortran?

我也一直在努力调用一个返回字符串的C例程,上面的答案非常有用但是因为我几乎不知道C和答案有点混乱我只是想贡献我使用C指针的解决方案,我做了没有设法使用上述任何其他提案。 我调用的C程序打开一个单独的窗口来浏览文件名。

 program test use iso_c_binding implicit none ! AC function that returns a string need a pointer to the array of single char type (c_ptr) :: C_String_ptr ! This is the Fortran equivalent to a string of single char character (len=1, kind=c_char), dimension(:), pointer :: filchar=>null() ! Interface to a C routine which opens a window to browse for a file to open interface function tinyopen(typ) bind(c, name="tinyopen") use iso_c_binding implicit none integer(c_int), value :: typ type (C_Ptr) :: tinyopen end function tinyopen end interface character (len=256) :: filename integer typ,jj typ=1 C_String_ptr = tinyopen(typ) ! convert C pointer to Fortran pointer call c_f_pointer(C_String_ptr,filchar,[256]) filename=' ' if(.not.associated(filchar)) then ! if no characters give error message write(*,*)'No file name' else ! convert the array of single characters to a Fortran character jj=1 do while(filchar(jj).ne.c_null_char) filename(jj:jj)=filchar(jj) jj=jj+1 enddo endif write(*,*)'Text is: ',trim(filename) end program test 

希望这个例子能让下一个具有相同问题的例子变得更容易。

在Fortran中,项目需要声明为“character(kind = c_char,len = 1),dimension(255)”而不是len = 255。 这将创建一个长度为1的字符数组,这是您在C端需要的。 令人困惑的是,有一个exception允许Fortran将字符串与一维数组进行匹配。

你的意思是你想从C调用Fortran程序? 请参阅此示例: 从C调用FORTRAN子例程 。

编辑:ifort和gfortran都说在此上下文中函数返回时不允许使用数组。 这使得返回字符串作为函数参数从C到Fortran比使用字符串作为参数更难(在上面的链接中示例)…您必须使用指针然后使用c_f_pointer Fortran内部函数将C字符串转换为Fortran字符串,正如haraldkl所解释的那样。 这是另一个代码示例:

 program test_c_func use iso_c_binding implicit none type (C_PTR) :: C_String_ptr character (len=1, kind=c_char), dimension (:), pointer :: ErrChars => null () character (len=255) :: ErrString integer :: i INTERFACE FUNCTION GetLastErrorMessage () bind (C, name="GetLastErrorMessage" ) USE ISO_C_BINDING type (C_PTR) :: GetLastErrorMessage END FUNCTION GetLastErrorMessage END INTERFACE C_String_ptr = GetLastErrorMessage () call c_f_pointer ( C_String_ptr, ErrChars, [255] ) ErrString = " " xfer_string: do i=1, 255 if ( ErrChars (i) == c_null_char) exit xfer_string ErrString (i:i) = ErrChars (i) end do xfer_string write (*, '( "Fortran: <", A, ">" )' ) trim (ErrString) end program test_c_func 

如果您知道字符串的长度,那么Pap的答案可以大大简化:

 function stringc2f(n, cstr) result(fstr) integer, intent(in) :: n type(c_ptr), intent(in) :: cstr character(:), allocatable :: fstr character(n, kind=c_char), pointer :: fptr call c_f_pointer(cstr, fptr) fstr = fptr end function 

上面的函数接受带有字符串和字符串长度的C指针,并将副本作为Fortran字符串返回。