Création d’une interface FORTRAN pour une fonction C qui renvoie un caractère *

Cela fait environ une semaine que je suis bloqué dans ce dossier et j’ai cherché, forum après forum, une explication claire de la procédure à suivre pour envoyer un caractère * de C à FORTRAN. Pour rendre la chose plus frustrante, envoyer un argument char * de FORTRAN à C était simple …

Envoi d’un argument char * de FORTRAN à C (cela fonctionne très bien):

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

Et de 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) 

Lorsque j’essaie d’utiliser une logique analogue pour renvoyer un caractère * de C, j’ai un problème après l’autre. Une tentative qui, à mon avis, devrait fonctionner est

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

Et l’interface 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 

(Je ne peux pas utiliser littéralement la DIMENSION (*), je suis donc surdimensionné à 255.)

Cela devrait renvoyer un pointeur sur un tableau de 255 caractères de style C – mais si c’est le cas, je suis incapable de le convertir en une chaîne explicite. En pratique, il renvoie un ensemble de caractères aléatoires, de Wingdings au caractère “bell” …

J’ai aussi tenté de revenir:

  • Un pointeur sur CHARACTER (LEN = 255, KIND = C_CHAR).
  • Littéralement CHARACTER (LEN = 255, KIND = C_CHAR).
  • Un INTEGER (C_SIZE_T), et a essayé d’append cela dans un pointeur sur un tableau de chaînes.
  • UN CARACTÈRE.
  • etc.

Si quelqu’un peut me donner un exemple sur la façon de procéder, je vous en serais très reconnaissant …

Meilleures salutations,

Mike

Les chaînes de longueur dynamic sont toujours un peu délicates avec l’interaction en C. Une solution possible consiste à utiliser des pointeurs.

Commençons par un cas simple, dans lequel vous devez transférer une chaîne terminée par un caractère nul à une fonction-C. Si vous ne passez vraiment que la chaîne, vous devez vous assurer de la finaliser avec le c_null_char, cette direction est donc assez simple. Voici des exemples d’une interface LuaFortran :

 subroutine flu_getfield(L, index, k) type(flu_State) :: L integer :: index character(len=*) :: k integer(kind=c_int) :: c_index character(len=len_sortingm(k)+1) :: c_k c_k = sortingm(k) // c_null_char c_index = index call lua_getfield(L%state, c_index, c_k) end subroutine flu_getfield 

Et l’ interface de lua_getfield ressemble à ceci:

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

Et l’interface C-Code est:

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

Maintenant, le cas un peu plus complexe, où nous devons traiter une chaîne retournée de C avec une longueur dynamic. La solution la plus portable que j’ai trouvée jusqu’à présent utilise des pointeurs. Voici un exemple avec un pointeur, où la chaîne est donnée par le C-Routine (également de la bibliothèque Aotus mentionnée ci-dessus):

 function flu_tolssortingng(L, index, len) result(ssortingng) type(flu_State) :: L integer :: index integer :: len character,pointer,dimension(:) :: ssortingng integer :: ssortingng_shape(1) integer(kind=c_int) :: c_index integer(kind=c_size_t) :: c_len type(c_ptr) :: c_ssortingng c_index = index c_ssortingng = lua_tolssortingng(L%state, c_index, c_len) len = int(c_len,kind=kind(len)) ssortingng_shape(1) = len call c_f_pointer(c_ssortingng, ssortingng, ssortingng_shape) end function flu_tolssortingng 

où lua_tolssortingng a l’ interface suivante:

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

Enfin, voici une tentative visant à clarifier la manière dont un c_ptr peut être interprété comme une chaîne de caractères Fortran: Supposons que vous ayez un c_ptr pointant vers la chaîne:

 type(c_ptr) :: a_c_ssortingng 

Et sa longueur est donnée par une variable len du type suivant:

 integer(kind=c_size_t) :: ssortingnglen 

Vous voulez obtenir cette chaîne dans un pointeur sur une chaîne de caractères en Fortran:

 character,pointer,dimension(:) :: ssortingng 

Donc, vous faites la cartographie:

 call c_f_pointer(a_c_ssortingng, ssortingng, [ ssortingnglen ]) 

Merci à heraldkl de m’avoir apporté la solution à ce problème très frustrant. Je publie ce que j’ai finalement mis en œuvre, quels sont les rôles de la conversion du pointeur dans l’interface, ce qui signifie que l’application finale peut appeler la fonction C sans avoir à connaître la conversion du pointeur:

La fonction C:

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

Le module d’interface 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 

Et pour appeler cette fonction depuis un programme Fortran:

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

Mes remerciements vont à nouveau à heraldkl pour cette solution. Je n’aurais pas compris comment faire sans sa participation.

Ce fil est un peu vieux, mais depuis que j’ai eu un problème similaire (et probablement d’autres le seront), je poste quand même une réponse.

