2016-01-02 8 views
5

Ich versuche, ein Array von C/C++ in ein Fortran 2003-Modul zu übergeben und die berechneten Werte zurück in C/C++ zu erhalten. Ich konnte einzelne Werte (Skalare) übergeben und zurückgeben, aber es ist schwierig, ein Array hin und her zu bekommen. Ich habe viele Threads zu skalaren Werten gefunden, und ich bin erfolgreich darin gewesen, diese zu arbeiten.Übergeben Sie Arrays von C/C++ zu Fortran und geben Sie ein berechnetes Array zurück

Ich habe meine Array-basierten Funktionen nach meiner Arbeit skalare Funktionen modelliert.

Ich benutze gcc/gfortran.

Hier ist das Fortran-Modul (ConvertUnitsLib.f03).

module ConvertUnitsLib 

use :: iso_c_binding ! for C/C++ interop 
real(c_double), bind(c) :: degF, degC 

public DegCtoF 

contains 

! 
! Convert temperature degrees Celsius Fahrenheit 
! 
real(kind = c_double) function DegCtoF(degC) result(degF) & 
    & bind(c, name = "DegCtoF") 

    real(c_double), intent(in), dimension(:) :: degC 
    real(c_double), dimension(size(degC)) :: degF 

    do i = 1, size(degC) 
     degF(i) = (degC(i) * 1.8) + 32 
    end do 

end function DegCtoF 


! End of module 
end module ConvertUnitsLib 

Und die C/C++, (CFort.cpp)

#include <stdio.h> 

#ifdef __cplusplus 
extern"C" { 
#endif 
    double DegCtoF(double *[]); 
#ifdef __cplusplus 
} 
#endif 


/**********************************************************************/ 


int main(int argc, char *argv[]) 
{ 
    printf("C/C++ and Fortran together!\n"); 

    double DegreesC[2] = {32, 64}; 
    double DegreesF[2]; 

    DegreesF = DegCtoF(&DegreesC); 
    printf("%3.1f [C] = %3.1f [F]\n", DegreesC, DegreesF); 

    return 0; 
} 

Und last but not least, das Makefile

# C++ directives 
CC=g++ 
CFLAGS=-std=c++11 

# Fortran directives 
FC=gfortran 
FFLAGS=-std=f2003 

all: clean 
    $(FC) $(FFLAGS) -c -fcheck=all ConvertUnitsLib.f03 
    $(CC) $(CFLAGS) -c CFort.cpp 
    $(FC) $(FFLAGS) ConvertUnitsLib.o CFort.o -o convert 

clean: 
    rm -f *.o 
    rm -f *.mod 
+0

Nun, ich bin nicht sicher über Fortran, aber in C und C++, übergibt ein Array (das tatsächlich die Adresse des ersten Elements übergibt) alle Größeninformationen, so dass Ihre Fortran Routine nichts über die tatsächliche Anzahl der Elemente weiß Das Array enthält. Ich würde zwei Parameter erwarten, das Array (oder Zeiger auf den Puffer) und die Anzahl der Elemente. – PaulMcKenzie

+0

Von deinen Tags gehe ich davon aus, dass du gfortran/gcc verwendest, aber kannst du das bestätigen? Das Übernehmen eines angenommenen Formarguments wie "degC" gehört nicht zur Interoperabilität von Fortran 2003 C und erfordert viel mehr Aufwand. Der Aspekt, von dem derzeit erwartet wird, dass er Bestandteil von F2015 (der ISO TS) ist, wird von gcc nicht unterstützt. Wenn keine solche Unterstützung vorhanden ist, verwenden Sie den Kommentar von @PaulMcKenzie und verwenden Sie ein explizites Shape-Array, dessen Größe ebenfalls übergeben wurde. – francescalus

+0

@francescalus, Ja, ich benutze gfortran/gcc. Wenn Sie mehr Aufwand bedeuten, muss ich auch die Größe des Arrays übergeben? Ich weiß, wenn C die Fortran-Funktion aufruft, übergibt es die Skalarwerte mit einem Zeiger. Muss ich nur die Größe auch überschreiten, und dann kann es (Fortran) den Rest ausmachen? –

