11

私はこれについて約 1 週間待たされており、C から FORTRAN に char* を送信する方法の明確な説明を求めて、次から次へとフォーラムを検索しました。さらに苛立たしいことに、FORTRAN から C に char* 引数を送信するのは簡単でした...

FORTRAN から C に char* 引数を送信する (これは問題なく動作します):

// The C header declaration (using __cdecl in a def file):
extern "C" double GetLoggingValue(char* name);

そしてFORTRANから:

! The FORTRAN interface:
INTERFACE
    REAL(8) FUNCTION GetLoggingValue [C, ALIAS: '_GetLoggingValue'] (name)
        USE ISO_C_BINDING       
        CHARACTER(LEN=1, KIND=C_CHAR), DIMENSION(*),    INTENT(IN) :: name                  
    END FUNCTION GetLoggingValue
END INTERFACE

! Calling the function:
GetLoggingValue(user_name)

類似のロジックを使用して C から char* を返そうとすると、次々と問題が発生します。私がうまくいくはずだと感じた1つの試みは次のとおりです。

// The C declaration header (using __cdecl in a def file):
extern "C" const char* GetLastErrorMessage();

そしてFORTRANインターフェース:

INTERFACE
    FUNCTION GetLastErrorMessage [C, ALIAS: '_GetLastErrorMessage'] ()
        USE ISO_C_BINDING   
        CHARACTER(LEN=1, KIND=C_CHAR), DIMENSION(255), :: GetLastErrorMessage
    END FUNCTION GetLastErrorMessage
END INTERFACE

(DIMENSION(*) は文字通り使えないので、255 にオーバーサイズしました。)

これは 255 個の C スタイル文字の配列へのポインターを返すはずですが、もしそうなら、これを意味のある文字列に変換できませんでした。実際には、Wingdings から「ベル」の文字まで、ランダムな文字セットを返します...

私も返そうとしました:

  • CHARACTER(LEN=255, KIND=C_CHAR) へのポインター。
  • 文字通り CHARACTER(LEN=255, KIND=C_CHAR)。
  • INTEGER(C_SIZE_T) であり、それを文字列配列へのポインターに精巧にしようとしました。
  • キャラクター。

誰かがこれを行う方法の例を教えてくれれば、とても感謝しています...

よろしくお願いします、

マイク

4

7 に答える 7

14

動的な長さの文字列は、C の相互作用では常に少し注意が必要です。考えられる解決策は、ポインターを使用することです。

最初の単純なケースでは、ヌル文字で終了する文字列を C 関数に渡す必要があります。実際に文字列のみを渡す場合は、c_null_char で確実にファイナライズする必要があるため、この方向は非常に簡単です。以下はLuaFortran Interfaceの例です:

subroutine flu_getfield(L, index, k)
  type(flu_State)  :: L
  integer          :: index
  character(len=*) :: k

  integer(kind=c_int) :: c_index
  character(len=len_trim(k)+1) :: c_k

  c_k = trim(k) // c_null_char
  c_index = index
  call lua_getfield(L%state, c_index, c_k)
end subroutine flu_getfield

lua_getfieldのインターフェースは次のようになります。

subroutine lua_getfield(L, index, k) bind(c, name="lua_getfield")
  use, intrinsic :: iso_c_binding
  type(c_ptr), value :: L
  integer(kind=c_int), value :: index
  character(kind=c_char), dimension(*) :: k
end subroutine lua_getfield

C コード インターフェイスは次のとおりです。

void lua_getfield (lua_State *L, int idx, const char *k)

ここで、動的な長さで C から返された文字列を処理する必要がある、もう少し複雑なケースです。これまでに見つけた最も移植性の高いソリューションは、ポインターを使用することです。これはポインターを使用した例で、文字列は C-Routine (これも上記の Aotus ライブラリーから) によって指定されます。

function flu_tolstring(L, index, len) result(string)
  type(flu_State) :: L
  integer :: index
  integer :: len
  character,pointer,dimension(:) :: string

  integer :: string_shape(1)
  integer(kind=c_int) :: c_index
  integer(kind=c_size_t) :: c_len
  type(c_ptr) :: c_string

  c_index = index
  c_string = lua_tolstring(L%state, c_index, c_len)
  len = int(c_len,kind=kind(len))
  string_shape(1) = len
  call c_f_pointer(c_string, string, string_shape)
end function flu_tolstring

lua_tolstring には次のインターフェースがあります。

function lua_tolstring(L, index, len) bind(c, name="lua_tolstring")
  use, intrinsic :: iso_c_binding
  type(c_ptr), value :: L
  integer(kind=c_int), value :: index
  integer(kind=c_size_t) :: len
  type(c_ptr) :: lua_tolstring
end function lua_tolstring

最後に、c_ptr を Fortran 文字列として解釈する方法を明確にする試みを次に示します。文字列を指す c_ptr を取得したとします。

