我有一个复杂的 C++ 对象,我想在我的 Fortran 代码中使用它。
一般来说,从 Fortran 调用 C++ 代码是没有问题的(例如只需要提供一个合适的接口(interface)与 C 链接)。

但是我的问题是我希望我对 C++ 的 Fortran 调用对我称之为持久对象的东西进行操作:一个由第一个 init 函数创建的 C++ 对象,并由其他 C++ 函数操作。

更具体地说,假设我有以下 C++ 代码

struct A {
    public:
      void do() { // do something on complicated stuff
    private:
      ... // complicated stuff
};

extern "C" {
    void* init_A() {
         A* a = new A();
         return reinterpret_cast<void*>(a);
    }

    void doSth(void* ptr_to_A) {
         A* a = reinterpret_cast<A*>(ptr_to_A);
         a.do();
    }

    void teardown_A(void* ptr_to_A) {
         A* a = reinterpret_cast<A*>(ptr_to_A);
         delete a;
    }
}

以及以下 fortran 代码(假设它是 main() ):
USE, INTRINSIC :: ISO_C_BINDING, ONLY: C_PTR
INTERFACE
    TYPE(C_PTR) FUNCTION init_A() BIND(C, NAME='init_A')
        USE, INTRINSIC :: ISO_C_BINDING, ONLY: C_PTR
        IMPLICIT NONE
    END FUNCTION init_A

    SUBROUTINE doSth(ptr_to_A) BIND(C, NAME='doSth')
        USE, INTRINSIC :: ISO_C_BINDING, ONLY: C_PTR
        IMPLICIT NONE
        TYPE(C_PTR), INTENT(IN), VALUE :: ptr_to_A
    END SUBROUTINE doSth

    SUBROUTINE teardown_A(ptr_to_A) BIND(C, NAME='teardown_A')
        USE, INTRINSIC :: ISO_C_BINDING, ONLY: C_PTR
        IMPLICIT NONE
        TYPE(C_PTR), INTENT(IN), VALUE  :: ptr_to_A
    END SUBROUTINE teardown_A
END INTERFACE

现在在我的真实代码中,这可以编译、链接,有时可以工作,但有时不行:
似乎在 init_A() 中分配的内存不能保证 Fortran 代码保持不变)

我无法在互联网上找到任何相关信息:
  • 不知道有没有什么标准机制可以保证init_A_()分配的内存保持不变,仍然由fortran代码分配?
  • 你知道其他适合我的问题的机制吗?

  • 另外,有人可以解释我为什么没有正确管理内存吗?
    直到现在,我还以为
  • Fortran 会向操作系统询问内存,C++ 也是如此,
  • 操作系统提供给 Fortan 和 C++ 的内存段不相关,并且保证不重叠,
  • 如果要求新内存,操作系统不会让 Fortran 使用 C++ 内存,直到 C++ 释放它
  • 通过调用teardown_A() 或当程序(即Fortran main)终止时释放C++ 内存

  • 编辑: 我用 IanH 的回答更新了我的代码,但这仍然不起作用(段错误,部分内存在从 Fortran 调用 doSth() 时被释放

    我发布的原始代码如下(供引用)
    struct A {
        public:
          void do() { // do something on complicated stuff
        private:
          ... // complicated stuff
    };
    
    extern "C" {
        void init_A_(long* ptr_to_A) { // ptr_to_A is an output parameter
             A* a = new A();
             *ptr_to_A = reinterpret_cast<long>(a);
        }
    
        void doSth_(long* ptr_to_A) {
             A* a = reinterpret_cast<A*>(*ptr_to_A);
             a.do();
        }
    
        void teardown_A_(long* ptr_to_A) {
             A* a = reinterpret_cast<A*>(*ptr_to_A);
             delete a;
        }
    }
    

    和 Fortran 代码:
    integer :: ptr_to_A
    
    call init_A(ptr_to_A)
    
    do i=1,10000
        call doSth(ptr_to_A)
    enddo
    
    call teardown_A(ptr_to_A)
    

    最佳答案

    Fortran 2003 将 C 互操作性引入 Fortran 语言。这种语言特性使得编写可以以可移植和健壮的方式协同工作的 Fortran 和 C(以及 C++)源代码变得更加容易。除非由于其他原因阻止您使用此级别的语言,否则您应该非常使用此功能。

    您有指针间接问题 - 指向 C++ 对象的指针是存储在 long 中还是指向 long 的指针(doSth_ 和 teardown_A_ 中转换的操作数应该在它们之前有一个 *)。这取决于您使用的 C++ 和 Fortran 编译器,但 C long、C 指针和 Fortran 默认类型整数之间的大小可能不匹配。

    修改后的示例显示了下面使用 Fortran 2003 的 C 互操作性功能的方法。

    // C++
    struct A {
        public:
          void do_something()
          {
             // ...
          }
        private:
          // ...
    };
    
    // Note no need for trailing underscore.
    extern "C" {
        // Note pointer to pointer to void.
        void init_A(void** ptr_ptr_to_A) {
             A* a = new A;
             *ptr_ptr_to_A = reinterpret_cast<void*>(a);
        }
    
        void doSth(void* ptr_to_A) {
             A* a = reinterpret_cast<A*>(ptr_to_A);
             a->do_something();
        }
    
        void teardown_A(void* ptr_to_A) {
             A* a = reinterpret_cast<A*>(ptr_to_A);
             delete a;
        }
    }
    
    
    ! Fortran 2003
      USE, INTRINSIC :: ISO_C_BINDING, ONLY: C_PTR
      IMPLICIT NONE
      INTERFACE
        SUBROUTINE init_A(ptr_to_A) BIND(C, NAME='init_A')
          USE, INTRINSIC :: ISO_C_BINDING, ONLY: C_PTR
          IMPLICIT NONE
          ! This argument is a pointer passed by reference.
          TYPE(C_PTR), INTENT(OUT) :: ptr_to_A
        END SUBROUTINE init_A
        SUBROUTINE doSth(ptr_to_A) BIND(C, NAME='doSth')
          USE, INTRINSIC :: ISO_C_BINDING, ONLY: C_PTR
          IMPLICIT NONE
          ! This argument is a pointer passed by value.
          TYPE(C_PTR), INTENT(IN), VALUE :: ptr_to_A
        END SUBROUTINE doSth
        SUBROUTINE teardown_A(ptr_to_A) BIND(C, NAME='teardown_A')
          USE, INTRINSIC :: ISO_C_BINDING, ONLY: C_PTR
          IMPLICIT NONE
          ! This argument is a pointer passed by value.
          TYPE(C_PTR), INTENT(IN), VALUE :: ptr_to_A
        END SUBROUTINE teardown_A
      END INTERFACE
      TYPE(C_PTR) :: ptr_to_A
      INTEGER :: i
      !****
      CALL init_A(ptr_to_A)
      DO i = 1, 100
        CALL doSth(ptr_to_A)
      END DO
      CALL teardown_A(ptr_to_A)
    END
    

    关于c++ - 使用 Fortran 中的内存数据调用 C 代码,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/24643090/

    10-12 16:10
    查看更多