Verschiedene Objekte mit wiederkehrender Arbeit initialisieren



  • daddy_felix schrieb:

    Klaus82 schrieb:

    Auf jeden Fall scheine ich mit dem folgenden Minimalbeispiel (siehe unten) genau den Kern meines Problems zu treffen:

    Warum besitzt das struct ein rohes Array?

    double* p_array; // warum kein std::vector???????
    

    Wenn du dieses Array durch einen std::vector ersetzt, erleichterst du dir das Leben ungemein!

    Eine schlichte Antwort ist einfach, dass ich die ganzen Methoden eines STL Containers nicht benötige.

    Wie gesagt, zum Einen möchte ich in einer Struktur Arrays bereithalten, welche die GSL dann verwendet, um interpolierte Werte zu berechnen.

    Und zum anderen möchte ich einen Array von solchen Strukturen, genauer einen Array mit Pointern auf solche Strukturen, anlegen, um die verschiedenen Strukturen zu verwalten und mittels Index darauf zugreifen zu können.

    Damit ist die Verwendung eines STL Containers für mich 'mit Kanonen auf Spatzen schießen' in diesem Zusammenhang.

    Gruß,
    -- Klaus.



  • Klaus82 schrieb:

    Eine schlichte Antwort ist einfach, dass ich die ganzen Methoden eines STL Containers nicht benötige.

    Doch, das tust du. Der Container nimmt dir bspw. das Speichermanagement ab und du hast keine Probleme beim Kopieren. Zudem musst du nicht die Größe separat mitschleppen.

    Klaus82 schrieb:

    genauer einen Array mit Pointern auf solche Strukturen

    Warum denn schon wieder Pointer?

    BTW, Indexzugriff mit [] ist bei einem std::vector genauso möglich wie bei einem veralteten C-Array.



  • daddy_felix schrieb:

    BTW, Indexzugriff mit [] ist bei einem std::vector genauso möglich wie bei einem veralteten C-Array.

    veraltet, haha. ich hab's! nicht idiomatisch. ein anti-pattern. 🤡



  • Klaus82 schrieb:

    Eine schlichte Antwort ist einfach, dass ich die ganzen Methoden eines STL Containers nicht benötige.

    Zusätzlich zu dem, was daddy_felix schon sagte: Diese Aussage klingt so, als würdest du einen ungeheuren Overhead befürchten. Das ist nicht so, std::vector ist üblicherweise genau gleich schnell wie dynamisch angelegte Arrays. Je nach Operation sogar schneller, dank seiner Allokationsstrategie. Ausserdem erscheinen nicht aufgerufene Methoden nicht im Binary, sodass du nur für das bezahlst, was du wirklich brauchst.

    Klaus82 schrieb:

    Damit ist die Verwendung eines STL Containers für mich 'mit Kanonen auf Spatzen schießen' in diesem Zusammenhang.

    Ne. STL-Container sind der Standardansatz, wenn du dynamische Datenstrukturen benötigst. Wenn du eine selbstgestrickte Lösung oder direkt new[] und delete[] einsetzt, solltest du einen wirklich guten Grund dafür haben. Denn diese sind die Ausnahme, ihre Nachteile sollte man nicht leichtfertig in Kauf nehmen. Diese wären:

    • Manuelle Speicherverwaltung ist viel aufwändiger, fehleranfälliger und weniger übersichtlich als RAII.
    • Du musst die Grösse separat speichern.
    • Du kannst nicht leicht zwischen verschiedenen Containertypen wechseln, sollten sich die Anforderungen ändern.
    • Sämtliche Operationen wie Einfügen, Löschen etc. müssen nachprogrammiert werden. Auch wenn du diese im Moment nicht brauchst, kann es plötzlich sein, dass du sehr froh um sie wärst. Mindestens ein Iterator-Interface sollte gegeben sein
    • Du hast überhaupt keine Laufzeitchecks, was Bugs schwierig zu finden macht. Vernünftige STL-Implementierungen prüfen Indizes, Iteratoren etc. im Debug-Modus (d.h. kein Overhead) auf Gültigkeit, sodass entsprechende Fehler sofort entdeckt werden.

    volkard schrieb:

    veraltet, haha. ich hab's! nicht idiomatisch. ein anti-pattern. 🤡

    Ja. Aber nicht wegen std::vector , sondern std::array :p

    Aber ernsthaft, falls man C++11 verwenden kann, spricht wirklich nichts mehr für C-Arrays. Und selbst vorher ist std::tr1::array , boost::array oder zur Not eine selbstgeschriebene Klasse sinnvoller.



  • So,
    ich habe für die STL Befürworter was gebastelt.

    Ist das so gemeint bzw. besser?

    Viele Grüße,
    -- Klaus.

    // mb.cpp
    #include <iostream>
    #include <vector>
    struct foo
    {
    foo(unsigned int const); 
    unsigned int const n;
    std::vector<double> daten;
    };
    
    foo::foo(unsigned int const x):
    n(x)
    {
    	for(unsigned int i = 0; i < n; ++i)
    		daten.push_back(i*0.2);
    }
    
    struct bar
    {
    	bar(std::vector<double>);
    	std::vector<double> daten;
    };
    
    bar::bar(std::vector<double> x):
    daten(x)
    {}
    
    int main()
    {
    
    	unsigned const n = 10; 
    
    	struct foo f(n);
    
    	struct bar b(f.daten);
    
    	std::cout << "Time to clean up!" << std::endl;
    
    	return 0;
    }
    


  • 👍

    * zwei Anmerkungen: das "n" in "foo" brauchst du vermutlich nicht mehr
    * dem Konstruktor von "bar" solltest du den vector als Referenz übergeben



  • Jetzt habe ich aber das Problem, dass die GSL scheinbar mit diesen Containern nicht zurecht kommt.

    Jetzt kriege ich z.B. die Fehlermeldung

    mb.cpp: In constructor ‘bar::bar(std::vector<double, std::allocator<double> >, std::vector<double, std::allocator<double> >)’:
    mb.cpp:39: error: cannot convert ‘std::vector<double, std::allocator<double> >’ to ‘const double*’ for argument ‘2’ to ‘int gsl_spline_init(gsl_spline*, const double*, const double*, size_t)’
    make: *** [mb.o] Fehler 1
    

    Zeile 39 weißt auf die Initialisierung hin, dazu sagt auch die GSL Dokumentation, dass ich einen const Array übergeben soll.

    Was mach ich denn jetzt?

    Gruß,
    -- Klaus.

    // mb.cpp
    #include <iostream>
    #include <vector>
    #include <gsl/gsl_errno.h>
    #include <gsl/gsl_spline.h>
    struct foo
    {
    foo(unsigned int const); 
    unsigned int const n;
    std::vector<double> x_values;
    std::vector<double> y_values;
    };
    
    foo::foo(unsigned int const x):
    n(x)
    {
    	for(unsigned int i = 0; i < n; ++i)
    	{
    		x_values.push_back(i);
    		y_values.push_back(i*i);
    	}
    }
    
    struct bar
    {
    	bar(std::vector<double>,std::vector<double>);
    	~bar();
    	std::vector<double> x_values;
    	std::vector<double> y_values;
    	unsigned int const n;
    
    	gsl_interp_accel* acc;
    	gsl_spline* spline;
    };
    
    bar::bar(std::vector<double> x, std::vector<double> y):
    x_values(x), y_values(y), n(x_values.size()),
    acc(gsl_interp_accel_alloc()),spline(gsl_spline_alloc(gsl_interp_cspline,n))
    {
    	gsl_spline_init(spline,x_values,y_values,n);
    }
    
    bar::~bar()
    {
    	gsl_spline_free(spline);
    	gsl_interp_accel_free(acc);
    }
    
    int main()
    {
    
    	unsigned const n = 10; 
    
    	struct foo f(n);
    
    	struct bar b(f.x_values,f.y_values);
    
    	std::cout << "Time to clean up!" << std::endl;
    
    	return 0;
    }
    


  • void DoSomething(const double* ptr)
    {
    // ...
    }
    
    // ...
    
    std::vector<double> tst(10);
    double* adressOfFirstElement = &tst.front();
    
    DoSomething(adressOfFirstElement);
    

    Ein std::vector garantiert, dass die Elemente wie in einem Array angeordnet sind. Somit kannst du die Adresse des ersten Elements übergeben.



  • Also um das Beispiel von oben zu ergänzen muss ich den spline der GSL wie folgt initialisieren

    gsl_spline_init(spline,& x_values.front(),& y_values.front(),n);
    

    Gruß,
    -- Klaus.



  • Kurz dazu:

    Nexus schrieb:

    Aber ernsthaft, falls man C++11 verwenden kann, spricht wirklich nichts mehr für C-Arrays. Und selbst vorher ist std::tr1::array , boost::array oder zur Not eine selbstgeschriebene Klasse sinnvoller.

    Ich sehe regelmäßig im Linux Magazin Artikel von Rainer Grimm zum neuen C++11 Standard. Sein Buch wird auch hier im Forum als gut befunden.

    Was mich etwas wundert: Mir wurde zur Verwendung von z.B. vector geraten, aber das ist doch 'nur' STL.

    Im Wikieintrag von C++11 [1] steht z.B.

    C++11 includes several additions to the core language and extends the C++ standard library, [..]

    während beim verlinkten Artikel der standard library [2] steht

    The C++ Standard Library is based upon conventions introduced by the Standard Template Library (STL). Although the C++ Standard Library and the STL share many features, neither is a strict superset of the other. In particular, the C++ Standard Library has also been influenced by the work of Alexander Stepanov and Meng Lee.

    Was mich nun zu zwei fragen führt:

    Haben wir jetzt über einen vector der STL gesprochen oder C++11? Ich habe lediglich #include <vector> geschrieben und schon funktioniert die Verwendung. Was mich gerade zu der peinlichen Frage führt, woran sehe ich das? Ich habe ja kein C++ installiert, sondern einen Compiler ( gcc (Debian 4.4.5-8) 4.4.5). Und dem kann ich z.B. die Option std=c++0x mitgeben, was die Verwendung des neuen Standards signailisiert?

    Die nächste einfache Frage ist: Lohnt sich das Buch (schon) für mich? Wenn ich mir das Inhaltsverzeichnis [3] ansehe, dann ist nur Kapitel 'IV Standardbibliothek' relevant und dazu habe ich schon das Buch 'Die C++ Standardbibliothek von Kuhlins und Schader'

    Viele Grüße,
    -- Klaus.



  • Klaus82 schrieb:

    Was mich etwas wundert: Mir wurde zur Verwendung von z.B. vector geraten, aber das ist doch 'nur' STL.

    Was heisst hier "nur"? Die STL ist nach wie vor eine der wichtigsten Komponenten der C++-Standardbibliothek. In C++11 sind neue Container und Algorithmen dazu gekommen, was nicht heisst, dass man die alten nicht mehr benutzen sollte.

    Auch musst du beim Begriff "STL" etwas aufpassen. Teilweise wurde er für alternative Implemenentierungen verwendet (d.h. Container-Bibliotheken ausserhalb der Standardbibliothek), besonders vor der Standardisierung. In diesem Forum ist mit STL aber meist die Standard Template Library als Bestandteil der Standardbibliothek gemeint, sowohl in C++98 als auch in C++11.



  • So,

    fast das große Finale: Ist die Intention bisher richtig verpackt?

    Die Struktur foo ist mittels build dafür zuständig die (x,y) Paare für lineare Funktionen zu basteln, während ich als Argument gerade die Steigung vorgebe.
    Im ersten Fall erstelle ich die Tupel für die Steigung m=1, initialisiere eine Struktur b_one mit diesen Werten und räume anschließend in foo mittels clean_up wieder auf, sodass diese bereit ist für die nächste Funktion.

    Schließlich bastle ich für die Steigung m=2 die nächsten (x,y) Werte, übergebe diese an die zweite initialisierte Struktur b_two und räume dann auch wieder auf.

    Ist die STL jetzt richtig und sinnvoll eingesetzt?

    Gruß,
    -- Klaus.

    <-- Hier stand Blödsinn -->



  • So,
    ich habe versucht das obige Vorgehen dahingehend zu ändern, dass ich nicht jedes Mal eine Instanz von bar initialisieren muss, sondern diese brav in einen vector schiebe.

    Leider habe ich dann wieder das alte Problem des Speicherzugriffs.

    Sehe ich das richtig, dass die Verwendung von std::vector<struct bar> mein Problem des Speicherzugriffsfehlers nicht löst und ich mich nach wie vor mit Copy & Swap Idiom beschäftigen muss, wie in diesem Post beschrieben?

    Ich hatte mich schon gewundert, dass die Verwendung von vector diese Probleme beheben soll, immerhin definiere ich einen Konstruktor und Destruktor von foo , d.h. ich muss mich nach Ro3 automatisch mit Kopieren und Zuweisung beschäftigen?

    Gruß,
    -- Klaus.

    <-- Hier stand Blödsinn -->



  • Also ich kriege es einfach nicht hin. 😞

    Ich habe versucht den Kopier- und Zuweisungsoperator zu basteln, aber ich kriege immernoch einen Speicherzugrifffehler und Valgrind schimpft auch mit mir.

    // mb.cpp
    #include <iostream>
    #include <vector>
    #include <gsl/gsl_errno.h>
    #include <gsl/gsl_spline.h>
    struct foo
    {
    void build (unsigned int const);
    void clean_up();
    std::vector<double> x_values;
    std::vector<double> y_values;
    };
    
    void foo::build(unsigned int const m)
    {
    	for(unsigned int i = 0; i < 100; ++i)
    	{
    		x_values.push_back(i);
    		y_values.push_back(m*i);
    	}
    }
    
    void foo::clean_up()
    {
    	x_values.clear();
    	y_values.clear();
    }
    
    struct bar
    {
    	bar(std::vector<double> &,std::vector<double> &);
    	bar(bar const &);
    	bar& operator=(const bar&);
    	~bar();
    	std::vector<double> x_values;
    	std::vector<double> y_values;
    	double get(double);
    
    	gsl_interp_accel* acc;
    	gsl_spline* spline;
    };
    
    bar::bar(std::vector<double> & x, std::vector<double> & y):
    x_values(x), y_values(y),
    acc(gsl_interp_accel_alloc()),spline(gsl_spline_alloc(gsl_interp_cspline,x_values.size()))
    {
    	gsl_spline_init(spline,& x_values.front(),& y_values.front(),x_values.size());
    }
    
    bar::bar(bar const & other):
    acc(other.acc),spline(other.spline)
    {}
    
    bar& bar::operator=(const bar& other)
    {
    	if(this != &other)
    	{
    		gsl_spline_free(spline);
    		gsl_interp_accel_free(acc);
    	}
    
    	acc = gsl_interp_accel_alloc();
    	spline = gsl_spline_alloc(gsl_interp_cspline,x_values.size());
    
    	return *this;
    }
    
    bar::~bar()
    {
    	if(spline != 0)
    		gsl_spline_free(spline);
    
    	if(acc != 0)
    		gsl_interp_accel_free(acc);
    }
    
    double bar::get(double x)
    {
    	return gsl_spline_eval(spline,x,acc);
    }
    
    int main()
    {
    	struct foo f;
    	std::vector<struct bar> bars;
    
    	f.build(1);
    
    	bars.push_back(bar(f.x_values,f.y_values));
    
    	f.clean_up();
    
    	return 0;
    }
    


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


Anmelden zum Antworten