Fortran 派生类型包含可从 C 访问的指针
Fortran derived types containing pointers to be accessible from 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
我是否需要为 COMPLEXF 创建 Y 的每个成员的子例程,即 SETY_A、QUERYY_A、SETY_B、QUERYY_BSIZE、QUERYY_B ETC。?还是有更好的方法来解决这个问题?
您可以在 Fortran 中编写一些可互操作的访问器过程,这些过程对派生类型进行操作并将必要的变量公开给 C++ 代码。这与一般 C++ 代码与 class.
的私有成员变量交互的方式非常相似
您可以使用 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 <vector>
#include <iostream>
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<int>& b)
{
::SetB(handle, &b[0], b.size());
}
std::vector<int> 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<int>();
std::vector<int> data(data_size);
::QueryBData(handle, &data[0]);
return data;
}
};
int main()
{
SimpleF x;
x.SetA(99);
std::cout << x.QueryA() << std::endl;
std::vector<int> testvector(2,100);
x.SetB(testvector);
std::cout << x.QueryB()[0] << ' ' << x.QueryB()[1] << std::endl;
return 0;
}
如果您的编译器支持通过 TS29113 "Further Interoperability of Fortran with C" 添加到语言中的功能,那么可互操作的过程可以具有指针参数,这可能会简化这些访问器的编写。旨在该 TS 引入的功能将成为下一个标准修订版的基础语言的一部分。
我有一个 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
我是否需要为 COMPLEXF 创建 Y 的每个成员的子例程,即 SETY_A、QUERYY_A、SETY_B、QUERYY_BSIZE、QUERYY_B ETC。?还是有更好的方法来解决这个问题?
您可以在 Fortran 中编写一些可互操作的访问器过程,这些过程对派生类型进行操作并将必要的变量公开给 C++ 代码。这与一般 C++ 代码与 class.
的私有成员变量交互的方式非常相似您可以使用 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 <vector>
#include <iostream>
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<int>& b)
{
::SetB(handle, &b[0], b.size());
}
std::vector<int> 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<int>();
std::vector<int> data(data_size);
::QueryBData(handle, &data[0]);
return data;
}
};
int main()
{
SimpleF x;
x.SetA(99);
std::cout << x.QueryA() << std::endl;
std::vector<int> testvector(2,100);
x.SetB(testvector);
std::cout << x.QueryB()[0] << ' ' << x.QueryB()[1] << std::endl;
return 0;
}
如果您的编译器支持通过 TS29113 "Further Interoperability of Fortran with C" 添加到语言中的功能,那么可互操作的过程可以具有指针参数,这可能会简化这些访问器的编写。旨在该 TS 引入的功能将成为下一个标准修订版的基础语言的一部分。