Objekte in deque packen - ich steh auf dem Schlauch....



  • @xenayoo
    STL Container regeln das mit ihrer Grösse dynamisch.
    Bei std::vector kann man mittels reserve() sicherstellen dass er genug Speicher für N Elemente hat, und daher bei bis zu N Elementen nicht umkopiert werden muss.
    Bei std::deque ist das nicht möglich.



  • da fehlt trotzdem dein StdKonstruktor

    schreibe in deine class noch dies rein

    MeineKlasse():
                m_a(0.0f),
                m_b(0.0f),
                m_c(0.0f),
                m_d(0.0f),
                m_e(0.0f)
    {
    
    }
    

  • Mod

    derStudi schrieb:

    da fehlt trotzdem dein StdKonstruktor

    Man muss keinen Standardkonstruktor haben und oft gibt es gute Gründe dagegen.



  • Hallo,

    vielen Dank für die Antworten.
    @Ich bin Matrix:
    Hier mal ein Vergleich (http://de.wikibooks.org/wiki/C++-Programmierung/_Die_STL/_Container)

    Demnach kommt vector nicht in frage, weil er kein push_front() kann, list beherrscht operator[] nicht. Zudem begegnete mir auf verschiedenen Seiten die Angabe, dass deque schneller sei als list.
    Für meine Anwendung kommt es auf die Reihenfolge der Daten an. Deshalb setze ich deque mit push_front(), pop_back() und Operator[] ein.

    Eine std::deque<meineKlasse> der Länge 0 anlegen und später mit z.B. push_back Elemente anfügen.

    Ich werde es mal ausprobieren. Jedoch ist auch die Größe wichtig - sonst macht push_front und pop_back keinen Sinn. Eventuell kann ich das später mit resize nachholen.

    @derStudi:
    Da ich die Klasse testhalber in einer eigenen .exe entwickelt habe und es keinerlei Beschwerden seitens des Kompilers gab, nehme ich mal an, dass das so in Ordnung ist. Die Verwendung eines solchen Standardkonstruktors erzeugt ein Objekt mit Fehlldaten. Die Klasse ist so beschaffen, dass sie Daten aufnimmt und diese später nicht geändert werden - lediglich wieder ausgelesen. Verwende ich einen solchen Leerkonstruktor, muss ich zusätzlichen Aufwand betreiben, um sicher zu stellen, dass mir diese Daten nicht meine Berechnungen verhageln....
    BTW: Das letze Element ist ein unsigned int und kein double.

    @all: ich probier's jetzt aus und melde mich.... Danke

    Jens 🙂


  • Mod

    xenayoo schrieb:

    Eine std::deque<meineKlasse> der Länge 0 anlegen und später mit z.B. push_back Elemente anfügen.

    Ich werde es mal ausprobieren. Jedoch ist auch die Größe wichtig - sonst macht push_front und pop_back keinen Sinn. Eventuell kann ich das später mit resize nachholen.

    Nein, kannst du nicht. Das ist genau das gleiche Problem. Wieso spielt hier die Größe eine Rolle? Was willst du überhaupt erreichen? Das klingt alles sehr wirr, du scheinst dir nicht so richtig im klaren zu sein, was ein Container speichert.



  • Hallo,

    mein Statusbericht:

    -gute Nachricht: Die ganze Fehlerliste, die auch Fehler in den icludierten dateien wie windows.h meldete, sind jetzt weg.

    -schlechte Nachricht: Es ist ein neuer Fehler da:

    [Error] request for member 'resize' in 'ContainerName', which is of non-class type 'std::deque<meineKlasse>()'
    

    Jens


  • Mod

    Da hast du wohl irgendwas wirres gemacht mit einem typedef. Wie denkst du, soll man dir ohne Code bei solch einer Frage helfen können? Bitte lies dir mal den ersten und den dritten Link in meiner Signatur durch. In diesem Thread wurde sehr viel aneinander vorbei geredet und die Helfer mussten sehr oft im Kaffeesatz lesen, was die eigentliche (nicht die unmittelbare) Problemstellung ist.



  • xenayoo schrieb:

    -schlechte Nachricht: Es ist ein neuer Fehler da:

    [Error] request for member 'resize' in 'ContainerName', which is of non-class type 'std::deque<meineKlasse>()'
    

    Noch schlechtere Nachricht: ohne deinen jetztigen Code kann man nur raten, was da passiert. Ich rate mal: du hattest ein Objekt "ContainerName" vom Typ deque, das du mit einem Konstruktorargument initialisiert hast:

    std::deque<meineKlasse> ContainerName(bla);
    

    Jetzt willst du den Default-Ctor aufrufen und hast deshalb einfach das Argument gelöscht, die Klammern aber gelassen:

    std::deque<meineKlasse> ContainerName();
    

    Was du nicht bemerkt hast ist, dass das jetzt keine Objektdefinition mehr ist, sondern die Deklaration einer Funktion namens ContainerName, die keine Argumente nimmt und eine deque zurückgibt. Wenn du jetzt ContainerName.resize aufrufst, denkt der Compiler, dass du resize auf der deklarierten Funktion aufrufst, was natürlich Blödsinn ist. Lösung: nimm die beiden Klammern weg, damit aus der Funktion wieder ein Objekt wird.



  • Sepp schrieb:

    Nein, kannst du nicht. Das ist genau das gleiche Problem. Wieso spielt hier die Größe eine Rolle? Was willst du überhaupt erreichen? Das klingt alles sehr wirr, du scheinst dir nicht so richtig im klaren zu sein, was ein Container speichert.

    Ich benötige einen sequentiellen Puffer, bei dem neue Daten oben rein kommen und alte daten unten rausfliegen. Dieser Puffer soll in seiner größe variabel sein - allerdings nur beim Aufruf. Wenn er eingerichtet ist, ändert sich seine Größe nicht mehr. Das Ganze soll aus einem C#-Programm genutzt werden.
    Hätte ich das Ganze in eine Klasse packen und diese einfach aus C# heraus direkt nutzen können, wäre dies kein Problem gewesen. Jedoch ist dies - aus welchem Grund auch immer (Absicht seitens MS, Kurzsichtigkeit der Programmierer oder tatsächlich technisch bedingt) nicht möglich. So kann ich entweder eine sogenannte Wrapper-dll erstellen oder den Wrapper gleich von vorne herein einbauen. Letzteres tue ich, indem aus den Memberfunktionen einfach extern"C"-Funktionen werden. Dann habe ich allerdings keinen Konstruktor, was eine der Ursachen für meine Probleme ist.

    Jens



  • xenayoo schrieb:

    Dieser Puffer soll in seiner größe variabel sein - allerdings nur beim Aufruf. Wenn er eingerichtet ist, ändert sich seine Größe nicht mehr.

    Die Größe muss sich ändern, sobald du etwas reinsteckst oder entnimmst. Meinst du vielleicht die maximale Größe?



  • xenayoo schrieb:

    Wenn er eingerichtet ist, ändert sich seine Größe nicht mehr.

    Du willst doch pop_... und push_... benutzen, wie Du schriebst? Selbstverständlich ändert sich dabei die Größe des Containers!


  • Mod

    Um die Antworten von MFK und Belli etwas zu erläutern:

    Wenn du ein Element aus deinem Puffer holst, die Größe aber konstant bleiben soll, was ist dann das neue Element im Puffer? Kannst du diese Frage beantworten? Das ist es, was dein Compiler von dir wissen möchte, ebenso ich, Belli und MFK. Ebenso: Wenn du ein Element in den Puffer packen willst, die Größe aber konstant bleiben soll, welches Element fliegt dann raus und wohin?



  • Konstante Größe ließe sich nur erreichen, wenn Du den Container mit einer Anzahl Elementen initialisierst (Default-Konstruktor erforderlich) und in der Folge nur noch mit dem [] - Operator darauf zugreifst, um auf Elemente zuzugreifen oder ihnen neue Werte zuzuweisen. Aber dann könntest Du viel besser einen Vector nehmen!



  • Hallo,

    der Puffer soll eine Maximalgröße haben. diesen hätte ich - wenn ich das Ganze direkt als Klasse hätte nach C# exportieren können (ohne Verwendung von Visual Studio und Berücksichtigung von Versionen) direkt den Konstruktor aufgerufen und die notwendigen Werte mit übergeben. Da dies aber nicht so geht,verwende ich extern"C"-Funktionen. Damit bin ich unabhängig von Kompiler und Version. Gleichzeitig habe ich auch keinen Konstruktor mehr.

    Deshalb wird der deque-Puffer global initialisiert - mit festgesetzter Maximalgröße. Wenn ich deque in der initialisierungs-Funktion einrichte, habe ich auf der einen Seite zwar alle Parameter zur Verfügung, aber deque hört auf zu existieren, sobald die Funktion verlassen wird.

    Die Maximalgröße wird beim ersten Aufruf festgelegt. Neue Elemente werden per push_front() eingefügt und alte Elemente (wenn die maximalgröße erreicht ist) per pop_back entfernt. Diese werden dann nicht mehr gebraucht. Für die Berechnung wird dann per []-Operator auf die Elemente zugegriffen. Genau so soll es sein.

    In C# kann ich den Klassenname direkt als Typ verwenden, um ein Feld oder was auch immer zu deklarieren. In C++ scheint dies nicht zu funktionieren - obwohl meine Klasse nur aus 4 doubble und 1 unsigned int besteht. Scheinbar benötigt deque eine fertige Instanz von meineKlasse, damit der Speicher entsprechend reserviert werden kann.

    Ich habe das rezise jetzt aus der Funktion herausgenommen und werde es durchführen, wenn die ersten daten in ein Objekt von meineKlasse gepackt wurden.
    Warum einfach, wenn es auch kompliziert geht.

    Jens



  • Bis auf die Tatsache, dass in C++ keine Referenzen/Pointer, sondern Objekte gespeichert werden, verhält sich der Container nicht anders als in C#. Dort werden halt bei der Reservierung von 10 Plätzen in der Queue 10 nulls abgelegt. Und die musst Du auch abfangen. Um das in C++ nachzubilden, könntest Du Deinem Objekt einen "null-Zustand" geben, oder aber wiederum Zeiger abspeichern (was ich aber beides nicht empfehlen würde).

    Aber so kompliziert ist das jetzt auch nicht 😉

    std::deque<MeinObjekt> queue; // Keine Reservierung, kein Resize
    
    if (queue.size() >= MAXIMUM_CAPACITY)
        queue.pop_back();
    queue.push_front(neues_element);
    
    for (int i = 0; i < queue.size(); ++i)
        ... queue[i] ...
    

    In C# kann ich den Klassenname direkt als Typ verwenden, um ein Feld oder was auch immer zu deklarieren. In C++ scheint dies nicht zu funktionieren - obwohl meine Klasse nur aus 4 doubble und 1 unsigned int besteht. Scheinbar benötigt deque eine fertige Instanz von meineKlasse, damit der Speicher entsprechend reserviert werden kann.

    Doch, in C++ funktioniert dies genauso. Nur, dass Du in C++ ein Feld von Objekten anlegst (welche natürlich einen gültigen Zustand haben müssen - Defaultkonstruktor) und in C# ein Feld von Referenzen (welche natürlich einen gültigen Zustand haben müssen - null).



  • xenayoo schrieb:

    der Puffer soll eine Maximalgröße haben.

    Maximalgröße != Größe

    xenayoo schrieb:

    diesen hätte ich - wenn ich das Ganze direkt als Klasse hätte nach C# exportieren können (ohne Verwendung von Visual Studio und Berücksichtigung von Versionen) direkt den Konstruktor aufgerufen und die notwendigen Werte mit übergeben. Da dies aber nicht so geht,verwende ich extern"C"-Funktionen. Damit bin ich unabhängig von Kompiler und Version. Gleichzeitig habe ich auch keinen Konstruktor mehr.

    Deshalb wird der deque-Puffer global initialisiert - mit festgesetzter Maximalgröße. Wenn ich deque in der initialisierungs-Funktion einrichte, habe ich auf der einen Seite zwar alle Parameter zur Verfügung, aber deque hört auf zu existieren, sobald die Funktion verlassen wird.

    Was denn nun? Ist die die deque global oder funktionslokal?!

    xenayoo schrieb:

    Die Maximalgröße wird beim ersten Aufruf festgelegt.

    Aufruf von was?

    xenayoo schrieb:

    Neue Elemente werden per push_front() eingefügt und alte Elemente (wenn die maximalgröße erreicht ist) per pop_back entfernt. Diese werden dann nicht mehr gebraucht. Für die Berechnung wird dann per []-Operator auf die Elemente zugegriffen. Genau so soll es sein.

    Schön und gut. Warum meinst du jetzt, irgendwie irgendwo eine Größe festlegen zu müssen?

    xenayoo schrieb:

    In C# kann ich den Klassenname direkt als Typ verwenden, um ein Feld oder was auch immer zu deklarieren. In C++ scheint dies nicht zu funktionieren - obwohl meine Klasse nur aus 4 doubble und 1 unsigned int besteht.

    Doch, das funktioniert auch. Du bist aber wahrscheinlich in die most-vexing-parse Falle reingelaufen. Zumindest verstehe ich die Fehlermeldung so. Wissen können wir das jetzt nicht, weil du deinen Code nicht zeigst.

    xenayoo schrieb:

    Scheinbar benötigt deque eine fertige Instanz von meineKlasse, damit der Speicher entsprechend reserviert werden kann.

    Nein. deque benötigt eine fertige Instanz, um Objekte zu erzeugen. Deine deque soll doch aber am Anfang sicherlich leer sein. Also spar die das mit der Größenangabe!

    xenayoo schrieb:

    Ich habe das rezise jetzt aus der Funktion herausgenommen und werde es durchführen, wenn die ersten daten in ein Objekt von meineKlasse gepackt wurden.
    Warum einfach, wenn es auch kompliziert geht.

    🙄



  • Du verstehst offensichtlich die Funktionsweise der Container nicht. Wenn Du

    deque<meineKlasse> deq(12);
    

    machst, dann enthält die deque bereits 12 Objekte (wenn Deine Klasse denn einen Def.-Konstr. hat). Wenn Du nun push_front aufrufst, dann hat sie bereits 13 Objekte.
    Um das, was ich nun verstanden zu haben glaube, umzusetzen, solltest Du Dir eine Klasse schreiben, die eine (zunächst) leere deque<meineKlasse> sowie ein Attribut maxGroesse beinhaltet.
    Dann kannst Du mit push_... Elemente in Deine deque bringen und jederzeit prüfen, ob maxGroesse erreicht ist, und dann nach Wunsch mit der deque verfahren.



  • Hallo Krümelkacker,

    das Ganze befindet sich in einer dll, die von einem c# aufgerufen und mit Parametern beschickt. Wenn ich eine Klasse einfach so exportieren könnte, würde ich zum einmaligen intialisieren einfach den Konstrucktor aufrufen. Um jedoch künftige Probleme zu vermeiden, habe ich extern"C" -Funktionen eingesetzt. Diese sind dann unabhängig von der Frameworkversion nutzbar.
    Statt also nun verschiedene Memberfunktionen einer Klasse zu verwenden, verwende ich mehrere extern"C" -Funktionen. Somit wird die deque global innerhalb der dll initialisiert, damit sie in den verschiedenen extern"C"-Funktionen verfügbar ist. Ich mache quasi die ganze dll zur Klasse, die beim laden instanziert wird.
    Ergo: die deque wird innerhalb der dll GLOBAL initialisiert.

    Da ich nun also die ganze dll quasi wie eine Klasse behandele, wird sie beim ersten Aufruf geladen. Soweit ich das nun verstehe, passiert dann folgendes (unter Vorraussetzung, dass ich die dll unter C# richtig anspreche): Bei dem ersten Aufruf einer Funktion dieser dll wird diese geladen und globale Elemente initialisiert.

    @Belli: Ja. Die meisten Dokumente, die ich gelesen habe, waren quasi Kombibeschreibungen der Container. Damit schlich sich bei mir der Fehler ein, dass es bei deque einmal die Funktion capacity() gibt, die das Maximale Fassungsvermögen der Deque angibt - also dass, wass ich mit maxDeq definiert habe und die Funktion size(), die angibt, wieviele Elemente belegt sind. Capacity gibt es jedoch nur bei vector. Also werde ich auf die Größendefinition verzichten...

    Danke an alle....

    Jens

    P.S.: Falls ich eine dll wie eine Klasse aufrufen kann - also etwa LoadLibrary(parameter 1,.....) aus c# heraus und diese Parameter dann nutzen kann..... Dann her mit dem Wissen... 😉



  • P.S.: Falls ich eine dll wie eine Klasse aufrufen kann - also etwa LoadLibrary(parameter 1,.....) aus c# heraus und diese Parameter dann nutzen kann..... Dann her mit dem Wissen...

    Solche Probleme kann man sehr gut mit swig ( www.swig.org ) lösen.
    Das Teil generiert dir über deine Klassen Wrapper die du über C# ansprechen kannst.
    Einfache mal anschauen, eventuell ist das was für dich.



  • xenayoo schrieb:

    Damit schlich sich bei mir der Fehler ein, dass es bei deque einmal die Funktion capacity() gibt, die das Maximale Fassungsvermögen der Deque angibt - also dass, wass ich mit maxDeq definiert habe und die Funktion size(), die angibt, wieviele Elemente belegt sind. Capacity gibt es jedoch nur bei vector.

    Auch bei Vektor hat der Rückgabewert von capacity() nicht unmittelbar mit dem Wert max zu tun, wenn Du

    vector<klasse> vec(max);
    

    machst. Im Gegenteil, wenn Du nun

    vec.size();
    

    aufrufst, dann bekommst Du max zurück. Auch hier gilt, Deine klasse muss einen Def.Konstr. haben, weil beim Initialisieren des Vectors sofort max Objekte vom Typ klasse im Vektor gespeichert werden.


Anmelden zum Antworten