Fortran派生类型,包含可从C访问的指针
我有一个Fortran代码,其中包含许多包含指针的派生类型。 我正在编写一个需要访问这些变量的C ++代码。 我不能在没有指针的情况下重写这些派生类型,因为它们在Fortran代码的数百个不同的地方使用。
以下是示例代码:
module simple use iso_c_binding TYPE,bind(C) :: SIMPLEF INTEGER :: A INTEGER, POINTER :: B, C(:) END TYPE SIMPLEF end module simple
我需要从C访问SIMPLEF派生类型。我知道我不能使用它,因为Fortran指针不能在派生类型中,如果它应该可以从C访问。有任何解决方法吗?
EXTENSION:作为前一个问题的扩展(由于IanH解决了),我已经派生出了具有成员派生类型的类型。 示例如下:
TYPE COMPLEXF INTEGER :: X TYPE (SIMPLEF) :: Y END TYPE COMPLEXF
我是否需要为每个Y成员创建COMPLEXF子程序,即SETY_A,QUERYY_A,SETY_B,QUERYY_BSIZE,QUERYY_B等? 或者有更好的方法来解决这个问题吗?
您可以在Fortran中编写一些可以在派生类型上运行的可互操作的访问器过程,并将必要的变量公开给C ++代码。 这与一般C ++代码与类的私有成员变量交互的方式非常相似。
您可以使用SIMPLEF类型的对象的C地址作为C ++代码中的不透明句柄 – Fortran中的类型不必具有BIND(C)属性以允许将该类型的对象传递给C_LOC(尽管对象该类型将需要具有TARGET属性)。
对于数组数据,您可能需要为数据获取器提供多个入口点,以允许适当协调用于将数据从Fortran传输到C的内存缓冲区。
MODULE simple IMPLICIT NONE ! An example of an non-interoperable type (no BIND(C)). TYPE :: SIMPLEF INTEGER :: A ! Note that given the problem description, the component B ! appears to have value semantics. If so, as of Fortran 2003 ! this should be an ALLOCATABLE component. Because it is ! a pointer component, we will default initialize it to ! help avoid its pointer association status becoming ! inadvertently undefined INTEGER, POINTER :: B(:) => NULL() END TYPE SIMPLEF CONTAINS FUNCTION GetHandle() RESULT(handle) BIND(C, NAME='GetHandle') USE, INTRINSIC :: ISO_C_BINDING, ONLY: C_PTR, C_LOC TYPE(C_PTR) :: handle TYPE(SIMPLEF), POINTER :: p !*** ! For the sake of example we are exposing an interface that ! allows client code to create an object. Perhaps in your ! case the object already exists and its lifetime is managed ! in some other way, in which case: ! ! handle = C_LOC(existing_object_with_target_attribute) ! ! and you are done - no need for ReleaseHandle. ALLOCATE(p) ! Perhaps some constructory sort of stuff here? p%A = 666 ! Use the C address of the object as an opaque handle. handle = C_LOC(p) END FUNCTION GetHandle ! If you create objects, you need to be able to destroy them. SUBROUTINE ReleaseHandle(handle) BIND(C, NAME='ReleaseHandle') USE, INTRINSIC :: ISO_C_BINDING, ONLY: C_PTR, C_F_POINTER TYPE(C_PTR), INTENT(IN), VALUE :: handle TYPE(SIMPLEF), POINTER :: p !*** CALL C_F_POINTER(handle, p) DEALLOCATE(p) END SUBROUTINE ReleaseHandle SUBROUTINE SetA(handle, a) BIND(C, NAME='SetA') USE, INTRINSIC :: ISO_C_BINDING, ONLY: & C_PTR, C_F_POINTER, C_INT TYPE(C_PTR), INTENT(IN), VALUE :: handle INTEGER(C_INT), INTENT(IN), VALUE :: a TYPE(SIMPLEF), POINTER :: p !*** CALL C_F_POINTER(handle, p) p%A = a END SUBROUTINE SetA FUNCTION QueryA(handle) RESULT(a) BIND(C, NAME='QueryA') USE, INTRINSIC :: ISO_C_BINDING, ONLY: & C_PTR, C_F_POINTER, C_INT TYPE(C_PTR), INTENT(IN), VALUE :: handle INTEGER(C_INT) :: a TYPE(SIMPLEF), POINTER :: p !*** CALL C_F_POINTER(handle, p) a = p%A END FUNCTION QueryA SUBROUTINE SetB(handle, data, data_size) BIND(C, NAME='SetB') USE, INTRINSIC :: ISO_C_BINDING, ONLY: & C_PTR, C_F_POINTER, C_INT TYPE(C_PTR), INTENT(IN), VALUE :: handle INTEGER(C_INT), INTENT(IN), VALUE :: data_size INTEGER(C_INT), INTENT(IN) :: data(data_size) TYPE(SIMPLEF), POINTER :: p !*** CALL C_F_POINTER(handle, p) ! Allocate p%B to appropriate size. ! ! Assuming here the pointer association status of p%B is always ! defined or dissociated, never undefined. This is much easier ! with allocatable components. IF (ASSOCIATED(p%B)) THEN IF (SIZE(p%B) /= data_size) THEN DEALLOCATE(p%B) ALLOCATE(p%B(data_size)) END IF ELSE ALLOCATE(p%B(data_size)) END IF p%B = data END SUBROUTINE SetB SUBROUTINE QueryBSize(handle, data_size) BIND(C, NAME='QueryBSize') USE, INTRINSIC :: ISO_C_BINDING, ONLY: & C_PTR, C_F_POINTER, C_INT TYPE(C_PTR), INTENT(IN), VALUE :: handle INTEGER(C_INT), INTENT(OUT) :: data_size TYPE(SIMPLEF), POINTER :: p !*** CALL C_F_POINTER(handle, p) ! See comments about assumed association status above. IF (ASSOCIATED(p%B)) THEN data_size = SIZE(p%B, KIND=C_INT) ELSE data_size = 0_C_INT END IF END SUBROUTINE QueryBSize SUBROUTINE QueryBData(handle, data) BIND(C, NAME='QueryBData') USE, INTRINSIC :: ISO_C_BINDING, ONLY: & C_PTR, C_F_POINTER, C_INT TYPE(C_PTR), INTENT(IN), VALUE :: handle INTEGER(C_INT), INTENT(OUT) :: data(*) TYPE(SIMPLEF), POINTER :: p !*** CALL C_F_POINTER(handle, p) ! See comments about assumed association status above. IF (ASSOCIATED(p%B)) THEN data(:SIZE(p%B)) = p%B ELSE ! Someone is being silly. END IF END SUBROUTINE QueryBData ! ...etc... END MODULE simple //~~~~~~ #include #include extern "C" void* GetHandle(); extern "C" void ReleaseHandle(void* handle); extern "C" void SetA(void* handle, int a); extern "C" int QueryA(void* handle); extern "C" void SetB(void* handle, const int* data, int data_size); extern "C" void QueryBSize(void* handle, int* data_size); extern "C" void QueryBData(void *handle, int *data); class SimpleF { private: void *handle; public: SimpleF() { handle = GetHandle(); } ~SimpleF() { ReleaseHandle(handle); } void SetA(int a) { ::SetA(handle, a); } int QueryA() { return ::QueryA(handle); } void SetB(const std::vector& b) { ::SetB(handle, &b[0], b.size()); } std::vector QueryB() { // Get the data size, construct a suitable buffer, populate the buffer. int data_size; ::QueryBSize(handle, &data_size); if (data_size == 0) return std::vector (); std::vector data(data_size); ::QueryBData(handle, &data[0]); return data; } }; int main() { SimpleF x; x.SetA(99); std::cout << x.QueryA() << std::endl; std::vector testvector(2,100); x.SetB(testvector); std::cout << x.QueryB()[0] << ' ' << x.QueryB()[1] << std::endl; return 0; }
如果您的编译器支持使用TS29113“Fortran with C的进一步互操作性”添加到该语言的function,则可互操作的过程可以具有指针参数,这可以简化编写这些访问器。 通过该TS引入的function将成为下一个标准版本的基本语言的一部分。