Verschiedene Objekte mit wiederkehrender Arbeit initialisieren



  • Ich habe zwar noch nie was von GSL gehört, aber müsstest du in

    bar::bar(bar const & other):
    acc(other.acc),spline(other.spline)
    {}
    

    nicht den Inhalt kopieren (und nicht nur den Pointer drauf)?

    Beachte

    {
      bar b = ...
      {
        bar kopie = b;
      } // ~bar macht gsl_spline_free(kopie.spline);
    } // ~bar macht gsl_spline_free(b.spline); => double free!!!
    


  • gsl schrieb:

    Ich habe zwar noch nie was von GSL gehört, aber müsstest du in

    bar::bar(bar const & other):
    acc(other.acc),spline(other.spline)
    {}
    

    nicht den Inhalt kopieren (und nicht nur den Pointer drauf)?

    Und wie?



  • ...



  • Okay,

    dann backen wir mal wieder kleine Brötchen. Also das Problem ist die deep copy eines Pointers der GSL in einen anderen.

    Ich habe mich an den C++11 Standard herangetraut und unique_ptr ausprobiert, der die Ressourcen verwaltet, oder nicht?

    Es scheint auch zu funktionieren:

    #include <iostream>
    #include <vector>
    #include <memory>
    
    #include <gsl/gsl_errno.h>
    #include <gsl/gsl_spline.h>
    
    int main()
    {
    	unsigned int N = 100;
    	std::vector<double> x,y;
    
    	for(unsigned int i = 0; i < N; ++i)
    	{
    		x.push_back(static_cast<double>(i));
    		double j = static_cast<double>(i) * static_cast<double>(i);
    		y.push_back(j);
    	}
    
    	std::unique_ptr<gsl_interp_accel> acc { gsl_interp_accel_alloc() };
    	std::unique_ptr<gsl_spline> spline { gsl_spline_alloc(gsl_interp_cspline,N) };
    
    	gsl_spline_init(spline.get(), x.data(), y.data(),x.size());	
    
    	std::cout << gsl_spline_eval(spline.get(), 2.5, acc.get()) << std::endl;
    
    	std::cout << "\n" << std::endl;
    
    	std::unique_ptr<gsl_interp_accel> acc2 { std::move(acc) };
    	std::unique_ptr<gsl_spline> spline2 { std::move(spline) };
    
    	std::cout << gsl_spline_eval(spline2.get(), 2.5, acc2.get()) << std::endl;
    
        gsl_spline_free(spline);
    	gsl_interp_accel_free(acc);
    
    	return 0;
    }
    

    Wenn ich das richtig verstehe, dann werden die Inhalte von acc und spline aus Zeile 20 und 21 in die neuen Pointer in Zeile 29 und 30 'ordentlich' kopiert. Also nicht nur die Adressen, sondern auch die Inhalte der Speicher, wo die Pointer hinzeigen.

    Das Problem ist jetzt nach wie vor die Freigabe des Speichers. Denn wie in Zeile 34 und 35 angedeutet, würde ich die gegebenen GSL Funktionen aufrufen, um den Speicher von acc und spline wieder freizugeben. Das funktionert allerdings nicht, weil mit dem unique_ptr kollidiert.

    :34:24: error: cannot convert ‘std::unique_ptr<gsl_spline>’ to ‘gsl_spline*’ for argument ‘1’ to ‘void gsl_spline_free(gsl_spline*)’
    :35:27: error: cannot convert ‘std::unique_ptr<gsl_interp_accel>’ to ‘gsl_interp_accel*’ for argument ‘1’ to ‘void gsl_interp_accel_free(gsl_interp_accel*)’
    

    Kann ich dem unique_ptr nicht sagen, dass er seine Ressource mittels der beiden freigeben soll.

    Aber dann müsste ich für beide GSL Pointer einen eigenen unique_ptr definieren? Bringt also auch nichts ...

    Also eigentlich alles Mist.

    Aber die vorgeschlagenen Methoden

    Swordfish schrieb:

    std::copy() oder std::memcpy() zum Bleistift.

    bringen auch nichts, denn wie ich festgestellt habe liefertn mir sizeof nur die Größe des Pointers und nicht des Speicherblocks, wo er hinzeigt. Ich kann aber auch nicht so einfach eine deep copy wie in diesem Beispiel machen, weil sich diese GSL Pointer nicht dereferenzieren lassen.

    Was sind das denn für ominöse Konstrukte! 😡



  • Klaus82 schrieb:

    Kann ich dem unique_ptr nicht sagen, dass er seine Ressource mittels der beiden freigeben soll.

    Ist das eine Frage? 😉
    Die Antwort wäre ja. Und das ist wohl auch der Weg, den Du gehen solltest.

    Eigentlich sollte jetzt ein kleines Beispiel kommen, aber es ist doch größer geworden (eins der Beispiele von der gsl Site).
    Das Herzstück sind allerdings die beiden unique_ptr , die diesmal einen deleter mitbekommen.

    #include <iostream>
    #include <memory>
    #include <vector>
    #include <cmath>
    
    #include <gsl/gsl_interp.h>
    
    class interpolation{
    public:
      // An C++ Algorithmen angelehnt:
      template<typename InputIter, typename InputIter2>
      interpolation(InputIter first1, InputIter last1, InputIter2 first2, const gsl_interp_type*& t = gsl_interp_cspline):
        x_(first1, last1),
        y_(first2, first2 + std::distance(first1,last1)),
        acc_(gsl_interp_accel_alloc(), gsl_interp_accel_free),
        interp_(gsl_interp_alloc(t, x_.size()), gsl_interp_free)
      {
        gsl_interp_init(interp_.get(), &x_.front(), &y_.front(), x_.size());
      }
    
      double operator()(double x)
      {
        return gsl_interp_eval (interp_.get(), &x_.front(), &y_.front(), x, acc_.get());
      }
      private:
      std::vector<double> x_,y_;
      std::unique_ptr<gsl_interp_accel, decltype((gsl_interp_accel_free))> acc_;
      std::unique_ptr<gsl_interp, decltype((gsl_interp_free))> interp_;
    };
    
    int main(){
      std::vector<double> x,y;
      std::cout << "#m=0,S=2\n";
      for(int i = 0;i<10; ++i){
        x.push_back(i + 0.5 * std::sin (i));
        y.push_back(i + std::cos (i * i));
        std::cout << x.back() << ' ' << y.back() << '\n';
      }
      std::cout << "#m=1,S=0\n";
    
      interpolation interp(begin(x), end(x), begin(y));
    
      for (double xi = x.front(); xi < x.back(); xi += 0.01)
        {
          std::cout << xi << ' ' << interp(xi) << '\n';
        }
    }
    


  • Das ist mal geiler Sch*** 😮

    Vielen Dank! 🙂

    Ich dein Beispiel genommen und vom Syntax etwas modifiert, um schließlich eine Header und C++ Datei zu schreiben:

    #ifndef WRAPPER_H_
    #define WRAPPER_H_
    
    #include <iostream>
    #include <vector>
    #include <memory>
    
    #include <gsl/gsl_errno.h>
    #include <gsl/gsl_spline.h>
    
    struct wrapper
    {
    	wrapper(std::vector<double>&, std::vector<double>&);
    
    	double operator() (double);
    
    	std::vector<double> x,y;
    	std::unique_ptr<gsl_interp_accel, decltype((gsl_interp_accel_free))> acc;
    	std::unique_ptr<gsl_spline, decltype((gsl_spline_free))> spline;
    };
    
    #endif
    
    #include <iostream>
    #include "wrapper.h"
    /*
    ---{}--- constructor ---{}---
    */
    wrapper::wrapper(std::vector<double>& x, std::vector<double>& y):
    x(x),y(y),
    acc(gsl_interp_accel_alloc(), gsl_interp_accel_free),
    spline(gsl_spline_alloc(gsl_interp_cspline,x.size()), gsl_spline_free)
    {
    	gsl_spline_init(spline.get(), x.data(), y.data(), x.size());
    }
    /*
    ---{}--- operator() ---{}---
    */
    double wrapper::operator() (double x)
    {
    	return gsl_spline_eval(spline.get(), x, acc.get());
    }
    

    Und jetzt muss in den Kopier und Zuweisungsoperator die move Semantik von einem unique_ptr in den anderen?

    Was mich anhand der GSL noch wundert: Laut der Beschreibung sind alle nötigen Informationen in spline gespeichert, d.h. ich könnte mir das kopieren der beiden Vektoren sparen, oder nicht? D.h. die Inhalte der unique_ptr werden kopiert und die Vektoren im neuen Objekt 'geleert' bzw. auf Null gesetzt?

    Gruß,
    -- Klaus.



  • Klaus82 schrieb:

    Und jetzt muss in den Kopier und Zuweisungsoperator die move Semantik von einem unique_ptr in den anderen?

    Im Kopier-c'tor wohl nicht.
    Brauchst Du denn beide in der kopierenden Variante? Schließlich sind das - in der jetzigen Form - immer zwei Vektoren, die da kopiert werden.
    Aber mach erstmal...

    Klaus82 schrieb:

    Was mich anhand der GSL noch wundert: Laut der Beschreibung sind alle nötigen Informationen in spline gespeichert, d.h. ich könnte mir das kopieren der beiden Vektoren sparen, oder nicht? D.h. die Inhalte der unique_ptr werden kopiert und die Vektoren im neuen Objekt 'geleert' bzw. auf Null gesetzt?

    Das "higher level interface" habe ich mal schön ausser acht gelassen. Geht aber sicherlich auch ohne std::vector und dann als Wrapper um diese splines.



  • Furble Wurble schrieb:

    Klaus82 schrieb:

    Und jetzt muss in den Kopier und Zuweisungsoperator die move Semantik von einem unique_ptr in den anderen?

    Im Kopier-c'tor wohl nicht.
    Brauchst Du denn beide in der kopierenden Variante? Schließlich sind das - in der jetzigen Form - immer zwei Vektoren, die da kopiert werden.
    Aber mach erstmal...

    Ich bin mir gerade nicht mehr so sicher, was die Benutzung von unique_ptr angeht. Ich bin darauf aus einen Wrapper zu schreiben, d.h. eine Klasse, die ihre Ressourcen verwaltet. Zum Einen aus der Motivation heraus, dass die GSL mit Pointern arbeitet und zum anderen, um lokale Kopien der Objekte an verschiedene Threads zu verteilen (-> Open MP).

    Wenn ich das mit den Pointern richtig verstehe, dann müssen sowohl der Pointer, d.h. die ihn im gespeicherte Adresse und der Speichbereich worauf er zeigt, verwaltet werden.

    Dann schaue ich mir die member function constructor des uniqe_ptr an.

    Wenn ich das richtig verstehe, dann geht es darum sowohl um den Pointer, d.h. die Adresse:

    The object takes ownership of p

    D.h. der unique_ptr kümmert sich um die Adresse des Pointers und

    The object acquires the content managed by x

    Da bin ich mir nicht so sicher, aber ich verstehe das so, dass mit the content managed der Speicherbereich gemeint ist, worauf der Pointer zeigt. 😕

    Die Sache ist nun, dass wir einen einzigartigen (unique) Pointer haben: Wenn ich die Intention richtig verstehe, dann ist genau der unique_ptr für den ihm gegebenen Pointer und den zugrunde liegenden Speicherbereich zuständig - und sonst niemand! Also kein anderer Pointer darf auf den gleichen Speicherbereich zeigen. Das klingt sehr sinnvoll für die beabsichtigte Resourcenverwaltung.

    Wenn ich nun ein neues Objekt B anhand eines vorhandenen (gleichen) Objekts A initialisieren möchte, dann muss das doch so gehen:
    i) Ich muss so viel Speicher bereitstellen, wie jener groß ist worauf der Pointer in A (verpackt im unique_ptr ) zeigt.
    ii) Ich muss den Inhalt des Speichers worauf der Pointer in A zeigt hinüberkopieren in den neu angelegten Speicher.
    <--- jetzt bin ich eigentlich mit Objekt A schon fertig ---->
    iii) Ich muss in B einen Pointer bereitstellen, der zu dem neu angelegten Inhalt im Speicher passt.
    iv) Ich muss die Adresse an diesen Pointer übergeben
    v) Ich muss den Pointer (und damit den Inhalt) in einen unique_ptr stecken, der fortan darüber wacht!

    Und dabei ist es kontraintuitiv, weil der unique_ptr keinen Kopierkonstruktor besitzt. 😕
    Aber vielleicht bezieht sich dieses Kopieren nur auf die Adresse im Pointer, denn es darf ja kein anderer Pointer auf den Speichbereich zeigen, also ist es unnötig die Adresse kopieren zu müssen.

    Gruß,
    -- Klaus.



  • Klaus82 schrieb:

    Wenn ich das mit den Pointern richtig verstehe, dann müssen sowohl der Pointer, d.h. die ihn im gespeicherte Adresse und der Speichbereich worauf er zeigt, verwaltet werden.

    Ja. Klingt einleuchtend.

    Klaus82 schrieb:

    Wenn ich nun ein neues Objekt B anhand eines vorhandenen (gleichen) Objekts A initialisieren möchte, dann muss das doch so gehen:
    i) Ich muss so viel Speicher bereitstellen, wie jener groß ist worauf der Pointer in A (verpackt im unique_ptr ) zeigt.
    ii) Ich muss den Inhalt des Speichers worauf der Pointer in A zeigt hinüberkopieren in den neu angelegten Speicher.
    <--- jetzt bin ich eigentlich mit Objekt A schon fertig ---->
    iii) Ich muss in B einen Pointer bereitstellen, der zu dem neu angelegten Inhalt im Speicher passt.
    iv) Ich muss die Adresse an diesen Pointer übergeben
    v) Ich muss den Pointer (und damit den Inhalt) in einen unique_ptr stecken, der fortan darüber wacht!

    So schlimm wird's schon nicht werden - den unique_ptr benutzt Du hauptsächlich, damit die *_free() Funktionen überall ordentlich aufgerufen werden.
    Bevor Du so ein Ding wie einen gsl_interp_alloc* kopierst, allozierst und generierst Du Dir lieber einen neuen über das Interface, das gsl dafür bietet.



  • <--- kann gelöscht werden --->


Anmelden zum Antworten