簡體   English   中英

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

我是否需要為每個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 <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“Fortran with C的進一步互操作性”添加到該語言的功能,則可互操作的過程可以具有指針參數,這可以簡化編寫這些訪問器。 通過該TS引入的功能將成為下一個標准版本的基本語言的一部分。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM