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

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

问题描述

我有一个 Fortran 代码,其中包含许多包含指针的派生类型.我正在编写需要访问这些变量的 C++ 代码.如果没有指针,我无法重写这些派生类型,因为它们在 Fortran 代码中的数百个不同地方使用.

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.

下面是示例代码:

module simple
use  iso_c_binding

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

end module simple

我需要从 C 中访问 SIMPLEF 派生类型.我知道我不能按原样使用它,因为如果应该从 C 中访问 Fortran 指针,它就不能属于派生类型.有什么解决方法吗?

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?

扩展:作为前一个问题的扩展(感谢 IanH 解决),我有派生类型,这些派生类型本身具有派生类型的成员.示例如下:

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

我是否需要为 COMPLEXF 创建 Y 的每个成员的子例程,即 SETY_A、QUERYY_A、SETY_B、QUERYY_BSIZE、QUERYY_B 等?还是有更好的方法来解决这个问题?

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?

推荐答案

您可以在 Fortran 中编写一些可互操作的访问器过程,它们对派生类型进行操作并将必要的变量公开给 C++ 代码.这与一般 C++ 代码与类的私有成员变量交互的方式非常相似.

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.

您可以使用 SIMPLEF 类型对象的 C 地址作为 C++ 代码中的不透明句柄 - Fortran 中的类型不必具有 BIND(C) 属性以允许将该类型的对象传递给 C_LOC(尽管该类型的对象需要具有 TARGET 属性).

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).

对于数组数据,您可能需要为数据获取器提供多个入口点,以便适当协调用于将数据从 Fortran 传输到 C 的内存缓冲区.

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;
}

如果您的编译器支持通过 TS29113Fortran 与 C 的进一步互操作性"添加到语言中的功能,那么可互操作的过程可以具有指针参数,这可能会简化这些访问器的编写.该 TS 引入的功能旨在成为下一个标准版本的基础语言的一部分.

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天全站免登陆