//explizite konvertierung in andere klasse via operator=
-
Meep Meep schrieb:
re
also jetzt steh ich auf dem schlauch.
dein 'MyVector' hat keinen std::vector als member ?
oder wie ist das genau.
??genau. letztlich enthält er ein simples dynamisches array :-), da der std::vector hier zuviel overhead mitbringt und ich hier das speichermanagement selbst managen möchte. leider ist dies hie ruach nicht mit eigenen allocators möglich. lange rede kurzer sinn: MyVector hat nen Allocator, der im std-fall ein dynamisches array allokiert.
Meep Meep schrieb:
und warum hast du dann die daten doppelt im speicher vorliegen ?
weil die von dir vorgeschlagene Lösung einen std::vector in der HasVector klasse vorhält... aber das war wohl ein missverständnis.
[quote="Meep Meep"]
in deinem starter-posting hast du doch auch eine kopie des inhaltes des vectors gemacht.[/quote="Meep Meep"]
habe ich... std::vector<T>::operator[] zu MyVector<T>::operator[], wobei letzterer data[i] retuniert und data im std-fall T* ist
Meep Meep schrieb:
irgendwie kapier ich das jetz grad nicht so wirklich.
sorry dafür
Meep Meep schrieb:
was meintest du mit
aber da ich die std.klasse nicht ändern kann/darf/möchte,?
std::vector ??genau.
-
najut dann probier ich es nochmal.
vorausgesetzt das du ein normales dynamisches array verwendest, koennte folgendes passen:
template<class T> class MyVector { public: typedef T* iterator; typedef const T* const_iterator; typedef T value_type; friend std::vector<T>& operator=(std::vector<T> &vector, const MyVector<T> &hv) { vector.resize(hv.size()); MyVectorBasic<T>::const_iterator begin = hv.begin(); MyVectorBasic<T>::const_iterator end = hv.end(); while(begin != end) vector.push_back(*begin++); return vector; } std::size_t size(void) const { return dyn_array_size } iterator begin(void) { return dyn_array; } iterator end(void) { return dyn_array + dyn_array_size; } const_iterator begin(void) const { return dyn_array; } const_iterator end(void) const { return dyn_array + dyn_array_size; } protected: T *dyn_array; std::size_t dyn_array_size; };ist es so passender ? oder schon wieder thema verfehlt ?
Meep Meep
-
das sieht doch gut aus, bis auf die Tatsache, dass das nicht erlaubt ist:
friend std::vector<T>& operator=(std::vector<T> &vector, const MyVector<T> &hv)das entspricht eienr globalen überladung und genau die ist für diesen operator nicht zulässig! und genau da liegt das ursprungsproblem
-
darf vom std her der operator= nicht ueberladen werden ? war mir bis jetzt nicht bewusst oder wurde der globale operator= schon mal definiert ?
Meep Meep
-
[Meep Meep] schrieb:
darf vom std her der operator= nicht ueberladen werden ? war mir bis jetzt nicht bewusst oder wurde der globale operator= schon mal definiert ?
Es gibt keinen globalen operator=, der gehört immer zu einer Klasse.
-
muffmolch schrieb:
letztlich enthält er ein simples dynamisches array :-), da der std::vector hier zuviel overhead mitbringt und ich hier das speichermanagement selbst managen möchte. leider ist dies hie ruach nicht mit eigenen allocators möglich. lange rede kurzer sinn: MyVector hat nen Allocator, der im std-fall ein dynamisches array allokiert.
Es darf bezweifelt werden, dass deine MyVector-Klasse weniger Overhead mitbringt als std::vector*. Schliesslich haben da mehrere Koepfe ihr Gehirnschmalz reingesteckt, die vermutlich mehr Ahnung von C++ hatten als du. Welchen Overhead von std::vector kann man denn deiner Meinung nach vermeiden, ohne an anderer Stelle noch viel mehr Overhead zu erzeugen? std::vector besteht selbst aus nicht viel mehr als aus einem dynamischen Array. Wenn du nur auf eigenem Speichermanagement aufbauen willst, reicht es ziemlich wahrscheinlich, einen eigenen Allokator zu schreiben und den dem vector als Templateargument mitzugeben. Ich weiss, du schreibst dass das nicht moeglich ist. Aber evtl kann dir hier geholfen werden, eine Loesung fuer einen eigenen Allokator zu finden, bevor man dir hilft, das Rad neu zu erfinden

