Untermenge der Klasse eines Arrays übergeben
-
CStoll schrieb:
Du kannst natürlich ein gleich großes Array von double-Werten anlegen und dort alle x()-Werte eintragen, bevor du es an die Unterfunktion übergibst. Oder (ist vermutlich einfacher) du übergibst der Funktion deinen vector<> und rufst dann in der Funktion nur die Methode x() auf (niemand ist gezwungen, ALLE Methoden eines gegebenen Objekts wirklich aufzurufen ;)).
Ok, diese Möglichkeiten waren mir klar.
Das Problem ist nur, dass die Arrays relativ groß sind und sehr häufig konvertiert werden müssten. Deshalb möchte ich ungerne ein extra Array jedesmal anlegen, oder die allgemeine Funktion, die nur double aktzeptiert komplett umschreiben.Kann ich aus deiner Antwort also schließen, dass es so wie ich das wollte nicht geht?
Matthias
-
pospiech schrieb:
CStoll schrieb:
Du kannst natürlich ein gleich großes Array von double-Werten anlegen und dort alle x()-Werte eintragen, bevor du es an die Unterfunktion übergibst. Oder (ist vermutlich einfacher) du übergibst der Funktion deinen vector<> und rufst dann in der Funktion nur die Methode x() auf (niemand ist gezwungen, ALLE Methoden eines gegebenen Objekts wirklich aufzurufen ;)).
Ok, diese Möglichkeiten waren mir klar.
Das Problem ist nur, dass die Arrays relativ groß sind und sehr häufig konvertiert werden müssten.Wenn das Array sehr groß wird (bzw. werden könnte), solltest du es als Referenz herumreichen. Dann müssen deine Funktionen aber auch in der Lage sein, einen vector<vector<MyClass> > entgegennehmen zu können.
Deshalb möchte ich ungerne ein extra Array jedesmal anlegen, oder die allgemeine Funktion, die nur double aktzeptiert komplett umschreiben.
Von was für einer allgemeinen Funktion reden wir denn hier? Soll die Auswertungsfunktion einen vector<double> übernehmen (dann bleibt dir nichts anderes übrig als entweder umzukopieren oder die Funktion anzupassen) oder verwendet sie intern eine Hilffunktion, die einen double erwartet (das ist kein Problem - übergib der Hilfsfunktion einfach das Ergebnis von (z.B.) the_vector[i][j].x())?
-
CStoll schrieb:
Von was für einer allgemeinen Funktion reden wir denn hier? Soll die Auswertungsfunktion einen vector<double> übernehmen (dann bleibt dir nichts anderes übrig als entweder umzukopieren oder die Funktion anzupassen)
Ja soll die Function. Wenn mir dann nichts anders übrigbleibt (was ja meine Frage war), dann werde ich die Auswertefunktion überladen müssen.
Das unschöne daran ist nur, dass ich dann gleichzeitig abragen muss ob .x oder .y ausgelesen werden soll - was den Quellcode wesentlich unschöner macht, als wenn ich von vorneherein nur .x Komponenten übergeben würde.
Matthias
-
Hi,
ich würde das nicht mittels "tell_me_what_to_do-Parameter" machen. Wenn der Aufrufer weiß, welches Attribut er auslesen will, kann er das besser über einen Funktionsnamen (oder meinetwegen einen member pointer) mitgeben.
Insgesamt scheint mir aber sowieso eine eigene Funktion für das Ganze ein wenig oversized .... evtl. würde ich mir dabei std::copy() zunutze machen (mit einem entsprechenden back_inserter sollte das gehen).
Gruß,
Simon2.
-
Simon2 schrieb:
Hi,
ich würde das nicht mittels "tell_me_what_to_do-Parameter" machen. Wenn der Aufrufer weiß, welches Attribut er auslesen will, kann er das besser über einen Funktionsnamen (oder meinetwegen einen member pointer) mitgeben.
Insgesamt scheint mir aber sowieso eine eigene Funktion für das Ganze ein wenig oversized .... evtl. würde ich mir dabei std::copy() zunutze machen (mit einem entsprechenden back_inserter sollte das gehen).
Aähh - ich verstehe nur Bahnhof. Kannst du das mit Quellcode etwas konkreter machen.
Was macht denn std::copy() ?
Was ich konkret mache:
Ich habe Daten einer Simulation die in einem Array gespeichert sind. Die Daten sind in einer Klasse organisiert, die unter anderem die Aufrufe .phase() und .amplitude() kennt.Diese möchte ich auf dem Bildschirm plotten. Dazu habe ich eine Funktion der ich nur ein double-vector-Array korrektor Größe übergeben muss und die Daten erscheinen auf dem Bildschirm.
Die Eigenschaften .phase() und .amplitude() sind double Werte eines Arrays korrekter Größe (1024x1024 Werte). Daher möchte ich gerne diese übergeben, damit ich Sie nicht vorher in ein extra Array kopieren muss, oder die Plotfunktion überladen muss, damit sie den einen oder anderen Wert ausliest.
Matthias
-
hi,
also wenn Deine "Plotfunktion" wirklich ein double[] erwartet, wirst Du ohne Kopieren nicht weiterkommen. Das liegt einfach schon an der Speicherrepräsentation. In einem Container (sei es nun Array oder std::vector, oder ...) für "MyClasses" (wäre hilfreich, wenn Du hier einfach mal den korrekten Namen angeben könntest) liegen eben komplette MyClasses (mit allen Attributen).
Wenn die API aber erwartet, dass im Speicher hintereinanderweg doubles liegen, passt das einfach nicht.
Mit std::vector kannst Du zwar hoffen, dass wenigstens die MyClasses hintereinanderliegen, aber da ist eben deutlich mehr hintereinander als nur die amplitude-Attribute.std::copy() macht genau das, was man erwartet: Kopiert aus einem Container in den nächsten - ist im Prinzip eine Vereinfachung und Verallgemeinerung; man spart sich eine Schleife und die konkrete jeweilige Einfügeoperation.
Ein Beispiel:vector<int> v; std::list<int> lInt; // List füllen lInt.push_back(1); lInt.push_back(2); std::copy( lInt.begin(), lInt.end(), std::back_inserter(v)); // wurde in vector kopiert. ..Ich schaue mal, ob ich da was Passendes basteln kann.
Gruß,
Simon2.
-
Plotfunktion
void CGraphCtrl::SetPlotData(vector<vector<double> > &InputArray) { if ((PixelNumber.x>0) && (PixelNumber.y > 0)) { for (int ix=0; ix < PixelNumber.x; ix++) { for (int iy=0; iy < PixelNumber.y; iy++) { PlotData[ix][iy] = GetColor(InputArray[ix][iy]); } } } }PlotData ist ein COLORREF Array. Das ganze ist Bestandteil einer überladenen CStatic Klasse namens CGraphCtrl.
Die Daten die hier Übergeben werden sollen sind in einem vector-Array der Klasse CComplex (stammt nicht von mir und ist nicht das gleiche wie Complex) gespeichert. Diese hat die Funktionen .phase() und .amplitude().
Matthias
-
hi,
tja, da bleibt Dir wirklich nichts als Kopieren übrig. copy() würde ich bei näherer Betrachtung nicht nehmen, sondern transform():
struct CComplex { CComplex(double a, double b) : x(a), y(b) {} double x; double y; }; struct extractX { double operator()(CComplex const& m) { return m.x; } }; int main() { extractX e; std::list<CComplex> lInt; // 'list' nur, um mal zu zeigen, dass es geht // geht natürlich auch mit 'vector' lInt.push_back(CComplex(1.0, 10.0)); lInt.push_back(CComplex(2.0, 20.0)); lInt.push_back(CComplex(3.0, 30.0)); std::vector<double> v(lInt.size()); std::transform( lInt.begin(), lInt.end(), v.begin(), e); return 0; }Noch ein wenig aufpeppen könnte man das mit Memberpointern (leider recht häßliche Syntax) und evtl. mit einem back_inserter (dann braucht man nicht vorher die Kapazität des vectors zu bestimmen), aber es funktioniert auch so.
NACHTRAG: So - ich hab's mal reingebastelt:
struct CComplex { CComplex(double a, double b) : x(a), y(b) {} double x; double y; }; typedef double CComplex::*CComplexMember; struct extract { CComplexMember member; extract(CComplexMember mem) : member(mem) {} double operator()(CComplex const& m) { return m.*member; } }; int my_back_inserter_main() { extract ex(&CComplex::x); // Hier kann man beliebige double-Attribute von CComplex hinterlegen extract ey(&CComplex::y); std::list<CComplex> lInt; lInt.push_back(CComplex(1.0, 10.0)); lInt.push_back(CComplex(2.0, 20.0)); lInt.push_back(CComplex(3.0, 30.0)); vector<double> vx; // Keine GrößenAngabe mehr notwendig vector<double> vy; std::transform( lInt.begin(), lInt.end(), back_inserter(vx), ex); std::transform( lInt.begin(), lInt.end(), back_inserter(vy), ey); // Ausgabe von vectoren cout << "vx: "; copy(vx.begin(), vx.end(), std::ostream_iterator<double>(cout, " ") ); cout << "\nvy: "; copy(vy.begin(), vy.end(), std::ostream_iterator<double>(cout, " ") ); return 0; }OK, vielleicht ein wenig oversized, aber vielleicht ein wenig interessant.
Gruß,
Simon2.
-
Simon2 schrieb:
Noch ein wenig aufpeppen könnte man das mit Memberpointern (leider recht häßliche Syntax) und evtl. mit einem back_inserter (dann braucht man nicht vorher die Kapazität des vectors zu bestimmen), aber es funktioniert auch so.
Vielen Dank für deine Mühe! Leider sind meine C++ Kenntnisse nicht wirklich ausreichend um den Code zu verstehen... Ich schreibe mal dazu was ich davon denke verstanden zu haben:
struct CComplex { CComplex(double a, double b) : x(a), y(b) {} double x; double y; };Hier definierst du quasi die Klasse, die ich ja schon habe.
typedef double CComplex::*CComplexMember;Hier wird CComplexMember zu einem Zeiger auf einen double Member von CComplex ?
struct extract { CComplexMember member; extract(CComplexMember mem) : member(mem) {} double operator()(CComplex const& m) { return m.*member; } };Verstehe ich nicht wirklich, aber scheint einen Member von CComplex zurückzuliefern.
int my_back_inserter_main() { extract ex(&CComplex::x); // Hier kann man beliebige double-Attribute von CComplex hinterlegen extract ey(&CComplex::y);Hier mache ich ex und ey zu Variablen die auf CComplex::x zeigen, ohne das eine Klasse zu einer konkreten Instanz gemacht wurde ?
std::list<CComplex> lInt; lInt.push_back(CComplex(1.0, 10.0)); lInt.push_back(CComplex(2.0, 20.0)); lInt.push_back(CComplex(3.0, 30.0));Hier füllst du lInt mit Variablen und nimmst list statt vector (hat das einen speziellen Grund?)
vector<double> vx; // Keine GrößenAngabe mehr notwendig vector<double> vy; std::transform( lInt.begin(), lInt.end(), back_inserter(vx), ex); std::transform( lInt.begin(), lInt.end(), back_inserter(vy), ey);Da ich keine Dokumentation zu transform habe rate ich einfach mal:
Du gibst das Beginn und das Ende deines Arrays an (was bei mir zweidimensional wäre, bleibt der Code dann derselbe?), gibst die Variable vx (vy) an in die die Ergebnisse geschrieben werden, und mit ex den Teil von lInt der kopiert werden soll.Gruß,
Matthias
-
Hi pospiech,
also Du hast das eigentlich schon alles richtig verstanden !

Nur noch kleine Anmerkungen:
pospiech schrieb:
...
typedef double CComplex::*CComplexMember;Hier wird CComplexMember zu einem Zeiger auf einen double Member von CComplex ?...
Genauergesagt: Ich definiere mir einen Typen namens CComplexMember für alle double-Member von CComplex - ist einfach eine Schreiberleichterung; sonst müsste ich überall das "
double CComplex::*" mitschleppen, wo ich jetzt einfachCComplexMemberschreiben kann.pospiech schrieb:
...
struct extract { CComplexMember member; extract(CComplexMember mem) : member(mem) {} double operator()(CComplex const& m) { return m.*member; } };Verstehe ich nicht wirklich, aber scheint einen Member von CComplex zurückzuliefern....
Japp ! Ist ein "Funktor", also eine Klasse, die eigentlich nur einen Funktionsaufruf kapselt. Dafür verwendet man in C++ gerne den "Funktionsoperator" operator()(), der besagt, dass man Objekte dieser Klasse wie eine Funktion aufrufen kann. In diesem Fall z.B.
CComplex myComplex; extract ex(&CComplex::x); double my_x = ex(myComplex); // ist einfach ein FunktionsaufrufDass ich hier Memberpointer verwende, ist eine "C++-Schmackazie", die für den vorliegenden Fall eigentlich übertrieben ist, weil ich einfach mal zeigen wollte, wie viel Flexibilität die Sprache so mitbringt. Stattdessen hätten es ein Funktor oder eine konkrete Funktion, die zu einem CComplex direkt das x-Attribut zurückliefern, auch getan.
pospiech schrieb:
...
double operator()(CComplex const& m) { return m.*member; }member ist ein Zeiger auf ein double-Attribut von CComplex (wie Du schon sagst: "ohne konkretes Objekt"). Das Konstrukt "
m.*member" ist einfach eine "Dereferenzierung" analog zu:int a, b; int* p = &a; cout << *p; p = &b; cout << *p;Der wesentliche Unterschied ist, dass man sich in einem Memberpointer zwar "merken kann", welches Attribute (x oder y) man verwenden will, aber nicht, von welchem konkreten Objekt man diese haben möchte. Deswegen braucht man zur Auflösung nicht nur den Pointer, sondern auch ein konkretes Objekt (im obigen Beispiel das übergebene "
m".pospiech schrieb:
...
std::list<CComplex> lInt; lInt.push_back(CComplex(1.0, 10.0)); lInt.push_back(CComplex(2.0, 20.0)); lInt.push_back(CComplex(3.0, 30.0));Hier füllst du lInt mit Variablen und nimmst list statt vector (hat das einen speziellen Grund?)
Eigentlich ist der Grund nur "Weil ich es kann !"
Ich wollte klarmachen, dass dieses Konzept nicht von den konkreten Containern abhängt.pospiech schrieb:
...
Da ich keine Dokumentation zu transform habe rate ich einfach mal:
Du gibst das Beginn und das Ende deines Arrays an (was bei mir zweidimensional wäre, bleibt der Code dann derselbe?), gibst die Variable vx (vy) an in die die Ergebnisse geschrieben werden, und mit ex den Teil von lInt der kopiert werden soll.
...Korrekt ! Glückwunsch !
transform() macht eigentlich nichts anderes, als// Liste durchgehen und for(std::list<CComplex>::const_iterator it = lInt.begin(); it != lInt.end(); ++it) { back_inserter(vx) = ex(*it); // ... für jedes Element ex aufrufen und Ergebnis in back_inserter schreiben }Doku zu so etwas findest Du online z.B. http://www.cppreference.com/cppalgorithm/transform.html
@"2-Dim Arrays": Eigentlich gibt es sowas ja gar nicht in C++; es gibt nur "Container von Containern" oder "intelligentes Springen in sequentiellen Daten".
Prinzipiell geht das Konzept auch mit solchen 2D-Konstrukten, aber man muss ein wenig mehr herumschrauben. Z.B. bekommt extract dann kein CComplex-Objekt mehr, sondern einen vector<CComplex>, mit dem er dann auch irgendetwas machen (vermutlich wieder transform mit einem geeigneten Funktor aufrufen) muss.
Für Konkreteres braucht man aber die genaue Spezifikation vom "2D-Konstrukt".Gruß,
Simon2.