2012-04-02 6 views
7

Sono stato trattenuto su questo per circa una settimana, ora, e ho cercato forum dopo forum per una chiara spiegazione di come inviare un char * da C a FORTRAN. Per rendere la cosa più frustrante, l'invio di un char * argomento da FORTRAN a C era straight-forward ...Creazione di un'interfaccia FORTRAN a una funzione C che restituisce un carattere *

L'invio di un char * argomento da FORTRAN a C (questo funziona bene):

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

E da 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) 

Quando si cerca di usare la logica analoga per restituire un char * da C, ottengo problema dopo problema. Un tentativo che ho sentito dovrebbe funzionare è:

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

E l'interfaccia 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 

(non posso letteralmente utilizzare la dimensione (*), quindi sono andato fuori misura a 255.)

Questo deve restituire un puntatore a un array di 255 caratteri in stile C, ma se lo fa, non sono riuscito a convertirlo in una stringa significativa. In pratica, restituisce un insieme casuale di caratteri, dovunque da Wingdings al carattere 'campana' ...

Ho anche tentato di tornare:

  • Un puntatore a carattere (LEN = 255, TIPO = C_CHAR).
  • Letteralmente CHARACTER (LEN = 255, KIND = C_CHAR).
  • Un INTEGER (C_SIZE_T) e ha cercato di ridurlo a un puntatore a un array di stringhe.
  • A CARATTERE.
  • ecc

Se qualcuno mi può dare un esempio di come fare questo, sarei molto grato ...

Con i migliori saluti,

Mike

+0

Quali toolchain state usando? –

+0

Sto usando il compilatore VC++, ma sto compilando l'interfaccia in puro C. FORTAN è Visual Fortran 2011, che credo sia FORTRAN 90. Tuttavia, questo è per un'API pubblicata, quindi deve essere richiamabile da tutti gli artificieri possibili ... –

risposta

12

Le stringhe di lunghezza dinamica sono sempre un po 'complicate con l'interazione C. Una possibile soluzione è usare i puntatori.

Prima un caso semplice, in cui è necessario passare una stringa terminata con carattere null a una funzione C. Se passi davvero la stringa solo dentro, devi assicurarti di finalizzarla con c_null_char, quindi questa direzione è abbastanza semplice. Ecco alcuni esempi di un 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 

E la interface di lua_getfield assomiglia:

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 

E l'interfaccia C-Code è:

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

Ora il piccolo caso più complesso , dove dobbiamo gestire una stringa restituita da C con una lunghezza dinamica. La soluzione più portabile che ho trovato finora utilizza i puntatori. Ecco un esempio con un puntatore, in cui la stringa è dato dal C-routine (anche dalla libreria Aotus menzionato sopra):

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 

dove lua_tolstring ha la seguente interface:

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 

Infine, ecco un tentativo di chiarire come un c_ptr può essere interpretato come una stringa di caratteri Fortran: Si supponga di avere un c_ptr che punta alla stringa:

type(c_ptr) :: a_c_string 

E la lunghezza di esso è dato da una variabile len con il seguente tipo:

integer(kind=c_size_t) :: stringlen 

si vuole ottenere questa stringa in un puntatore a una stringa di caratteri in Fortran:

character,pointer,dimension(:) :: string 

Così si fa la mappatura:

call c_f_pointer(a_c_string, string, [ stringlen ]) 
+0

Grazie, heraldkl!L'esempio sopra è quello di interfacciare una funzione C vuota: questo approccio funzionerebbe quando il tipo di ritorno della funzione C è un char *? Il mio paradigma operativo generale è che le mie funzioni API restituiscano il risultato, piuttosto che passarle tramite subroutine void, e questo funziona bene quando si usano tutti gli altri tipi di caratteri EXCEPT :-( –

+0

Immagino che l'approccio sopra funzionerebbe anche con non-vuoto funzioni Non vedo un motivo, perché questo dovrebbe essere influenzato dal valore di ritorno – haraldkl

+0

Sono certamente felice di usare i puntatori - in effetti, questo è stato il mio primo approccio. Ho paura di avere problemi a seguire i frammenti 'ho fornito sopra - è tutto necessario convertire un puntatore C in un array const char in una stringa FORTRAN? Per dirla in un altro modo, se ho un TYPE (C_PTR) e una lunghezza di stringa, qual è il minimo che ho bisogno di convertirlo in una stringa FORTRAN? Mi spiace di essere ottuso ... –

2

ho sempre lotta con queste caratteristiche di interoperabilità. Penso che l'interfaccia di dichiarare

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

e che, quando si chiama la funzione, si passa una variabile di carattere Fortran che corrisponde con una lunghezza uguale o superiore alla lunghezza della matrice di caratteri C si aspetta di tornare .

Poiché sembra che Intel Fortran, analizzi gli esempi di codice forniti, ne fornisce un esempio completo.

Immagino tu sappia che ciò che hai pubblicato non è sintatticamente corretto per Fortran?

+0

Intendi la virgola prima di ::? Scusa: un errore di copia e incolla! Ti darò il via sopra ... –

+0