_______________
* das faengt schon damit an, dass du beim op= oben die Uebergabe by value statt by reference gemacht hast, was unnoetige Kopien des zu kopierenden Objekts erzeugt.
-
pumuckl schrieb:
Es darf bezweifelt werden, dass deine MyVector-Klasse weniger Overhead mitbringt als std::vector*. Schliesslich haben da mehrere Koepfe ihr Gehirnschmalz reingesteckt, die vermutlich mehr Ahnung von C++ hatten als du. Welchen Overhead von std::vector kann man denn deiner Meinung nach vermeiden, ohne an anderer Stelle noch viel mehr Overhead zu erzeugen? std::vector besteht selbst aus nicht viel mehr als aus einem dynamischen Array. Wenn du nur auf eigenem Speichermanagement aufbauen willst, reicht es ziemlich wahrscheinlich, einen eigenen Allokator zu schreiben und den dem vector als Templateargument mitzugeben. Ich weiss, du schreibst dass das nicht moeglich ist. Aber evtl kann dir hier geholfen werden, eine Loesung fuer einen eigenen Allokator zu finden, bevor man dir hilft, das Rad neu zu erfinden

Keine Sorge. Ich benutze zu 99% ausschlißelich den std::vector. Der ist wie der Rest der STL sein Geld wert und ebenso die zugehörigen Algorithmen.
Aber leider kann ich ihn an bestimmten Stelle in unserem Strömungssimulator nicht verwenden, denn dort benötige ich ihn in einer template Klasse acht Vetoren, die sich nur in ihrer Allokierung in beliebiger Kombination unterscheiden (einige sind "remote"-Vektoren, die via MPI zum Nachbarprozess gesendet werden). Da der Allocator eines std::vectors zur Compile-Zeit festehen muss und ich keine Lust habe für 8 Vektoren und deren Kobinationen die template Klasse zu basteln, haben wir hier eben einen CbVector eingeführt, der ebenfalls einen Allocator besitzt, allerdings kann dieser zur Laufzeit gewechselt werden und ist nicht Teil der Template Parameter Liste wie beim std::vector. Und da er wie gesagt nur in den Modulen vorkommt, ausser dem Index Operator, der resize und seize Methode keinerlei Funktionalität benötigt ist er mehr als schlank und eine gute Möglichkeit sich zusammen mit einem "Speicherpool" etwas Programmiergeschick anzueignen. Das mit dem Zuweisungsoperator war nur ne Frage in eigener Sache und das Bsp mit dem vector passte grad so schön
-
pumuckl schrieb:
_______________
* das faengt schon damit an, dass du beim op= oben die Uebergabe by value statt by reference gemacht hast, was unnoetige Kopien des zu kopierenden Objekts erzeugt.ja, aber nur weil ich es hie rin dem Beispiel vergessen habe... Im Code ist das & Zeichen brav vorhanden. Keine Sorge die CbVectoren sind gründlich validiert worden! Und unter MSVC8 und 9 mit cl sogar im release mode teils schneller im Zugriff als std::vectoren (wobei das für mich keinen Sinn macht...).
BTW kann ich das range-checking bei den std::vectoren auch im release mode aktivieren?
-
muffmolch schrieb:
kann ich das range-checking bei den std::vectoren auch im release mode aktivieren?
std::vector<>::at() ist immer range-checked (schmeisst ne exception wenn du ueber die range gehst)
Den Allokator zur Laufzeit zu wechseln stelle ich mir recht umstaendlich vor, denn damit du nicht mit dem neuen Allokator B Speicher deallokierst, den du vor dem Wechsel mit A allokiert hast, muesstest du beim Allokator-Wechsel mit dem neuen Allokator den kompletten vector neu allokieren, aus dem alten vector in den neuen ueberkopieren und danach den alten allokator entfernen. Wahlweise kann das Ganze natuerlich aufgeschoben werden bis Allokator B das erste mal benutzt wird, aber drum herumkommen wird man vermutlich nicht. Der Prformanceaufwand beim Wechsel des Allokators und beim kompletten Wechsel eines std::vectors tut sich also nichts, hinzu kommt der Aufwand, die komplette Vector-Klasse neu zu schreiben, waehrend die Wiederverwendung von std::vector mit unterschiedlichen Allokatoren nur ein oder zwei straight-forward templates braucht. Eine Skizze, wie man das designen koennte:
template<class T> class stdvecwrapbase { public: /*.pure virtuelle Memberfunktionen wie z.B. Zugriffsopertor etc..*/ }; template<class T, class Alloc> class stdvecwrap : public stdvecwrapbase<T> { std::vector<T,Alloc> v; /*Konstruktoren analog std::vector*/ /*.virtuelle Member von stdvecwrapbase werden mittels v implementiert.*/ } template<class T> class MyVector { private: stdvecwrapbase<T>* pvec; public: /* Member werden einfach an pvec weitergeleitet */ template<class Newalloc> void ChangeAlloc(Newalloc alloc) { stdvecwrapbase<T>* newvec = new stdvecwrap<T,Newalloc>(pvec->begin(),pvec->end(),alloc); swap(newvec,pvec); delete newvec; } }Wenn ich nix uebersehn hab ist der Rest straight forward.
-
stimmt. das wäre eine möglichkeit! aber der CbVector leistet nun schon zwei Jahre gute Diesnte und der CbVectorPool, den der MPIPoolConnector verwendet ebenfalls... aber in Zukunft werde ich darüber nachdenken, ob deine Lösung evtl hilfreich ist. Wie geasgt er wird ausschließlich in diesen Modulen für PODs verwendet (eigentlich dogar nur floats und doubles...)
leider ist es quasi unmöglich bei der verwendung von std::vectoren so das rangechecking im release zu aktiveren. bei uns muss hier lediglich eine päprozessor definition gesetzt werden. und nein, debug mode reichtuns nicht, dennso warte ich 2 min bis evtl der fehle rauftritt uns weißt es ist eine bereichsübertretung und im debug mode warte ich u.U. bis zu 20min... da ist der zeit/nutzen faktor doch erhebleich. zumal ich sicher nicht alle 1000 stellen im code von[] auf at wechsel

