C-Fortran 字符串互操作性

Posted

技术标签:

【中文标题】C-Fortran 字符串互操作性【英文标题】:C-Fortran character string interoperability 【发布时间】:2012-02-24 19:37:26 【问题描述】:

美好的一天。抱歉,我的问题的定义可能不太容易理解,也许有些不准确 - 我刚刚开始尝试自己编程。不过,我会尽力解释清楚。

我有用 Fortran 编写的数学 DLL。

例如,有一个函数。该函数用于将日志文件的名称解析到dll中以观察计算。

  integer function initLog(
 *                          int_parameter,
 *                          char_parameter,
 *                          char_parameter_length,
 *                        )
 *bind(C, name = "initLog");
  use, intrinsic :: ISO_C_BINDING;
  !DEC$ATTRIBUTES DLLEXPORT::initLog

    integer(C_INT),         value :: parameter;
    character(C_CHAR), intent(in) :: char_parameter(char_parameter_length);
    integer(C_INT),         value :: char_parameter_length;

    ...

    some_other_variable = char_parameter(1:1)(1:char_parameter_length); 

  end function;

通常我使用 MATLAB 来处理 dll,因此必须使用 .mex 文件直接从 MATLAB 调用我的函数。在 .mex 文件中,我有一些用 C 语言编写的接口代码,提供 MATLAB 和 dll 之间的接口。例如,上述函数的 C 接口为:

int doSmth(const int   int_parameter,
           const char* char_parameter,
           const int   char_parameter_length,);

然后我使用 loadLibrary 和 GetProcAddress 来获取函数。这很好用。

但是,现在我需要在 Fortran 中创建 .exe 测试文件,该文件将使用我的 dll。因此,我必须通过将我的 dll 链接到导入的 .lib 库来将其链接到 exe。此可执行文件的另一个选项是通过命令行将日志文件的名称作为参数。因此,首先我尝试仅从 exe 文件中传递日志文件文件名,如下所示:

program test
  use dll_name;
  use ifport;
  implicit none;
  ...
  integer :: log_init_status;
  ...
  log_init_status = init_log(2, 'logfile.log', len('logfile.log'));
  ...
end program

这在发行版中运行良好,但在调试中返回“严重 (664):超出范围:子字符串结束位置 '11' 大于字符串长度 '1'”错误。但是一开始我并没有发现这个错误并继续编写代码。这就是我现在得到的:

program test
  use dll_name;
  use ifport;
  use ISO_C_BINDING;
  implicit none;
  ...
  character*255     :: log_flag_char;
  integer(C_INT)    :: log_flag;
  character*255     :: filename;
  character(C_CHAR) :: log_filename;
  integer(C_INT)    :: log_filename_length;

  ....

  call getarg(5, log_flag_char);
  read(log_flag_char, *) log_flag;

  call getarg(6, log_filename);
  log_filename_length = len(log_filename);

  log_init_status = analyticsLogInit(log_flag, log_filename, log_filename_length);

  ...

end program

这很好用,但只占用了 log_filename 的第一个字符(“C:\abcd\logfile.log”转换为“C”)。如果我改变了

字符(C_CHAR) :: log_filename;

字符(C_CHAR) :: log_filename(255);

,我遇到了 2 个问题:首先,我的 log_filename 的长度等于 255(尽管可以通过修剪来修复),其次 - 也是主要的 - 我再次得到“严重 (664):超出范围:子字符串结束位置 '255' 大于字符串长度 '1'"。

如果我改变了

log_init_status = analyticsLogInit(log_flag, log_filename, log_filename_length);

log_init_status = analyticsLogInit(log_flag, C_LOC(log_filename), log_filename_length);

,我得到关于虚拟参数类型与实际参数类型不同的错误。

我自己有一种感觉,显示的 664 错误来自 dll 中的这一行:

some_other_variable = char_parameter(1:1)(1:char_parameter_length);

。我应该在我的 exe 中写类似

字符*255 :: log_filename;

而不是

字符 :: log_filename(255);

但是如何使用 (C_CHAR) 解析它?

我意识到这一切都很混乱,这一切都来自于理解的泄漏,但这几乎是我第一次认真的编程体验。

【问题讨论】:

【参考方案1】:

我只浏览了您的问题,但需要注意的一件事是声明 character 变量或命名常量的方式。您可以提供两个类型参数:长度和种类。如果在声明中不使用相应的关键字,则第一个参数指定长度,第二个参数(如果存在)指定种类。 所以如果你想声明一个长度为255、种类为C_CHAR的字符变量,你可以通过以下任何一种方式来实现:

character(len=255, kind=C_CHAR) :: log_filename
character(255, kind=C_CHAR)     :: log_filename
character(255, C_CHAR)          :: log_filename
character(kind=C_CHAR, len=255) :: log_filename
character(kind=C_CHAR)          :: log_filename*255

另一方面,以下语法(这是您使用的语法)声明了一个长度为 C_CHARcharacter 变量,无论其值如何。

character(C_CHAR) :: log_filename

哦,下一个语法声明了一个包含 255 个元素的数组,每个元素都是一个长度为 C_CHAR 的字符变量。

character(C_CHAR) :: log_filename(255)

所以结论是,应该花一些时间来研究在 fortran 中声明字符实体的特性。

【讨论】:

感谢您的回复。当然,我知道在 Fortran 中对我来说有很多要研究的东西。尽管如此,我确信“character(C_CHAR) :: log_filename”语法可以正常工作,因为它适用于整数和双精度数(并且因为它写在我阅读的文章中) 唉,将“character(C_CHAR)”更改为“character(kind=C_CHAR)”什么也没做。另外,现在我不明白“some_other_variable = char_parameter(1:1)(1:char_parameter_length);”如果它被声明为 "character(C_CHAR), intent(in) :: char_parameter(char_parameter_length);"

以上是关于C-Fortran 字符串互操作性的主要内容,如果未能解决你的问题,请参考以下文章

通过互操作将字符串数组从 C# 传递到 C++

在 COM 互操作方法中将字符串作为参数传递

字符串操作之语序颠倒/大小写互转

互操作 C++ 时释放分配的内存

Excel 互操作在我的数字格式字符串中添加斜杠

Clojure - Java 互操作:传递一个 OutputStream 参数并返回一个字符串