Antwort

5

Vor francescalus bestätigt es, ich sagen wollte Soweit ich weiß, war das etwas alt, die Interoperabilität erlaubt nicht, was Sie mit Arrays machen wollen. Darüber hinaus sind einige gute Gewohnheiten immer entscheidend beim Codieren. Zum Beispiel mit implicit none in Fortran, um die Deklaration aller Variablen zu erzwingen, bevor sie verwendet werden. Die Verwendung der benannten Konstante, wenn die Sprache dies zulässt, z. B. 2, die Sie als Array-Größe in Fortran verwenden.

Unten ist eine modifizierte Version Ihres Codes, die etwas tun sollte, was Sie erreichen möchten.

// Fortran

module ConvertUnitsLib 

use :: iso_c_binding ! for C/C++ interop 
!real(c_double), bind(c) :: degF, degC 
implicit none 

public DegCtoF 

contains 

! 
! Convert temperature degrees Celsius Fahrenheit 
! 
subroutine DegCtoF(degC, degF, n)& 
    bind(c, name = "DegCtoF") 

    integer, intent(in) :: n 
    real(c_double), intent(in), dimension(n) :: degC 
    real(c_double), intent(out), dimension(n) :: degF 
    integer :: i 

    do i = 1, n 
     degF(i) = (degC(i) * 1.8) + 32 
    end do 

end subroutine DegCtoF 

// C++

#include <stdio.h> 

#ifdef __cplusplus 
extern"C" { 
    #endif 
    double DegCtoF(double [], double [], const int *); 
    #ifdef __cplusplus 
} 
#endif 


/**********************************************************************/ 


int main(int argc, char *argv[]) 
{ 
    const int N = 2; 
    printf("C/C++ and Fortran together!\n"); 

    double DegreesC[N] = {32, 64}; 
    double DegreesF[N]; 

    DegCtoF(DegreesC, DegreesF, &N); 
    for(int i = 0; i<N; i++){ 
     printf("%d : %3.1f [C] = %3.1f [F]\n", i, DegreesC[i], DegreesF[i]); 
    } 

    return 0; 
} 
+0

Das ist, was ich suche. Habe den Code getestet und es funktioniert super. Was ich nicht sicher bin, ist die Sicherheit bei der Verwendung eines Unterprogramms. Von IDL kommend erlauben Unterroutinen dem Codeblock, jede interne Variable innerhalb des Blocks zu modifizieren. Im Wesentlichen wird eine Subroutine inline ausgeführt. Ist das in Fortran gleich? Was ich neugierig bin, ist, wenn ich eine Variable namens NUM in der Subroutine gesagt habe, wird die Subroutine in der Lage sein, diese Variable im globalen Sinne zu modifizieren? –

+0

Es zu einem Unterprogramm zu machen ist eine Sache, die ich vergessen zu erwähnen. So mache ich es vor ein paar Jahren. Danke an @francescalus, um es in seiner Antwort zu erwähnen. Ich weiß nichts über IDL. Für die Inline-Ausführung verstehe ich deine Frage nicht. – innoSPG

5

Nach den Regeln der aktuellen Fortran (Fortran 2008, aber dies ist das gleiche für, wenn C Interoperabilität in Fortran 2003 eingeführt wurde), Eine Fortran-Prozedur ist nicht mit C interoperabel, wenn sie ein angenommenes Shape-Dummy-Argument hat (andere Einschränkungen gelten ebenfalls). In Ihrem Code degC erklärte das Dummy-Argument in der Funktion DegCtoF, als

real(c_double), intent(in), dimension(:) :: degC 

so etwas ist.

So, unter F2003 können Sie nicht eine solche interoperable Funktion haben. Wo wird es knifflig?

In dem vorgeschlagenen Entwurf für F2015 (basierend auf der ISO TS29113 Further Interoperability of Fortran with C) so etwas ist kompatibel. Und diese Syntax wird (glaube ich) von den letzten Versionen von gcc unterstützt, weshalb der Code von gfortran nicht zurückgewiesen wird.

