我有一个复杂的C ++对象,我想在我的Fortran代码中使用它。 通常,从Fortran调用C ++代码没有问题(例如,只需要提供一个带有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代码保持不变。
我无法在互联网上找到任何相关内容:
此外,有人可以解释为什么内存管理不正确吗? 到现在为止,我想到了
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)
答案 0 :(得分:8)
Fortran 2003在Fortran语言中引入了C互操作性。这种语言特性使编写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