-
Gut, wenn die Klasse schon so lang im Gebrauch ist wirds natürlich schwer, sie mal eben zu ersetzen, zumal die ideale Modularisierung ja nie gegeben ist, wo man nur ein typedef ändern müsste.
Was das range-checking angeht: Hab ich das richtig verstanden, dass im bisherigen Code der Zugriffsoperator ohne Rangecheck verwendet worden ist, ohne dass der rufende Code sicherstellt dass die Range ok ist? Denn ein Wechsel zu at() wäre doch nur in dem Fall nötig. Falls dem tatsächlich so sein sollte, wäre es vielleicht angebracht, die Entwickler die das verbrochen haben, zum Nachsitzen antreten zu lassen, schließlich sind sie für die unsaubere und unsichere Software verantwortlich

Aber zurück zum ursprünglichen Thema:
muffmolch schrieb:
ja, das problem ist eben, dass ich mit der konvertierungsmethode jetzt zwar einen MyVector mit vec.stdvec() einen std::vector erhalte, meine andere templateklasse aber dann nichtmehr als Typ den std::vector verwenden, kann, der keine stdvec() methode implementiert...
Ich verstehe das so, dass du in deiner Templateklasse intern je nach Bedarf entweder einen std::vector oder einen deiner vectoren Marke Eigenbau verwendest, und sie unabhaengig davon in einen std::vector umwandeln moechtest. Vielleicht hilft da ein freies Umwandlungsfunktions-Template, das man mit einem gewissen Trick "partiell spezialisiert":
template <class TheVector> struct VecConverter; //allgemeine deklaration des Konvertertemplates //partielle spezialisierung des konverters für std::vectoren template <class T, class Alloc> struct VecConverter<std::vector<T,Alloc> > { typedef T value_type; static std::vector<T> convert(std::vector<T,Alloc> const& other) { std::vector<T> newvec(other.begin(),other.end()); return newvec; } }; //partielle spezialisierung für die Klasse MyVector mit stdvec() member template <class T> struct VecConverter<MyVector<T> > { typedef T value_type; static std::vector<T> convert(MyVector<T> const& other) { return other.stdvec(); } }; //============================================== //Funktionstemplate das den jeweiligen Konverter aufruft template<class TheVector> std::vector<typename VecConverter<TheVector>::value_type> ConvertToStdVec(TheVector const& other) { return VecConverter<TheVector>::Convert(other); } //=========================== //Anwendung class MyAlloc; class Foo; int main() { std::vector<Foo, MyAlloc> sv; MyVector<Foo> mv; std::vector<Foo> v1 = ConvertToStdVec(sv); std::vector<Foo> v2 = ConvertToStdVec(mv); }Das Funktionstemplate wird dadurch partiell spezialisiert dass es vollkommen auf einem klassentemplate beruht, das man ja problemlos partiell spezialisierne kann.
Wie das meiste von mir nur ein fixer Gedankengang der ohne Tests und Garantie schnell eingetippt wurde

-
template <class T, class A> vector<T> ConvertToStdVector (vector<T, A> const& v) { /* ... */ } template <class T> vector<T> ConvertToStdVector (MyVector<T> const& v) { /* ... */ } //=========================== //Anwendung class MyAlloc; class Foo; int main() { std::vector<Foo, MyAlloc> sv; MyVector<Foo> mv; std::vector<Foo> v1 = ConvertToStdVec(sv); std::vector<Foo> v2 = ConvertToStdVec(mv); }
-
danke, pumuckl. respekt, dass du dich wirklich in sowas reindenkst. das mit der modularisierung stimmt schon...
rangechecking ist bei uns an sich nicht erwünscht, wenn wir strömungeb berechnen, denn das kostet einen menge zeit. zum überpruefen, ob man in der implementierung die indices richtig setzt muss man das natürlich schon überprüfen, daher die präprozessor variable für solche zwecke. im debug ist es bei uns auch std-mäßig aktiv, kann aber auch dort deaktiviert werden.
diese freiheit ist explizit erwünscht.
der wrapper macht durchaus sinn und ich werde ihn vermutlich testen.gruß und dank,
sirann