(TS) Standardisierte Interoperation mit einem solchen Verfahren mit einem angenommenen Form Argument erfordert jedoch die C-Deskriptors in ISO_Fortran_binding.h auf der C-Seite beschrieben, die unter Verwendung von nicht in gcc implementiert ist. Um eine solche Interaktion durchzuführen, muss der gcc-Array-Deskriptor direkt verstanden werden.

Aber du hast Glück.In Ihrem Fall müssen Sie nicht unbedingt ein angenommenes Shape-Dummy-Argument verwenden: Sie können ein explizites Shape-Dummy-Argument verwenden, und diese Interoperation ist Teil von F2003. Alles, was Sie tun müssen, ist die Größe des Arrays zu übergeben.

In beiden Fällen muss eine interoperable Funktion ein skalares Ergebnis zurückgeben. Daher sollten Sie auch in ein Unterprogramm wechseln, wie in der angegeben.

Schließlich werde ich die Nutzung von

real(c_double), bind(c) :: degF, degC 

im Modul erwähnen.

Dies sind interoperable globale Variablen (durch Verknüpfung Assoziation). Sie verweisen nicht auf diese Variablen im Fortran-Code: Der Dummy und das Funktionsergebnis sind nicht diese Dinge.


In diesem einfachen Fall von oben und der anderen Antwort, wird man gerne ein Unterprogramm wie

hat
subroutine DegCtoF(n, degC, degF) bind(c,name='DegCtoF') 
    ... 
end subroutine 

aber das ist vielleicht eine gute Gelegenheit, die Verwendung des C-Descriptor zu beschreiben, von ISO_Fortran_binding.h. Beachten Sie jedoch, dass gfortran diesen Ansatz nicht sofort unterstützt.

Betrachten Sie die Fortran Quelle

subroutine DegCtoF(degC, degF) bind(c,name='DegCtoF') 
    use, intrinsic :: iso_c_binding, only : c_double 
    implicit none 
    real(c_double), intent(in), dimension(:) :: degC 
    real(c_double), intent(out), dimension(*) :: degF 

    degF(1:SIZE(degC)) = degC*1.8+32 
end subroutine DegCtoF 

(der Einfachheit halber werde ich davon ausgehen, dass die Speicherverwaltung von degF alle auf der C-Seite getan wird - man könnte natürlich über die angenommene Größe Array erweitern). Damit dieses Unterprogramm interoperabel ist, muss das Argument, das degC entspricht, ein Zeiger auf CFI_cdesc_t sein.

Auf den C-Code (mit einer Größe von magischen Zahlen)

#include "ISO_Fortran_binding.h" 
#include <stdio.h> 

void DegCtoF(CFI_cdesc_t*, double*); 

int main(int argc, char *argv[]) 
{ 
    printf("C and Fortran together!\n"); 

    CFI_CDESC_T(1) DegreesC_Fdesc; 
    CFI_index_t extent[1] = {2}; 
    CFI_rank_t rank = 1; 

    double DegreesC[2] = {32, 64}; 
    double DegreesF[2]; 

    CFI_establish((CFI_cdesc_t*)&DegreesC_Fdesc, &DegreesC, CFI_attribute_other, 
        CFI_type_double, 2*sizeof(double), rank, extent); 

    DegCtoF((CFI_cdesc_t*)&DegreesC_Fdesc, DegreesF); 
    printf("%3.1f [C] = %3.1f [F]\n", DegreesC[0], DegreesF[0]); 
    printf("%3.1f [C] = %3.1f [F]\n", DegreesC[1], DegreesF[1]); 

    return 0; 
} 

Hier CFI_establish einen geeigneten C-Deskriptor stellt DegreesC_Fdesc die zu dem angenommenen Form Fortran Scheinargument entsprechen kann. Innerhalb der Fortran-Subroutine gibt es überhaupt kein Problem, die Größe des ankommenden Arrays zu beurteilen.

+0

Vielen Dank für die Einsicht. –

Verwandte Themen