C++ Fortran wrapper



  • Hallo ihr Lieben,

    ich versuche mich gerade an einer Schnittstelle zwischen Fortran und C++ , genauer die Übergabe eines Arrays.

    Bisher sieht mein einfaches Fortran Programm wie folgt aus:

    program me
    implicit none
    
    integer, parameter :: length = 10
    integer :: n
    
    real(8), dimension (1:length) :: array
    
    do n = 1, length
    	array(n) = n
    end do
    
    call show_array(length,array)
    
    end program me
    

    Mein zugehöriger Wrapper wie folgt:

    #include <iostream>
    #include <vector>
    
    extern "C"{
    	void show_array_(unsigned int* n, double* arr);
    }
    
    void show_array_(unsigned int* n, double* arr)
    {
    	std::vector<double> vec(*n);
    
    	for(unsigned int i = 0; i < vec.size(); ++i)
    		vec[i] = arr[i];
    }
    

    Jetzt frage ich mich natürlich, ob das nicht eleganter geht. Wenn ich das richtig verstehe, dann sind arrays und vectoren Felder, deren Weitergabe i.d.R. dadurch geschieht, dass ein Pointer auf das erste Element weitergereicht wird.

    Kann ich also als Argument von show_array nicht direkt den Vektor hineinschreiben oder muss ich wirklich den Array aus Fortran übernehmen und dann nachträglich den Vector in C++ füllen?

    Viele Grüße,
    -- Klaus.


  • Mod

    Du kannst vectoren wie Arrays behandeln. &vec[0] (oder seit C++11 auch einfach vec.data()) sind Zeiger auf das erste Element. Es ist garantiert, dass die Elemente allesamt direkt hintereinander im Speicher liegen, wie bei einem Array.

    (Wieder der Disclaimer an all die Klugscheißer, die auf 5 Seiten Nebenerklärungen bestehen, die sonst niemanden interessieren (der Rest möge hier aufhören zu lesen): In C++98 ist nur garantiert, dass die Elemente nebeneinander im Speicher liegen, aber nicht aufsteigend hintereinander. Wer eine Implementierung kennt, die tatsächlich die Elemente rückwärts speichert, möge sich melden, das wäre ein prima Partygag.)



  • Klaus82 schrieb:

    Jetzt frage ich mich natürlich, ob das nicht eleganter geht. Wenn ich das richtig verstehe, dann sind arrays und vectoren Felder, deren Weitergabe i.d.R. dadurch geschieht, dass ein Pointer auf das erste Element weitergereicht wird.

    Das stimmt für Arrays, aber nicht für Vektoren. vector ist eine Klasse, die ein Array verwaltet und muss daher wie eine Klasse behandelt werden. Du kannst, wie SeppJ schon schrub, mit den Innereien direkt arbeiten, ohne alles über die vector-Schnittstelle zu machen, aber für die Weitergabe gelten die üblichen Regeln bzgl. Objektkopie/-move usw.

    Kann ich also als Argument von show_array nicht direkt den Vektor hineinschreiben oder muss ich wirklich den Array aus Fortran übernehmen und dann nachträglich den Vector in C++ füllen?

    Wenn Fortran nicht direkt mit Vektoren umgehen kann, du aber in C++ mit Vektoren arbeiten musst (wofür ich nicht unbedingt einen Grund sehe, immerhin musst du die Ressource nicht verwalten), dann musst du erstmal umkopieren.

    Das geht aber einfacher:

    void show_array_(unsigned int* n, double* arr)
    {
        std::vector<double> vec(arr, arr + *n);
        // arbeite mit vec
    }
    


  • Hallo 🙂

    SeppJ schrieb:

    Du kannst vectoren wie Arrays behandeln. &vec[0] (oder seit C++11 auch einfach vec.data()) sind Zeiger auf das erste Element.

    Ach was, wieder etwas gelernt. Danke.

    Bashar schrieb:

    Wenn Fortran nicht direkt mit Vektoren umgehen kann, du aber in C++ mit Vektoren arbeiten musst (wofür ich nicht unbedingt einen Grund sehe, immerhin musst du die Ressource nicht verwalten)

    Ich verwende die Vektoren dann für Interpolation oder numerische Integration und da finde ich den Syntax von vector sehr nützlich wie front() , back() und size() .

    Bashar schrieb:

    , dann musst du erstmal umkopieren.

    Das geht aber einfacher:

    void show_array_(unsigned int* n, double* arr)
    {
        std::vector<double> vec(arr, arr + *n);
        // arbeite mit vec
    }
    

    Ja, sehr cool. Danke! 🙂

    Zuletzt geht es darum, dass der übergebene Array in meinem Cpp Code bearbeitet wird und dadurch auch wächst, d.h. das Umkopieren in einen vector ist auch nützlich.
    Allerdings möchte ich den Inhalt dann wieder an den Fortran Code zurückgeben. Kann ich diese dann einfach machen, indem ich den übergebenen Pointer double* arr auf meinen Vektor ausrichte?

    Gruß,
    -- Klaus.



  • Klaus82 schrieb:

    Bashar schrieb:

    Wenn Fortran nicht direkt mit Vektoren umgehen kann, du aber in C++ mit Vektoren arbeiten musst (wofür ich nicht unbedingt einen Grund sehe, immerhin musst du die Ressource nicht verwalten)

    Ich verwende die Vektoren dann für Interpolation oder numerische Integration und da finde ich den Syntax von vector sehr nützlich wie front() , back() und size() .

    Das ist ja nichts essentielles.

    Zuletzt geht es darum, dass der übergebene Array in meinem Cpp Code bearbeitet wird und dadurch auch wächst, d.h. das Umkopieren in einen vector ist auch nützlich.

    Das schon, das geht mit Arrays natürlich nicht.

    Allerdings möchte ich den Inhalt dann wieder an den Fortran Code zurückgeben. Kann ich diese dann einfach machen, indem ich den übergebenen Pointer double* arr auf meinen Vektor ausrichte?

    Nein, definitiv nicht. Der Speicher gehört dem Vektor und wird ungültig, wenn der Vektor aus dem Scope geht (also die Funktion zurückkehrt). Hier musst du völlig neu denken. Wie funktioniert das denn in Fortran, wenn man da ein Array in einer Funktion in der Größe verändert? Stimmt die Aufrufkonvention -- Pointer auf das erste Element -- überhaupt? Das würde ich in dem Fall ja anzweifeln müssen.

    edit: Das geht auch schon deshalb nicht, weil der Pointer ja by value übergeben wurde und daher nicht nach außen wirksam auf den Vektor umgebogen werden kann...


  • Mod

    Klaus82 schrieb:

    Allerdings möchte ich den Inhalt dann wieder an den Fortran Code zurückgeben. Kann ich diese dann einfach machen, indem ich den übergebenen Pointer double* arr auf meinen Vektor ausrichte?

    Ja.



  • SeppJ schrieb:

    Klaus82 schrieb:

    Allerdings möchte ich den Inhalt dann wieder an den Fortran Code zurückgeben. Kann ich diese dann einfach machen, indem ich den übergebenen Pointer double* arr auf meinen Vektor ausrichte?

    Ja.

    😃

    Ja, eigentlich hätte ich mich solch einer Antwort rechnen müssen.

    Ich arbeite an einem Vorschlag, warten sie ...



  • So,

    ich hätte da mal folgenden Vorschlag für den wrapper:

    #include <iostream>
    #include <vector>
    
    extern "C"{
    	double* show_array_(unsigned int* n, double* arr);
    }
    
    double* show_array_(unsigned int* n, double* arr)
    {
    	std::vector<double> vec(arr, arr + *n);
    
    	vec.push_back(vec.size() + 1);
    
    	return vec.data();
    }
    

    Das kompiliert auch, gut muss zunächst nichts heißen! 😃

    Was mich aber wieder wundert: Will ich jetzt einen Pointer zurückgeben oder eine Adresse, die dann in einen Pointer gesteckt wird?
    Eigentlich dachte ich an letzteres, deshalb dachte ich zunächst an die folgende Konstruktion:

    std::vector<double>& show_array_(unsigned int* n, double* arr)
    {
    // stuff
        return *vec;
    }
    

    Aber warum funktioniert das nicht?

    Gruß,
    -- Klaus.



  • Wieso ignorierst du meine Postings?



  • Bashar schrieb:

    Wieso ignorierst du meine Postings?

    Du meinst den Teil, dass ich einen vector wie eine Klasse behandeln muss?

    Gruß,
    -- Klaus.


  • Mod

    Bashar schrieb:

    Wieso ignorierst du meine Postings?

    Meine Schuld, ich hätte nicht die einfache Antwort "Ja." geben dürfen. Ich hatte erwartet, dass er so etwas wie Gültigkeitsbereiche beherrscht, wenn er sich an Interoperabilität von C++ und Fortran versucht. Dann hätte es gereicht, wenn er weiß, dass ein vector an sich ein Array verwaltet. Anscheinend mangelt es aber an den Grundlagen, weshalb jetzt eine völlig falsche Umsetzung des Ratschlags erstellt wurde. Das würde so ja weder in C++ noch in Fortran funktionieren und natürlich erst recht nicht über Sprachgrenzen hinweg.



  • Nein, das zweite Posting.



  • Bashar schrieb:

    Nein, das zweite Posting.

    Oh, das hatte ich gar nicht gesehen.

    Dann schauen wir mal wieder, was ich wieder alles zu lernen habe ...



  • Klaus82 schrieb:

    Bashar schrieb:

    Nein, das zweite Posting.

    Oh, das hatte ich gar nicht gesehen.

    Ich weiß, mein vorheriges Posting war auch eher als Hinweis darauf zu verstehen ... aber lassen wir das.



  • Also um auf diesen Gültigkeitsbereich einzugehen, vielleicht verstehe ich ein Teil des Problems richtig, was erstmal gar nichts mit Arrays & Co. zu tun hat.

    Ich arbeite in einer Funktion und erstelle darin eine Variable. Jetzt sollte es doch prinzipiell möglich sein, dass ich die Adresse dieser Variable als return -statement der Funktion ausgebe.

    Diese Adresse speichere ich dann in einem Pointer.

    Das Problem ist nun, dass in diesem Augenblick die Funktion beendet ist und damit alles ungültig wird, was darin initialisiert wurde.

    Damit zeigt der Pointer ... ja wohin eigentlich? Ist das nicht einfach ein Dangling Pointer? Oder ist das schlimmer, weil der Speicher nicht einfach nur angefordert und vergessen wurde wieder freizugeben, sondern regelrecht 'ungültig' ist?

    Gruß,
    -- Klaus.



  • Klaus82 schrieb:

    Ich arbeite in einer Funktion und erstelle darin eine Variable. Jetzt sollte es doch prinzipiell möglich sein, dass ich die Adresse dieser Variable als return -statement der Funktion ausgebe.

    Ja, das ist möglich. Zumindest prinzipiell.

    Damit zeigt der Pointer ... ja wohin eigentlich?

    Der zeigt dahin, wo er vorher hingezeigt hat. Aber da liegt nicht mehr dasselbe wie vorher ... vielleicht liegen da noch Überreste deines Objektes, vielleicht sogar unverändert. Vielleicht liegt da jetzt ein neues Objekt. Vielleicht Rücksprungadressen oder dergleichen. Vielleicht wurde der Speicher auch an das Betriebssystem zurückgegeben. Wer weiß?



  • Okay.

    Also kann der Inhalt der Funktion nicht über das return -statement stattfindetn.

    Kann ich da mit einer Mischung aus Pointer / Referenz und move-Semantik arbeiten? Ich übergebe einen Pointer an die Funktion und bewege dann den gewünschten Inhalt mittels move .

    Damit sollte der Inhalt in den Gültigkeitsbereich des Pointers / der Referenz übergehen und die überleben ja das Ende der Funktion.

    Sind da brauchbare Gedanken dabei?

    Gruß,
    -- Klaus.



  • Theoretisch schon, aber das ganze steht und fällt damit, was von der Fortran-Seite her möglich ist. Wenn Arrays so wie von dir gezeigt wie in C übergeben werden, kannst du das alles vergessen. Ist das überhaupt so? Probier das mal aus:

    #include <iostream>
    #include <vector>
    
    extern "C"{
        void show_array_(unsigned int* n, double* arr);
    }
    
    void show_array_(unsigned int* n, double* arr)
    {
        for (unsigned i = 0; i < *n; ++i)
          cout << arr[i] << '\n';
    }
    

    ?



  • Das funktioniert ohne Probleme. 🙂

    Gruß,
    -- Klaus.


  • Mod

    Bashar schrieb:

    Theoretisch schon, aber das ganze steht und fällt damit, was von der Fortran-Seite her möglich ist. Wenn Arrays so wie von dir gezeigt wie in C übergeben werden, kannst du das alles vergessen. Ist das überhaupt so?

    Ja, Fortran und C arbeiten zusammen, habe ich schon einmal gemacht. Aufpassen muss man vor allem bei 2 Dingen:
    1. in Fortran ist alles "by reference", aber auch nicht richtig, sondern über die selbe Krücke wie in C mittels Pointern. Man sieht es bloß nicht so leicht.
    2. Mehrdimensionale Arrays sind genau anders herum geordnet.


Anmelden zum Antworten