包含指针的Fortran派生类型可以从C访问 [英] Fortran derived types containing pointers to be accessible from C

查看:181
本文介绍了包含指针的Fortran派生类型可以从C访问的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个包含许多派生类型的指针的Fortran代码。我正在编写一个需要访问这些变量的C ++代码。我不能在没有指针的情况下重写这些派生类型,因为它们在遍布Fortran代码的数百个不同地方使用。



以下是示例代码:

 模块简单
使用iso_c_binding

TYPE,绑定(C):: SIMPLEF
INTEGER: :A
INTEGER,POINTER :: B,C(:)
END TYPE SIMPLEF

结束模块简单

我需要从C中访问SIMPLEF派生类型。我知道我不能按原样使用它,因为如果Fortran指针不是派生类型可以从C中访问吗?是否有任何解决方法?



扩展:作为前一个问题的扩展(解决方法归功于IanH),我有派生类型,派生类型本身。示例如下:

  TYPE COMPLEXF 
INTEGER :: X
TYPE(SIMPLEF):: Y
END TYPE COMPLEXF

我是否需要为COMPLEXF创建Y的每个成员的子例程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
!非互操作类型的示例(无BIND(C))。
TYPE :: SIMPLEF
INTEGER :: A
!请注意,考虑到问题描述,组件B
!似乎有价值语义。如果是这样,那么从Fortran 2003
开始!这应该是一个ALLOCATABLE组件。因为它是
!一个指针组件,我们会默认将它初始化为
!有助于避免其指针关联状态变为
!无意中未定义
INTEGER,POINTER :: B(:) => NULL()
END TYPE SIMPLEF
CONTAINS
函数GetHandle()RESULT(句柄)BIND(C,NAME ='GetHandle')
USE,INTRINSIC :: ISO_C_BINDING,ONLY: C_PTR,C_LOC
TYPE(C_PTR):: handle
TYPE(SIMPLEF),POINTER :: p
!***
!为了举例,我们公开了一个接口
!允许客户端代码创建一个对象。也许在你的
!如果对象已经存在并且其生命周期被管理
!换句话说,在这种情况下:

! handle = C_LOC(existing_object_with_target_attribute)

!你就完成了 - 不需要ReleaseHandle。
ALLOCATE(p)
!也许在这里有一些建设性的东西?
p%A = 666
!使用该对象的C地址作为不透明的句柄。
handle = C_LOC(p)
END FUNCTION GetHandle

!如果你创建对象,你需要能够销毁它们。
SUBROUTINE ReleaseHandle(句柄)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
$ b $ 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(句柄)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)
!将p%B分配到适当的大小。

!假设这里p%B的指针关联状态总是
!定义或分离,从未未定义。这很容易
!具有可分配的组件。
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
$ b $ 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)
!请参阅上面关于假定关联状态的评论
IF(ASSOCIATED(p%B))THEN
data_size = SIZE(p%B,KIND = C_INT)
ELSE
data_size = 0_C_INT
END IF
END SUBROUTINE QueryBSize
$ b SUBROUTINE QueryBData(句柄,数据)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)
!请参阅上面关于假定关联状态的评论
IF(ASSOCIATED(p%B))THEN
data(:SIZE(p%B))= p%B
ELSE
!有人正在愚蠢。
END IF
END SUBROUTINE QueryBData

! ... etc ...
END MODULE simple

// ~~~~~~
#include< vector>
#include< iostream>

externCvoid * GetHandle();
externCvoid ReleaseHandle(void * handle);
externCvoid SetA(void * handle,int a);
externCint QueryA(void * handle);
externCvoid SetB(void * handle,const int * data,int data_size);
externCvoid QueryBSize(void * handle,int * data_size);
externCvoid 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()
{
//获取数据大小,构建一个合适的缓冲区,填充缓冲区。
int data_size;
:: QueryBSize(句柄,& data_size);
if(data_size == 0)返回std :: vector< int>();

std :: vector< int>数据(DATA_SIZE);
:: QueryBData(句柄,& data [0]);
返回数据;
}
};

int main()
{
SimpleF x;
x.SetA(99);
std :: cout<< x.QueryA()<<的std :: ENDL;

std :: vector< int> testvector(2100);
x.SetB(testvector);
std :: cout<< x.QueryB()[0]<< ''<< x.QueryB()[1]<<的std :: ENDL;

返回0;





$ b如果您的编译器支持添加到TS29113语言的功能Further Interoperability of Fortran with C,然后可互操作的过程可以有指针参数,这可能会简化写入访问器。预计该TS中引入的功能将成为下一个标准版本的基础语言的一部分。


I have a Fortran code with many derived types containing pointers. I am writing a C++ code which needs to access these variables. I cannot rewrite these derived types without the pointers as they are used in hundreds of different places all over the Fortran code.

Below is a sample code:

module simple
use  iso_c_binding

TYPE,bind(C) :: SIMPLEF
INTEGER :: A
INTEGER, POINTER :: B, C(:)
END TYPE SIMPLEF

end module simple

I need to access the SIMPLEF derived type from C. I know I cannot use it as it is, since Fortran pointers cannot be in a derived type if that is supposed to be accessible from C. Is there any kind of workaround?

EXTENSION: As an extension to the previous problem (resolved thanks to IanH), I have derived types which have as members derived types themselves. Example below:

TYPE COMPLEXF
  INTEGER :: X
  TYPE (SIMPLEF) :: Y
END TYPE COMPLEXF

Would I need to create for COMPLEXF, subroutines for each member of Y, i.e. SETY_A, QUERYY_A, SETY_B, QUERYY_BSIZE, QUERYY_B etc.? Or is there a better way to approach this?

解决方案

You can write some interoperable accessor procedures in Fortran that operate on the derived type and expose the necessary variables to the C++ code. This is very similar to how general C++ code interacts with private member variables of a class.

You can use the C address of an object of type SIMPLEF as an opaque handle in the C++ code - the type in Fortran does not have to have the BIND(C) attribute to allow objects of that type to be passed to C_LOC (though objects of that type will need to have the TARGET attribute).

For array data, you may need to provide several entry points for the data getters, to allow appropriate coordination of the memory buffer used to transfer the data from Fortran to 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;
}

If your compiler supports the features added to the language with TS29113 "Further Interoperability of Fortran with C", then interoperable procedures can have pointer arguments, which may may simplify writing those accessors. It is intended that the features introduced with that TS will become part of the base language with the next standard revision.

这篇关于包含指针的Fortran派生类型可以从C访问的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