【问题标题】:Passing string from Fortran to c++ [duplicate]将字符串从 Fortran 传递到 C++ [重复]
【发布时间】:2017-06-27 12:44:54
【问题描述】:

我正在按照以下示例 (http://fortranwiki.org/fortran/show/Fortran+and+Cpp+objects) 将 C++ 代码改编为 Fortran,首先我尝试学习 Fortran 并在 Fortran 和 C++ 之间建立联系。

我修改了我想将字符串从 Fortran 传递到 C++ 的代码

#include <iostream>
#include <string>

using namespace std;
class CWLiDAR {
    string name;

public:
    CWLiDAR(string);
};


CWLiDAR::CWLiDAR(string name)
{
    this->name = name;
}


/*
*/

/* C wrapper interfaces to C++ routines */
#ifdef __cplusplus
extern "C" {
#endif
CWLiDAR* CWLiDAR__new(string name)
{
    return new CWLiDAR(name);
}
void CWLiDAR__delete(CWLiDAR* This)
{
    delete This;
}
#ifdef __cplusplus
}
#endif

Fortran 包装器

module CWLiDAR_module
  use, intrinsic :: ISO_C_Binding!, only: C_int, C_ptr, C_NULL_ptr
  implicit none
  private
  type CWLiDAR_type
    private
    type(C_ptr) :: object = C_NULL_ptr
  end type CWLiDAR_type
  interface
    function C_CWLiDAR__new (name, name_len) result(this) bind(C, name = "CWLiDAR__new")
      import
      type(C_ptr) :: this
      character(name_len, kind=C_CHAR), intent(IN) :: name
      integer :: name_len
    end function C_CWLiDAR__new
    subroutine C_CWLiDAR__delete (this) bind(C, name = "CWLiDAR__delete")
      import
      type(C_ptr), value :: this
    end subroutine C_CWLiDAR__delete
  end interface
  interface new
    module procedure CWLiDAR__new
  end interface new
  interface delete
    module procedure CWLiDAR__delete
  end interface delete
  public :: new, delete, CWLiDAR_type
contains


! Fortran wrapper routines to interface C wrappers
  subroutine CWLiDAR__new(this, name, name_len)
    type(CWLiDAR_type), intent(out) :: this
    character(*) :: name
    integer :: name_len
    this%object = C_CWLiDAR__new(name, name_len)
  end subroutine CWLiDAR__new
  subroutine CWLiDAR__delete(this)
    type(CWLiDAR_type), intent(inout) :: this
    call C_CWLiDAR__delete(this%object)
    this%object = C_NULL_ptr
  end subroutine CWLiDAR__delete
end module CWLiDAR_module

主要

program main
  use cwlidar_module
  type(CWLiDAR_type) :: lidar
  call new(lidar, "Test", 4)

  call delete(lidar)
end program main

我应该如何修改 Fortran 包装器以将字符串从 Fortran 传递到 C++?

【问题讨论】:

  • 您要修改哪个 Fortran 包装器?以何种方式?他们应该是正确的。他们不是正确的吗?以哪种方式?
  • 不知道为什么这被标记为重复 - 这个问题是关于从 Fortran 传递到 C++,而链接的所谓重复是关于从 C++ 传递到 Fortran。

标签: c++ fortran


【解决方案1】:

首先,您不能在 Fortran 和 C 之间的接口处使用 std::stringstd::string 是一些 C++ 实现定义的对象,它没有保证与 Fortran 互操作的内存布局。

您必须改用纯 C 字符串。因此,CVLiDAR__new() 必须接受 char* 类型的参数,并且 fortran 接口必须将此参数声明为

character(kind = c_char) :: name(*)

Fortran 包装例程必须采用一些 Fortran character 虚拟参数,然后继续将其内容复制到适当分配的 character(kind = c_char) 数组中,然后将其传递给 C 函数。

此时,您的 C 接口函数可能会继续将其再次转换为 C++ std::string。这种转换是可选的,因为 C++ 可以像 C 一样处理 C 字符串。不过,添加转换将使其余代码更加纯 C++。

在 Fortran 端组装 C 字符串时,不要忘记用零字符正确终止它!


这些参数转换确实是一个 PITA,如果您需要从 Fortran 调用任意数量的接口函数,则建议编写一个生成 Fortran 包装器的生成器脚本。可以在文件cdi-1.8.1/interfaces/f2003/bindGen.rb 中的气候数据接口库(CDI,源代码可以在https://code.mpimet.mpg.de/projects/cdi/files)中找到此类脚本的示例,其输出在@987654333 下的同一个tar-ball 中发布@。对于您的情况,此脚本可能完全是矫枉过正,但它对 CDI 非常有效,它的输出可能会启发/帮助您了解如何正确进行转换。

【讨论】:

  • 虽然我同意 C 风格的字符串对于这种目的是必要的,但这并不意味着 OP 不能使用std::stringstd::string 在 C++ 中经过优化、标准并且通常比 char * 更友好,而且由于 c_str() 函数允许您从 std::string 转换为 C 风格的字符串,为什么不使用它呢? (注意:它也是 appends a null terminator automatically 到生成的 C 样式字符串)。如果您不想要以 null 结尾的输出,请使用 data() 函数。
  • @VladislavMartin 我明白你的意思,抱歉太不精确了。我从来没有说你不能在你的 C++ 代码中使用std::string,我只是想说你不能在 Fortran-C 接口上使用它。我现在已经更新了我的答案,以便更清楚地说明这一点。
【解决方案2】:

您不能从 Fortran 传递 C++ string(实际上是 std::string)。它根本不兼容 C 或 Fortran,它是一种内部 C++ 类型。您必须使用 C 包装器并传递与 C 兼容的字符数组。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-10-25
    • 1970-01-01
    • 1970-01-01
    • 2012-02-12
    相关资源
    最近更新 更多