2012-04-02 70 views
7

我已经在这里停留了大约一个星期,并且已经在论坛之后搜索了关于如何从C发送char *的清晰解释的论坛FORTRAN。为了使事情更令人沮丧,发送一个char *从FORTRAN到C的说法是直线前进...创建FORTRAN接口到一个返回char *的C函数*

发送一个char *从FORTRAN参数C(这工作正常):

// 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 

(我不能随便使用维度(*),所以我已经过大,以255。 )

This 应该返回一个指向255个C风格字符数组的指针 - 但是如果是这样,我一直无法将其转换为有意义的字符串。在实践中,它返回一个随机的字符集,从任何地方宋体的“钟”字......

我还试图返回:

  • 指向字符(LEN = 255, KIND = C_CHAR)。
  • 字面字符(LEN = 255,KIND = C_CHAR)。
  • A INTEGER(C_SIZE_T),并试图将它转换为指向字符串数组的指针。
  • A CHARACTER。

如果任何人都可以给我如何做到这一点的例子,我将非常感激......

最好的问候,

迈克

+0

你正在使用什么工具链? – 2012-04-02 08:07:26

+0

我使用VC++编译器,但在纯C中编译接口。FORTAN是Visual Fortran 2011,我认为它是FORTRAN 90.但是,这是针对已发布的API,因此它必须可以从可用的许多种类调用... – 2012-04-02 08:19:15

回答

12

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

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

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的interface样子:

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-例程(也来自上述 夜猴文库)中给出一个例子:

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具有以下interface

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 ]) 
+0

谢谢,heraldkl!上面的例子是接口到一个无效的C函数 - 当C函数的返回类型是char *时,这种方法会工作吗?我的一般操作范例是为我的API函数返回结果,而不是通过void子例程传递它们,并且在使用所有其他数据类型时工作正常除了字符:-( – 2012-04-02 09:37:31

+0

我猜上面的方法也适用于非void功能,我没有看到一个理由,为什么这应该受到返回值的影响 – haraldkl 2012-04-02 10:10:32

+0

我当然乐意使用指针 - 实际上,这是我的第一种方法。恐怕我遇到了麻烦上面提供的 - 是否有必要将C指针转换为一个常量字符数组为FORTRAN字符串?换句话说,如果我有一个TYPE(C_PTR)和一个字符串长度,我需要的最小值是多少将此转换为FORTRAN字符串?对不起,我是钝的... – 2012-04-02 10:51:04

2

我总是与这些互操作性功能进行斗争。我认为你的接口应该声明

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

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

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

我想你知道你发布的内容在语法上不正确Fortran吗?

+0

你的意思是::之前的逗号?对不起 - 剪切和粘贴错误!我会给上面的一个去... – 2012-04-02 09:06:08

+0

我刚刚尝试过,但我得到的编译错误与我在尝试类似时发生的编译错误相同:“错误错误#6688:数组规范没有POINTER属性的函数名称必须是显式形状数组(R505.9)。“这就是为什么我尝试基本相同,但255而不是* - 但没有成功:-( – 2012-04-02 09:09:12

+0

我试过使用“CHARACTER(KIND = C_CHAR),DIMENSION(255):: GetLastErrorMessage”,而不是读入一个相同规格的字符数组,但是这显然返回了一个255个随机字符的数组... – 2012-04-02 09:13:27

1

在Fortran中的项目需要被宣布为 “字(KIND = c_char,LEN = 1),尺寸(255)” 拉泽比len = 255。这将创建一个长度为1的字符数组,这是您在C端需要的。令人困惑的是,有一个例外允许Fortran将字符串与一维数组进行匹配。

你是说你想从C调用一个Fortran程序?看到这个例子:Calling a FORTRAN subroutine from C

编辑:这两个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 
+0

Hello M.S. - 我尝试过字符(kind = c_char,len = 1),dimension(255),它似乎返回了一些东西,但它看起来第一个元素是一个NULL字符 - 它转换为空字符串。由于它直接从C中调用时返回正确的字符串,所以我一直假定故障在于FORTRAN接口。从C调用FORTRAN看起来并不那么困难 - 或者至少,我可以很容易地将字符串从FORTRAN转换为C. – 2012-04-02 13:44:37

5

我要感谢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提供这种解决方案 - 我不知道如何在没有他的输入的情况下做到这一点。

+0

完全没问题,很高兴它有帮助;) – haraldkl 2012-04-03 18:53:06

4

此线程有点老,但由于我有类似的问题(可能其他人会),我反正发表一个答案。

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

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 
+0

我发布了一些简化你的子程序[下](http://stackoverflow.com/a/40618786/479532)。 – 2016-11-15 20:09:38

0

如果知道字符串的长度,然后巴氏的answer以上可大大简化:

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字符串返回。

+0

我不确定这是现代Fortran,而我的答案不是;实际上这两个代码都是Fortran 2003.无论如何,你的代码比较简单,但是需要传递字符串长度('n')作为参数。当然,事先知道字符串的长度会使代码变得更小,但也使得该函数不太有用。在大多数情况下,你只是不知道C字符串是多久。 – Pap 2017-06-15 11:18:33

+0

@Pap,你是对的。我已经澄清了我的答案以反映这一点。事实上,两者都是F2003。我使用了LHS的自动分配,但这也是F2003。对于我的应用程序,我可以访问C和Fortran代码,因此将C字符串的长度传递到接口中并不是问题,这大大简化了事情。 – 2017-06-16 14:34:26

相关问题