Ho appena provato, ma ottengo lo stesso errore di compilazione che avevo quando ho provato qualcosa di simile: "Errore error # 6688: Le specifiche di array per un il nome della funzione senza attributo POINTER deve essere un array di forme esplicito (R505.9). " Questo è il motivo per cui ho provato praticamente lo stesso, ma con 255 invece di * - ma senza successo :-( –

+0

Ho provato a utilizzare "CARATTERE (KIND = C_CHAR), DIMENSION (255) :: GetLastErrorMessage" invece, e leggendo in una matrice di caratteri della stessa specifica, ma questo apparentemente restituisce una matrice di 255 caratteri casuali ... –

1

in FORTRAN l'articolo deve essere dichiarato come "carattere (tipo = c_char, len = 1), dimensioni (255)" Rathe r di len = 255. Questo creerà una serie di caratteri di lunghezza uno, che è ciò di cui hai bisogno sul lato C. Ciò che può creare confusione è che esiste un'eccezione che consente a Fortran di far corrispondere le stringhe agli array monodimensionali.

Vuoi dire che vuoi chiamare una procedura Fortran da C? Vedi questo esempio: Calling a FORTRAN subroutine from C.

EDIT: Sia ifort che gfortran dicono che gli array non sono consentiti come ritorni di funzione in questo contesto. Il che rende le stringhe di ritorno come argomenti di funzione da C a Fortran più difficile dell'uso di una stringa come argomento (esempio nel link sopra) ... devi usare il puntatore e poi il c_f_pointer Fortran intrinseco per convertire dalla stringa C a una stringa Fortran , come spiegato da haraldkl. Ecco un altro esempio di codice:

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 
+0

Hello M.S.B. - Ho provato il carattere (kind = c_char, len = 1), dimensione (255), che sembra restituire qualcosa, ma sembra che il primo elemento sia un carattere NULL - che si traduce in una stringa vuota. Come restituisce la stringa corretta quando chiamato direttamente da C, ho assunto che l'errore si trova nell'interfaccia FORTRAN. Chiamare il FORTRAN da C non sembra essere così difficile - o almeno, posso trasferire le stringhe da FORTRAN a C abbastanza facilmente. –

5

I miei ringraziamenti a heraldkl per avermi dato la soluzione a questo problema molto frustrante.Sto postando quello che ho finalmente implementato, quali ruoli la conversione puntatore nell'interfaccia, il che significa l'applicazione finale può chiamare la funzione C, senza che sia necessario conoscere la conversione puntatore:

La funzione C:

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

Il modulo di interfaccia 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 

E chiamare questa funzione da un programma FORTRAN:

USE MINTERFACES 

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

Grazie ancora a heraldkl per aver fornito questa soluzione - non avrei avuto la minima idea di come farlo senza il suo intervento.

+0

Nessun problema, contento che sia stato d'aiuto;) – haraldkl

4

Questo thread è un po 'vecchio, ma dal momento che ho avuto un problema simile (e probabilmente altri lo faranno), inviamo comunque una risposta.

I codici pubblicati sopra causeranno un errore di segmentazione se, per qualche motivo, la stringa C è nullo. Inoltre, non è necessario restituire una stringa di 255 caratteri (che probabilmente dovrà essere ritagliata prima dell'uso), poiché Fortran 2003/2008 supporta funzioni che restituiscono entità assegnabili. Usando tutte le informazioni postate sopra, ho finito con la seguente funzione, che ottiene una stringa C (puntatore) e restituisce la stringa Fortran corrispondente; Se la stringa C è nullo, viene restituito "NULL", in modo simile a di C "(null)" stampato in casi simili:

function C_to_F_string(c_string_pointer) result(f_string) 
use, intrinsic :: iso_c_binding, only: c_ptr,c_f_pointer,c_char,c_null_char 
type(c_ptr), intent(in) :: c_string_pointer 
character(len=:), allocatable :: f_string 
character(kind=c_char), dimension(:), pointer :: char_array_pointer => null() 
character(len=255) :: aux_string 
integer :: i,length 
call c_f_pointer(c_string_pointer,char_array_pointer,[255]) 
if (.not.associated(char_array_pointer)) then 
    allocate(character(len=4)::f_string); f_string="NULL"; return 
end if 
aux_string=" " 
do i=1,255 
    if (char_array_pointer(i)==c_null_char) then 
    length=i-1; exit 
    end if 
    aux_string(i:i)=char_array_pointer(i) 
end do 
allocate(character(len=length)::f_string) 
f_string=aux_string(1:length) 
end function C_to_F_string 
+0

Ho postato una piccola semplificazione della subroutine [in basso] (http://stackoverflow.com/a/40618786/479532). –

0

Se conoscete la lunghezza della stringa, quindi Pap di answer sopra può essere notevolmente semplificato :

function stringc2f(n, cstr) result(fstr) 
integer, intent(in) :: n 
type(c_ptr), intent(in) :: cstr 
character(:), allocatable :: fstr 
character(n, kind=c_char), pointer :: fptr 
call c_f_pointer(cstr, fptr) 
fstr = fptr 
end function 

la funzione sopra accetta un puntatore C con la stringa e la lunghezza della stringa, e restituisce una copia come stringa Fortran.

+0

Non sono sicuro che questo sia il Fortran moderno, mentre la mia risposta no; infatti entrambi i codici sono Fortran 2003. Ad ogni modo, il tuo codice è più semplice. BUT richiede di passare la lunghezza della stringa ('n'), come argomento. Naturalmente, conoscere in anticipo la lunghezza della stringa rende il codice molto più piccolo, ma rende anche la funzione meno utile. Nella maggior parte dei casi, semplicemente non sai quanto è lunga la stringa C. – Pap

+0

@Pap, hai ragione. Ho chiarito la mia risposta per riflettere questo. Infatti, entrambi sono F2003. Ho usato l'assegnazione automatica di LHS, ma questa è anche F2003. Per la mia applicazione, ho accesso a entrambi i codici C e Fortran, e quindi non è un problema passare la lunghezza della stringa C nell'interfaccia, il che semplifica enormemente le cose. –

Problemi correlati