将 char 数组从 c++ 传递到 fortran

Posted

技术标签:

【中文标题】将 char 数组从 c++ 传递到 fortran【英文标题】:passing char arrays from c++ to fortran 【发布时间】:2012-04-15 15:43:04 【问题描述】:

我在 (f90) 时遇到问题。

这是我的 c++ 文件,'cmain.cxx':

#include <iostream>

using namespace std;

extern "C" int ftest_( char (*string)[4] );

int main() 
    char string[2][4];

    strcpy(string[0],"abc");
    strcpy(string[1],"xyz");

    cout << "c++: string[0] = '" << string[0] << "'" << endl;
    cout << "c++: string[1] = '" << string[1] << "'" << endl;

    ftest_(string);

    return 0;

这是我的 fortran 文件,'ftest.f90':

SUBROUTINE FTEST(string)

CHARACTER*3 string(2)
CHARACTER*3 expected(2)
data expected(1)/'abc'/
data expected(2)/'xyz'/

DO i=1,2
    WRITE(6,10) i,string(i)
10  FORMAT("fortran: string(",i1,") = '", a, "'" )

    IF(string(i).eq.expected(i)) THEN
        WRITE(6,20) string(i),expected(i)
20      FORMAT("'",a,"' equals '",a,"'")
    ELSE
        WRITE(6,30) string(i),expected(i)
30      FORMAT("'",a,"' does not equal '",a,"'")
    END IF
ENDDO

RETURN
END

构建过程是:

gfortran -c -m64   ftest.f90 
g++ -c  cmain.cxx
gfortran -m64 -lstdc++ -gnofor_main -o test ftest.o cmain.o

编辑:请注意,可执行文件也可以通过以下方式构建:

g++ -lgfortran -o test ftest.o cmain.o

另外,当我运行 OSX 10.6 时,需要 -m64 标志。

执行'test'的输出是:

c++: string[0] = 'abc'
c++: string[1] = 'xyz'
fortran: string(1) = 'abc'
'abc' equals 'abc'
fortran: string(2) = 'xy'
'xy' does not equal 'xyz'

在 ftest.f90 中声明 'string' 和 'expected' 字符数组,大小为 4,即:

CHARACTER*4 string(2)
CHARACTER*4 expected(2)

重新编译会得到以下输出:

c++: string[0] = 'abc'
c++: string[1] = 'xyz'
fortran: string(1) = 'abc'
'abc' does not equal 'abc '
fortran: string(2) = 'xyz'
'xyz' does not equal 'xyz '

在'cmain.cxx'中声明大小为3的字符数组,即:

extern "C" int ftest_( char (*string)[3] );

int main() 
    char string[2][3];

并恢复到fortran文件中的原始大小(3),即:

CHARACTER*3 string(2)
CHARACTER*3 expected(2)

重新编译会得到以下输出:

c++: string[0] = 'abcxyz'
c++: string[1] = 'xyz'
fortran: string(1) = 'abc'
'abc' equals 'abc'
fortran: string(2) = 'xyz'
'xyz' equals 'xyz'

所以最后一种情况是唯一可行的,但在这里我将 3 个字符分配给大小为 3 的 char 数组,这意味着缺少终止的 '\0',并导致输出 'abcxyz' - 这是我的预期应用程序不可接受。

任何帮助将不胜感激,这让我发疯了!

【问题讨论】:

我没有看到任何迹象表明您正在使用 Fortran 的“与 C 的互操作性”功能,这些功能旨在缓解您面临的此类问题。我建议你使用它们。 嗨,马克,我有一个外部提供的 fortran 程序,我希望通过 c++ 与之交互。我无法修改 fortran 代码。您能找到一种无需修改原始 fortran 文件即可使其正常工作的方法吗? 这取决于你正在做的实际接口类型,但如果你有 Fortran 代码,即使你不能改变它,你也可以给它添加一个 C 接口模块。该模块只会使一些转换函数对 C 可见。作为奖励,您可以去掉函数名称中的尾随 _。 【参考方案1】:

C 字符串以零结尾,而按照惯例,fortran 字符串是用空格填充的,但大小固定。您不应该期望能够在不进行一些转换的情况下将 C 字符串传递给 fortran。

例如:

#include <algorithm>

void ConvertToFortran(char* fstring, std::size_t fstring_len,
                      const char* cstring)

    std::size_t inlen = std::strlen(cstring);
    std::size_t cpylen = std::min(inlen, fstring_len);

    if (inlen > fstring_len)
    
        // TODO: truncation error or warning
    

    std::copy(cstring, cstring + cpylen, fstring);
    std::fill(fstring + cpylen, fstring + fstring_len, ' ');

然后您可以将其与ftest 的 3 或 4 长度版本一起使用:

#include <iostream>
#include <ostream>
extern "C" int ftest_( char string[][4] );

void ConvertToFortran(char* fstring, std::size_t fstring_len,
                      const char* cstring);

int main()

    char cstring[2][4] =  "abc", "xyz" ;
    char string[2][4];

    ConvertToFortran(string[0], sizeof string[0], cstring[0]);
    ConvertToFortran(string[1], sizeof string[1], cstring[1]);

    std::cout << "c++: string[0] = '" << cstring[0] << "'" << std::endl;
    std::cout << "c++: string[1] = '" << cstring[1] << "'" << std::endl;

    ftest_(string);

    return 0;

【讨论】:

【参考方案2】:

我建议按照“高性能标记”的建议在 Fortran 端使用 ISO C 绑定。您已经在使用“extern C”。 Fortran 2003 的 ISO C 绑定(目前在大多数 Fortran 95/部分 Fortan 2003 编译器中实现)使其成为一种独立于编译器和平台的方法。 Charles Bailey 描述了两种语言中字符串之间的差异。这个 *** 问题有一个代码示例:Calling a FORTRAN subroutine from C

如果您不想修改现有的 Fortran 代码,您可以在 C++ 代码和现有的 Fortran 代码之间编写一个“粘合”例程。使用 ISO C 绑定在 Fortran 中编写粘合例程会更加可靠和稳定,因为这将基于语言标准的特性。

【讨论】:

【参考方案3】:

给出的例子太重了,只要你不想传递多个字符串,你可以使用“隐藏”长度参数...

extern "C" void function_( const char* s, size_t len )    
  std::string some_string( s, 0, len );
  /// do your stuff here ...
  std::cout << "using string " << some_string << std::endl;
  /// ...


你可以像 fortran 一样调用它

  call function( "some string or other" )

您无需为单独的复制操作而烦恼,因为 std::string 构造函数可以为您完成所有这些操作。

【讨论】:

也许它们是重量级的(但这不是我的意见,添加bind(C) 很简单),但它们是可移植的。 Fortran 标准中没有任何内容表明函数符号将是带有下划线的名称,并且没有任何内容可以保证隐藏参数的类型及其在参数列表中的位置。甚至不能保证有任何隐藏的论点。 谢谢,尽管示例 fortran 包装器需要写入例如 ***.com/questions/8207997/… 比我的 3 行示例复杂得多,所以我会坚持我的方法。

以上是关于将 char 数组从 c++ 传递到 fortran的主要内容,如果未能解决你的问题,请参考以下文章

将 char 数组值从 C++ 传递到 C# 应用程序

无法将二维数组传递给 C++ 中的辅助函数

如何将结构中的char *从c#传递到c++

将 C# char 数组传递给 C++ 与字节数组不同

如何将带有 unsigned char* 的结构从 C# 传递到 C++?

将 c++ dll 的 char 指针数组传递给 c# 字符串