Les codes affichés ci-dessus provoqueront une erreur de segmentation si, pour une raison quelconque, la chaîne C est null. De plus, il n’est pas nécessaire de renvoyer une chaîne de 255 caractères (qui devra probablement être ajustée avant d’être utilisée), car Fortran 2003/2008 prend en charge les fonctions renvoyant des entités pouvant être allouées. En utilisant toutes les informations postées ci-dessus, je me suis retrouvé avec la fonction suivante, qui obtient une chaîne C (pointeur) et renvoie la chaîne Fortran correspondante; Si la chaîne C est null, elle renvoie “NULL”, de la même manière que “C (null)” imprimé dans des cas similaires:

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

J’ai toujours du mal avec ces fonctionnalités d’interopérabilité. Je pense que votre interface devrait déclarer

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

et que, lorsque vous appelez la fonction, vous transmettez une variable de caractère Fortran correspondante dont la longueur est égale ou supérieure à la longueur du tableau de caractères C que vous comptez renvoyer.

Puisque vous semblez avoir Intel Fortran, parcourez les exemples de code fournis, ils en donnent un exemple complet.

Je suppose que vous savez que ce que vous avez posté n’est pas Fortran syntaxiquement correct?

J’ai également eu du mal à appeler une routine C qui retourne une chaîne et les réponses ci-dessus ont été très utiles, mais comme je ne connais presque rien de C et que les réponses sont un peu déroutantes, je voulais simplement consortingbuer à ma solution qui utilise un pointeur C. pas réussi à utiliser aucune des autres propositions ci-dessus. Le programme C que j’appelle ouvre une fenêtre distincte pour rechercher un nom de fichier.

 program test use iso_c_binding implicit none ! AC function that returns a ssortingng need a pointer to the array of single char type (c_ptr) :: C_Ssortingng_ptr ! This is the Fortran equivalent to a ssortingng of single char character (len=1, kind=c_char), dimension(:), pointer :: filchar=>null() ! Interface to a C routine which opens a window to browse for a file to open interface function tinyopen(typ) bind(c, name="tinyopen") use iso_c_binding implicit none integer(c_int), value :: typ type (C_Ptr) :: tinyopen end function tinyopen end interface character (len=256) :: filename integer typ,jj typ=1 C_Ssortingng_ptr = tinyopen(typ) ! convert C pointer to Fortran pointer call c_f_pointer(C_Ssortingng_ptr,filchar,[256]) filename=' ' if(.not.associated(filchar)) then ! if no characters give error message write(*,*)'No file name' else ! convert the array of single characters to a Fortran character jj=1 do while(filchar(jj).ne.c_null_char) filename(jj:jj)=filchar(jj) jj=jj+1 enddo endif write(*,*)'Text is: ',sortingm(filename) end program test 

Espérons que cet exemple facilitera la tâche du prochain avec le même problème.

En Fortran, l’élément doit être déclaré comme “caractère (type = c_char, len = 1), dimension (255)” plutôt que len = 255. Cela créera un tableau de caractères de longueur un, ce dont vous avez besoin du côté C. Ce qui peut être déroutant, c’est qu’il existe une exception qui permet à Fortran de faire correspondre les chaînes à des tableaux unidimensionnels.

Vous voulez dire que vous voulez appeler une procédure Fortran depuis C? Voir cet exemple: Appeler un sous-programme FORTRAN depuis C.

EDIT: ifort et gfortran disent que les tableaux ne sont pas autorisés car les retours de fonction dans ce contexte. Ce qui rend le retour des chaînes en tant qu’arguments de fonction de C à Fortran plus difficile que d’utiliser une chaîne en tant qu’argument (exemple dans le lien ci-dessus) … vous devez utiliser le pointeur puis l’insortingnsèque c_f_pointer Fortran pour convertir une chaîne C en chaîne Fortran , comme expliqué par haraldkl. Voici un autre exemple de code:

 program test_c_func use iso_c_binding implicit none type (C_PTR) :: C_Ssortingng_ptr character (len=1, kind=c_char), dimension (:), pointer :: ErrChars => null () character (len=255) :: ErrSsortingng integer :: i INTERFACE FUNCTION GetLastErrorMessage () bind (C, name="GetLastErrorMessage" ) USE ISO_C_BINDING type (C_PTR) :: GetLastErrorMessage END FUNCTION GetLastErrorMessage END INTERFACE C_Ssortingng_ptr = GetLastErrorMessage () call c_f_pointer ( C_Ssortingng_ptr, ErrChars, [255] ) ErrSsortingng = " " xfer_ssortingng: do i=1, 255 if ( ErrChars (i) == c_null_char) exit xfer_ssortingng ErrSsortingng (i:i) = ErrChars (i) end do xfer_ssortingng write (*, '( "Fortran: <", A, ">" )' ) sortingm (ErrSsortingng) end program test_c_func 

Si vous connaissez la longueur de la chaîne, la réponse de Pap ci-dessus peut être grandement simplifiée:

 function ssortingngc2f(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 fonction ci-dessus accepte un pointeur C avec la chaîne et la longueur de la chaîne et renvoie une copie sous forme de chaîne Fortran.