Pointer auf Element in deque?



  • Hi

    Ich arbeite gerade an einer kleinen USB-HID-Lib.

    Folgende Zeilen bereiten mir allerdings Probleme:

    std::deque< char > *def_vec = getData();
    
    int vecSize = def_vec->size(); // Größe der zu schreibenden Daten
    
    int repLen = Capabilities.OutputReportByteLength - 1; // Größe eines Datenblocks für die Übertragung
    
    if( ( vecSize % repLen ) != 0 )// Falls nötig...
    {
    	// ...Größe anpassen
    	def_vec->resize( ( int( vecSize / repLen ) + 1 ) * repLen, 0 );
    }
    
    // Das erste Byte wird vom Treiber verarbeitet und nicht gesendet
    def_vec->push_front( 0 );
    
    for( unsigned int i = 0; i < ( def_vec->size() - 1 ); i += repLen )
    {
    	// Erstes Byte auf 0 setzen
    	(*def_vec)[i] = 0x00;
    
    	// Daten schreiben
    	bResult = WriteFile( hDevice,
    						 &(*def_vec)[i], // hier ist das Problem
    						 Capabilities.OutputReportByteLength,
    						 &numBytesReturned,
    						 &HidOverlapped
    					   );
    
    	bResult = WaitForSingleObject(ReportEvent, 200);
    }
    

    Das Problem ist dass der erste gesendete Block immer Müll ist.

    Ich habe jetzt herausgefunden dass das an der Zeile def_vec->push_front( 0 ); liegt. Wenn ich diese auskommentiere werden die Daten richtig gesendet.

    Wenn man sich den internen Aufbau eines std::deque veranschaulicht ist auch klar warum das fehl schlägt.
    Durch den Befehl push_front wird ja intern ein zweites Array angelegt welches an einer anderen Stelle im Speicher steht.

    Ein iterator wäre natürlich die Lösung des Problems. Die Frage ist wie ich den an WriteFile übergebe?

    Vielen Dank für alle Vorschläge

    mfg

    stein


  • Administrator

    Ja, eine std::deque garantiert nicht, dass intern das Array in einem Block vorhanden ist. Meistens wird es realisiert, indem man mehrere Blöcke hat. Und du wirst mit den Iteratoren sowas auch nicht an WriteFile übergeben können. Hier hilft nur ein std::vector Abhilfe.

    // Kopie erstellen.
    std::vector<char> dataToWrite(def_vec->begin(), def_vec->end());
    
    // ...
    
    // Daten schreiben
    bResult = WriteFile(hDevice,
                        &dataToWrite[i],
                        Capabilities.OutputReportByteLength,
                        &numBytesReturned,
                        &HidOverlapped);
    

    Oder statt der Kopie, arbeitest du gleich mit std::vector , wenn das möglich ist.

    Grüssli



  • Hab ich mir fast gedacht dass das nicht gehen wird.

    Hatte ursprünglich mit nem vector angefangen aber weil beim Senden halt immer dieses erste Byte vorgehängt und beim Lesen entsprechend gelöscht werden muss bin ich auf deque umgestiegen.

    Trotzdem vielen Dank

    mfg



  • Kannst du beim std::vector nicht push_back() verwenden (halt die Reihenfolge umkehren)?



  • Das würde nicht funktionieren da die Daten in der richtigen Reihenfolge übertragen werden müssen.

    Oder hab ich dich da falsch verstanden?

    mfg



  • deckprob schrieb:

    ...

    ...
    // Das erste Byte wird vom Treiber verarbeitet und nicht gesendet
    def_vec->push_front( 0 );
    ...
    

    Das verstehe ich nicht ... kann es sein, dass Du pop_front() meintest ?
    Bzw. warum willst Du Deinem Container eine 0 voranstellen?
    Willst Du das erste Byte löschen (=aus dem Container entfernen) oder auf den Wert 0 setzen oder ... ?

    Gruß,

    Simon2.



  • Ich muss ein Byte am Anfang einfügen und dieses auf 0 setzen.

    Um Daten über WriteFile an mein USB-Gerät zu senden muss das erste Byte jedes Blocks(Reports) 0 sein.

    In dem Beispielcode aus dem ich meinen Code erarbeitet habe stand dazu, dass das erste Byte vom Treiber verarbeitet wird und 0 sein muss.
    Wenn ich das richtig verstanden habe ist dieses Byte die ReportID.

    Ab dem zweiten Block setze ich dazu einfch das entsprechende Byte im Puffer auf 0:

    (*def_vec)[i] = 0x00;
    

    Beim ersten Block würde ich damit ja aber die zu sendenden Daten verändern.

    Also muss ich am Anfang ein Byte einfügen, was ich über

    deque::push_front( wert_ist_eigentlich egal )
    

    machen wollte und jetzt halt über

    vector::insert(vector::begin(), wert_ist_egal )
    

    mache.

    Konnte ich etwas Licht ins Dunkel bringen? 🙂



  • Man muss natürlich auch abwägen, wie wichtig einem die Performance ist und ob häufig einzelne Elemente am Anfang eingefügt werden. Je nachdem lohnt sich vielleicht eine std::deque oder std::list mehr. Wenn man dann das interne Array braucht, kann man immer noch einen std::vector von diesen konstruieren. Das mag zwar Zeit in Anspruch nehmen, aber man muss daran denken, dass das Verschieben aller Elemente im Container auch nicht gerade eine leichtgewichtige Operation ist. Wenn dann noch die gelegentliche Reallokation des std::vector s vorkommt, ist man damit sicher auf der besseren Seite.

    Etwas weiter hergeholt wären auch folgende Möglichkeiten (wahrscheinlich kannst du die nicht brauchen, aber um sie mal genannt zu haben):

    • Reverse-Iterator
    • Schreiben einer Wrapperklasse, die umgekehrten Zugriff gewährt
    • Implementierung eines eigenen Containers, der intern wie ein Array aufgebaut ist, aber am Anfang vorallokiert.


  • Etwas mehr Infos:
    Bei dem Projekt handelt es sich wie gesagt um eine kleine USB-HID-Bibliothek.

    Sie besteht bis jetzt aus 4 Klassen:

    USBBaseClass // abstrakt, definiert lediglich ein paar Funktionen die in USBThread und USBInterface gebraucht werden.
    
    USBInterface // erbt von USBBaseClass und stellt die Schnittstelle für den Programmierer da
    
    USBThread // erbt von USBBaseClass, stellt Thread-Objekt mit Sende/Empfangs-Funktionen bereit.
    
    USBData // Datenklasse die den threadsicheren Austausch von Daten zwischen USBInterface und USBThread sicherstellt.
    

    Der Programmierer übergibt seinem Objekt von USBInterface die Daten die er an das Gerät schicken möchte mit Hilfe der Funktion

    addWriteData( char * buffer, int length ) // vorläufig, später soll man auch Container übergeben können
    

    Diese reicht buffer an das interne USBData-Objekt weiter welches buffer in einen Container(vorher deque, jetzt vector) packt welcher wiederum in eine queue gepackt wird:

    std::queue< container< char > > dataToWrite;
    

    Der Thread guckt jetzt ständig nach ob es neue Daten zum schreiben gibt, falls ja holt er sich diese von dem USBData-Objekt und sendet sie mit der Funktion aus meinem ersten Post an das USB-Gerät.

    Ich hoffe soweit ist noch alles klar.

    Wie ich schon erwähnt habe muss das erste Byte des Arrays welches an WriteFile übergeben wird 0 sein damit alles funktioniert. Dieses Byte wird nicht an das Gerät gesendet sondern offensichtlich lediglich vom Geräte-Treiber verarbeitet (Report-ID).

    Nach den Kriterien von Herrn Josuttis habe ich mich für die Datenspeicherung zunächst für eine deque entschieden (es muss oft am Anfang ein Element gelöscht bzw. eingefügt werden), was aber zu den oben beschriebenen Problemen geführt hat.

    Zu den Themen Reverse-Iterator, Wrapperklasse, eigener Container:

    Wie ein Reverse-Iterator das Problem lösen könnte ist mir unklar.
    Da WriteFile einen blanken Pointer erwartet komme ich an dieser Stelle mit iteratoren nicht weiter und auch an einer anderen Stelle würde sowas keinen Sinn machen.

    Wrapperklasse: auch hier ist das Problem das WriteFile eben ein klassisches sequenzielles Array erwartet. Ich wüsste nicht wie man das untergraben könnte.

    Eigener Container:
    Wäre eine Möglichkeit aber wohl mit Kanonen auf Spatzen geschossen.

    Falls ich da etwas falsch verstanden oder nicht bedacht habe würde ich aber sehr gerne mehr zum jeweiligen Thema hören.

    Ich denke auch nicht dass sich diese Stelle als Flaschehals herausstellen könnte.
    Die STL ist meiner Erfahrung nach so enorm leistungsfähig dass sich erst bei sehr großen Datenmengen eine Verzögerung auch nur messen lassen sollte und bei solchen Datenmengen ist dann der USB-Bus (ja ich weiß, korrekt müsste es US-Bus heißt weil das B schon für Bus steht aber zum besseren Verständnis schreib ichs so :P) der Flaschenhals.

    Und ja, ich habe schon darüber nachgedacht einfach beim Speichern der Daten in den Container ein Byte vorzuhängen aber nach meinem Verständnis vom Schichtenmodell sollte die Datenhaltungsschicht keine noch so kleine Aufgabe der Verarbeitungsschicht übernehmen.

    Ich denke gerade darüber nach ob man nicht doch eine deque verwenden könnte wenn man den ersten zu sendenen Block umkopiert aber dazu muss ich erstmal ein paar Überlegungen anstellen, Bücher wälzen und mir die Innereien von deque anschauen.

    Ich bin auch weiterhin für jeglichen Input dankbar 😃

    mfg



  • deckprob schrieb:

    Ich muss ein Byte am Anfang einfügen und dieses auf 0 setzen....

    Ah! OK - danke. 🙂 👍

    (bitte nicht persönlich nehmen ... hier sind eine Menge Anfänger unterwegs (was ja auch vollkommen in Ordnung ist), die da noch nicht genau wissen, was sie tun).

    Gruß,

    Simon2.


Log in to reply