type(c_ptr) :: a_c_string

そして、その長さは次の型の len 変数によって与えられます:

integer(kind=c_size_t) :: stringlen

Fortran の文字列へのポインタでこの文字列を取得したいとします。

character,pointer,dimension(:) :: string

したがって、マッピングを行います。

call c_f_pointer(a_c_string, string, [ stringlen ])
于 2012-04-02T09:23:33.297 に答える
5

この非常に苛立たしい問題の解決策を教えてくれたheraldklに感謝します。私が最終的に実装したものを投稿しています。これは、インターフェイスへのポインター変換の役割を果たします。つまり、最終的なアプリケーションは、ポインター変換について知らなくても C 関数を呼び出すことができます。

C 関数:

// The C declaration header (using __cdecl in a def file):
extern "C" const char* GetLastErrorMessage();

FORTRAN インターフェイス モジュール:

MODULE mINTERFACES

USE ISO_C_BINDING

INTERFACE
    FUNCTION GetLastErrorMessagePtr [C, ALIAS: '_GetLastErrorMessage'] ()
        USE ISO_C_BINDING   
    TYPE(C_PTR) :: GetLastErrorMessagePtr                   
    END FUNCTION GetLastErrorMessagePtr
END INTERFACE

CONTAINS    ! this must be after all INTERFACE definitions

FUNCTION GetLastErrorMessage()
    USE ISO_C_BINDING   
    CHARACTER*255 :: GetLastErrorMessage
    CHARACTER, POINTER, DIMENSION(:) :: last_message_array
    CHARACTER*255 last_message
    INTEGER message_length

    CALL C_F_POINTER(GetLastErrorMessagePtr(), last_message_array, [ 255 ])

    DO 10 i=1, 255
        last_message(i:i+1) = last_message_array(i)
10  CONTINUE

    message_length = LEN_TRIM(last_message(1:INDEX(last_message, CHAR(0))))

    GetLastErrorMessage = last_message(1:message_length-1)

END FUNCTION GetLastErrorMessage

FORTRAN プログラムからこの関数を呼び出すには:

USE MINTERFACES

PRINT *, "--> GetLastErrorMessage:      '", TRIM(GetLastErrorMessage()), "'"

このソリューションを提供してくれたheraldklに改めて感謝します。

于 2012-04-03T08:20:07.640 に答える
2

私はいつもこれらの相互運用機能に苦労しています。インターフェイスは宣言する必要があると思います

CHARACTER(KIND=C_CHAR),DIMENSION(*) :: getlasterrormessage

また、関数を呼び出すときに、返されると予想される C 文字の配列の長さ以上の長さを持つ対応する Fortran 文字変数を渡します。

Intel Fortran を使用しているようですので、提供されているコード サンプルを調べてください。これについての完全な例が示されています。

あなたが投稿したものが構文的に正しい Fortran ではないことを知っていると思いますか?

于 2012-04-02T08:25:14.770 に答える
1

Fortran では、len=255 ではなく、「文字 (kind=c_char,len=1), dimension (255)」としてアイテムを宣言する必要があります。これにより、C 側で必要な長さ 1 の文字の配列が作成されます。混乱を招く可能性があるのは、Fortran が文字列を 1 次元配列と照合できるようにする例外があることです。

C から Fortran プロシージャを呼び出したいということですか? 次の例を参照してください: C から FORTRAN サブルーチンを呼び出す

編集: ifort と gfortran の両方が、このコンテキストでは関数の戻り値として配列が許可されていないと言っています。これにより、文字列を引数として使用するよりも、C から Fortran への関数引数として文字列を返すことが難しくなります (上記のリンクの例)。C 文字列から Fortran 文字列に変換するには、ポインターを使用し、次に c_f_pointer Fortran 組み込み関数を使用する必要があります。 、haraldklによって説明されているように。別のコード例を次に示します。

program test_c_func

use iso_c_binding
implicit none

type (C_PTR) :: C_String_ptr
character (len=1, kind=c_char), dimension (:), pointer :: ErrChars => null ()
character (len=255) :: ErrString
integer :: i

INTERFACE
    FUNCTION GetLastErrorMessage ()  bind (C, name="GetLastErrorMessage" )
        USE ISO_C_BINDING
        type (C_PTR) :: GetLastErrorMessage
    END FUNCTION GetLastErrorMessage
END INTERFACE

C_String_ptr = GetLastErrorMessage ()
call c_f_pointer ( C_String_ptr, ErrChars, [255] )
ErrString = " "
xfer_string: do i=1, 255
   if ( ErrChars (i) == c_null_char) exit xfer_string
   ErrString (i:i) = ErrChars (i)
end do xfer_string

write (*, '( "Fortran: <", A, ">" )' )  trim (ErrString)

end program test_c_func
于 2012-04-02T12:38:48.053 に答える