C++ Fortran wrapper



  • 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.



  • SeppJ schrieb:

    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.

    OK, dann entfällt das Verändern der Größe. Das dürfte dann aber selbst in Fortran nicht gehen.

    Ich weiß nicht, ob man in C++ ein Array mit new anlegen kann, das man in Fortran deletet. Dann könntest du deinen Vektor zumindest *irgendwie* zurückbekommen.


  • Mod

    Bashar schrieb:

    Ich weiß nicht, ob man in C++ ein Array mit new anlegen kann, das man in Fortran deletet. Dann könntest du deinen Vektor zumindest *irgendwie* zurückbekommen.

    Wilde Idee: malloc/free müssten zu Fortran kompatibel sein, weil man einfach die C-Version statt dem Fortran-Äquivalent benutzen kann.

    Bei new ist das natürlich kritischer, da theoretisch nicht einmal new und free kompatibel sein müssen. Man könnte natürlich jeweils einen C-kompatiblen Wrapper um new und delete machen. edit: Hmm, man bekommt natürlich auf keine Weise die nötige Typeninformation von Fortran aus zum C++-Teil. Man müsste sich also doch auf malloc beschränken oder lauter Spezialisierungen für verschiedene Typen anbieten. Beides unbefriedigend 😞 .



  • Okay,
    ein Vorschlag zur Güte in 1. Näherung:

    Nach meinem bisherigen Verständnis der Problemstellung muss der Array wachsen. Es lässt sich allerdings schon auf der Seite von Fortran abschätzen was die maximale neue Länge sein wird, nämlich gerade das Doppelte.

    Also wäre der Fahrplan:

    Fortran gibt den Array vor.
    Fortran hängt einen Array gleicher Länge mit Nullen gefüllt dran.
    Fortran übergibt den Array doppelter Länge mittels des Wrappers an C++.
    C++ arbeitet und gibt den Array zurück.

    Das sollte doch in 1. Näherung das Problem des allokierten Speichers umgehen, oder?

    Gruß,
    -- Klaus.


Anmelden zum Antworten