Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

std::vector + "Spass" mit Pointern

25 views
Skip to first unread message

Robert Hartmann

unread,
Nov 26, 2011, 4:39:58 PM11/26/11
to
Hallo zusammen,

Durch einen Aufruf einer System-API (Sprache C) wird ein
Buffer gefüllt, d.h. es wird ein C struct (variabler Größe) gefüllt.

Das sollte für C++ im Normalfall kein Problem sein.

Prinzip so etwa:

size_t buffer_size = sizeof(TollerTyp);
PointerTollerTyp buffer = (PointerTollerTyp) new char[buffer_size];

int i = Systemaufruf(buffer, &buffer_size);
// i==0 bei Puffer-Fehler!
// i==NOMORE wenn Systemaufruf keinen nachfolgenden gleichen mehr zulässt
while( i!= NOMORE) {
while (!(i)){

/*
Größe des Puffers war zu klein.
Notwendige Größe steht jetzt aber in buffer_size drin.
Ist vom Systemaufruf verändert worden.
*/
delete[] buffer;
buffer = (PointerTollerTyp) new char[buffer_size];
i = Systemaufruf(buffer, &buffer_size);
}
i = Systemaufruf(buffer, &buffer_size);
}
//keine weiteren Aufrufe von Systemaufruf mehr zulässig.

Soweit so schön. Nun zu meinem Pointer-Problem:

Ich würde gerne die jeweiligen Puffer an einen std::vector<TollerTyp>
mit Namen Puffersammlung anhängen.

also Puffersammlung.push_back(*buffer)

Leider besitzt TollerTyp, dessen Definition ich nicht verändern kann,
selber Pointer, die in den "eigenen" Speicherbereich verweisen, d.h.
die Pointer-Ziele verbleiben innerhalb der Puffergrenzen.

Will heißen, das "Ungetüm" sieht im Prinzip so aus:

struct Tollertyp{
size_t StructSize;
char* NullTerminatedCString1;
//The string occupies memory area at the end of this structure.
char* NullTerminatedCString2;
//The string occupies memory area at the end of this structure.

....

}

Es reicht also nicht aus den Buffer wie er ist, mit push_back in den
std::vector zu transferieren, denn das push_back verändert natürlich
nicht den Wert in NullTerminatedCString1 bzw NullTerminatedCString2.
Was aber geschehen müsste, damit der String immer noch gefunden werden
kann. Spätestens jedoch werden die Ziele von NullTerminatedCString1
ungültig sein, wenn die Puffersammlung als Funktionsrückgabe benutzt
werden soll.

Auch bei einer std::vector internen Speicherveränderung, sei es
durch löschen oder über vector capacity hinausgehendes einfügen,
müssten die Pointer der Struktur aktualisiert werden ...

Ich grübele schon einige Zeit herum, aber eine passende Zeigerarithmetik
fällt mir nicht ein.

Es wäre sehr nett, wenn mir jemand das Bett vor dem Kopf etwas
durchlöchert.

Beste Grüße,
Robert Hartmann

P.S. Für diejenigen, die den konkreten Datentyp und die aufgerufene
Funktion wissen wollen:

Der TolleTyp ist INTERNET_CACHE_ENTRY_INFO
und Systemaufruf steht für FindFirstUrlCacheEntry bzw dann
FindNextUrlCacheEntry jeweils wininet.dll unter Windows:
http://msdn.microsoft.com/en-us/library/windows/desktop/aa385134
http://graphcomp.com/info/specs/ms/inetr008.htm
http://msdn.microsoft.com/en-us/library/windows/desktop/aa384049

P.P.S Jedoch das konkrete Problem der "Pointer-Anpassung"
ist nicht auf ein Betriebssystem oder API beschränkt und
somit in dieser NewsGroup nicht "off Topic".

Daniel Krügler

unread,
Nov 27, 2011, 10:33:40 AM11/27/11
to
Nun, es ist soweit klar, dass Tollertyp ein nicht-kopierbarer
Typ ist, mir ist daher unklar, warum du ihn dann kopieren
willst?

Wenn dir eine Container-artige Verwaltung wichtig ist,
solltest du dir am besten einen C++-Wrapper-Typen schreiben,
der jeweils ein konstruiertes Objekt von Tollertyp einfach
nur referenziert.

Mehrere Möglichkeiten fallen da ein:

a) Du nimmst einfach std::vector<TollerTyp*>.
b) Du nimmst std::vector<boost::shared_pointer<TollerTyp>

Ansatz (a) ist trivial, schränkt allerdings den Lebenszyklus des
Containers
auf die Gültigkeit der Pointer ein

Ansatz (b) hat den Vorteil, dass du unterschiedliche Lebensdauern
von TollerTyp abbilden kannst, da du eine eigene Löschfunktion
angeben kannst beim Erzeugen dieses SmartPointers.

> Auch bei einer std::vector internen Speicherveränderung, sei es
> durch löschen oder über vector capacity hinausgehendes einfügen,
> müssten die Pointer der Struktur aktualisiert werden ...
>
> Ich grübele schon einige Zeit herum, aber eine passende Zeigerarithmetik
> fällt mir nicht ein.
>
> Es wäre sehr nett, wenn mir jemand das Bett vor dem Kopf etwas
> durchlöchert.

Vielleicht helfen ja obige Anregungen ein wenig.

Besten Gruss aus Bremen,

- Daniel Krügler

Robert Hartmann

unread,
Nov 27, 2011, 2:53:20 PM11/27/11
to
Der konkrete Datentyp der wininet.dll kann jeweils einen Eintrag des
Windows IE-Caches aufnehmen. Man könnte abstrahieren und sagen, der
Datentyp ist ein Datei-Iterator. Wenn ich etwas verändern will, aber
nach dem Test der Auswirkung das original wiederherstellen möchte,
dann muss ich eben ein Backup machen. Da es leider nicht möglich ist
die Anzahl der Elemente in der Datei zu ermitteln ohne sie durchlaufen
zu sein, wollte ich das mehrfach durchlaufen der Datei ersparen ...

Dein Begriff "nicht-kopierbarer Typ" ist dann wohl etwas zu hart.
Für eine statische Kopie eines Puffers bin ich fündig geworden.
Aber für "versteckte" Verschiebungen im Speicherplatz, wie bei
std::vector möglich, funktioniert das folgende nicht:

************************
Copy&Paste vom Seitenende der URL
http://msdn.microsoft.com/en-us/library/windows/desktop/aa383928%28v=vs.85%29.aspx

If a pointer member is not NULL, it points to the memory area
immediately after the structure. While copying the buffer returned by a
function into another buffer, the pointer members should be fixed to
point to the appropriate place in the new buffer, as the following
example shows.


lpDstCEInfo->lpszSourceUrlName =
(LPINTERNET_CACHE_ENTRY_INFO) ((LPBYTE) lpSrcCEInfo +
((DWORD)(lpOldCEInfo->lpszSourceUrlName) - (DWORD)lpOldCEInfo));

************************

Abgesehen von den MS Datentypen ist das, wenn ich es richtig
interpretiere, folgende Rechnung:

PointerTollerTyp buffer1 = (PointerTollerTyp) new char[buffer_size];

buffer1 sei befüllt worden

PointerTollerTyp buffer2 = (PointerTollerTyp) new char[buffer1->StructSize];

Nun memcopy oder das Ding als char-Array in Schleife Zeichenweise kopieren.

Fehlt nur noch die Pointeranpassung am Beispiel-Pseudocode:

offset = buffer1->NullTerminatedCString1 - buffer1
//sollte den Adressversatz geben

buffer2->NullTerminatedCString1 = buffer2 + offset
//sollte den Zeiger NullTerminatedCString1
//auf die nun korrekte Adresse verweisen lassen

Oder?


Vielleicht ist std::vector doch nicht direkt geeignet,
zumindest nicht für std::vector<TollerTyp> oder
besser andersherum: TollerTyp ist nicht für std::vector
geeignet :-)


> Wenn dir eine Container-artige Verwaltung wichtig ist,
> solltest du dir am besten einen C++-Wrapper-Typen schreiben,
> der jeweils ein konstruiertes Objekt von Tollertyp einfach
> nur referenziert.
>
> Mehrere Möglichkeiten fallen da ein:
>
> a) Du nimmst einfach std::vector<TollerTyp*>.
> b) Du nimmst std::vector<boost::shared_pointer<TollerTyp>>
>
> Ansatz (a) ist trivial, schränkt allerdings den Lebenszyklus des
> Containers
> auf die Gültigkeit der Pointer ein

Stimmt.

> Ansatz (b) hat den Vorteil, dass du unterschiedliche Lebensdauern
> von TollerTyp abbilden kannst, da du eine eigene Löschfunktion
> angeben kannst beim Erzeugen dieses SmartPointers.

hört sich interessant an. Aber die Korrektur über Zeigerarithmetik,
müsste auch bei einem std::vector<boost::shared_pointer<TollerTyp>>
für die jeweiligen char* Elemente in TollerTyp gemacht werden.

Zumindest könnte ich so einen shared_pointer nicht an eine C Funktion
übergeben. Zumindest hab ich auf den schnellen Blick bei Boost nichts
derartiges gesehen.

Oder?

> Vielleicht helfen ja obige Anregungen ein wenig.

Ja, auf jeden Fall besten Dank :-)

Gruß Robert

Daniel Krügler

unread,
Nov 28, 2011, 3:35:52 PM11/28/11
to
On 27 Nov., 20:53, Robert Hartmann <Robert_Hartm...@gmx.net> wrote:
> Am 27.11.2011 16:33, schrieb Daniel Krügler:
[..]
> > Nun, es ist soweit klar, dass Tollertyp ein nicht-kopierbarer
> > Typ ist, mir ist daher unklar, warum du ihn dann kopieren
> > willst?
>
> Der konkrete Datentyp der wininet.dll kann jeweils einen Eintrag des
> Windows IE-Caches aufnehmen. Man könnte abstrahieren und sagen, der
> Datentyp ist ein Datei-Iterator. Wenn ich etwas verändern will, aber
> nach dem Test der Auswirkung das original wiederherstellen möchte,
> dann muss ich eben ein Backup machen. Da es leider nicht möglich ist
> die Anzahl der Elemente in der Datei zu ermitteln ohne sie durchlaufen
> zu sein, wollte ich das mehrfach durchlaufen der Datei ersparen ...
>
> Dein Begriff "nicht-kopierbarer Typ" ist dann wohl etwas zu hart.

Nicht wirklich. Der Datentyp, so wie er definiert ist, ist nicht
kopierbar. Das heisst nicht, dass man ihn nicht kopieren könnte,
wenn man das wollte. Du würdest dann eine Hüllklasse schreiben,
die Tollertyp enthält und dann würdest du den Kopierkonstruktor
und Zuweisungsoperator der Hüllklasse so schreiben dass das
Tollertyp-Element "richtig" kopiert wird.

struct Wrapper {
TollerTyp data;
Wrapper(const Wrapper&); // Zu definieren
Wrapper& operator=(const Wrapper&); // Zu definieren
~Wrapper(); // Zu definieren
};

[Nach der üblichen Faustregel muss dein Destruktor auch
definiert werden, wenn die Kopieroperationen zu definieren
sind. Dies scheint hier zuzutreffen, da diese Operationen
nach deiner unten folgenden Beschreibung ja Speicher
anfordern, der wieder freizugeben ist]

Technisch also kein Problem, aber es stellt sich die Frage,
ob man das wirklich will.
Schliesslich *muss* man ja Objekte eines solchen Typs nicht
unbedingt kopieren, nur weil man es technisch machen kann.

Mein Vorschlag war ein Objekt-Identitätsbezogene Lösung,
daher habe ich den Vorschlag gemacht, keine Kopien zu
erzeugen. Aus deiner bisherigen Beschreibung ist auch nicht
klar geworden, dass Kopieren eine Anforderung ist. Verwaltung
in einem Container geht - wie gezeigt - ja auch über Zeiger
oder "intelligente Zeiger".

> Für eine statische Kopie eines Puffers bin ich fündig geworden.
> Aber für "versteckte" Verschiebungen im Speicherplatz, wie bei
> std::vector möglich, funktioniert das folgende nicht:
>
> ************************
> Copy&Paste vom Seitenende der URLhttp://msdn.microsoft.com/en-us/library/windows/desktop/aa383928%28v=...
>
> If a pointer member is not NULL, it points to the memory area
> immediately after the structure. While copying the buffer returned by a
> function into another buffer, the pointer members should be fixed to
> point to the appropriate place in the new buffer, as the following
> example shows.
>
> lpDstCEInfo->lpszSourceUrlName =
>      (LPINTERNET_CACHE_ENTRY_INFO) ((LPBYTE) lpSrcCEInfo +
>         ((DWORD)(lpOldCEInfo->lpszSourceUrlName) - (DWORD)lpOldCEInfo));
>
> ************************

Wie gesagt, wenn du unbedingt willst, kannst du eine Hüllklasse
von TollerTyp definieren und die Kopieroperationen wie oben gezeigt
definieren. Start wie oben.

> Abgesehen von den MS Datentypen ist das, wenn ich es richtig
> interpretiere, folgende Rechnung:
>
> PointerTollerTyp buffer1 = (PointerTollerTyp) new char[buffer_size];
>
> buffer1 sei befüllt worden
>
> PointerTollerTyp buffer2 = (PointerTollerTyp) new char[buffer1->StructSize];
>
> Nun memcopy oder das Ding als char-Array in Schleife Zeichenweise kopieren.
>
> Fehlt nur noch die Pointeranpassung am Beispiel-Pseudocode:
>
> offset = buffer1->NullTerminatedCString1 - buffer1
>         //sollte den Adressversatz geben
>
> buffer2->NullTerminatedCString1 = buffer2 + offset
>         //sollte den Zeiger NullTerminatedCString1
>          //auf die nun korrekte Adresse verweisen lassen
>
> Oder?

Hört sich so an, ja (Habe mir das aber nicht im Detail angeguckt).

> Vielleicht ist std::vector doch nicht direkt geeignet,
> zumindest nicht für std::vector<TollerTyp> oder
> besser andersherum: TollerTyp ist nicht für std::vector
> geeignet :-)

Wie gesagt, es hängt von deinen Anforderungen ab.
Container können auch Pointer enthalten, da ist erstmal
nichts gegen zu sagen. Problematisch würde es, wenn du
den Container dann kopieren willst. Dann nimmst du besser
einen Container mit SmartPointer oder du nimmst die
oben beschriebene TollerType-Hüllklasse.

> > Ansatz (b) hat den Vorteil, dass du unterschiedliche Lebensdauern
> > von TollerTyp abbilden kannst, da du eine eigene Löschfunktion
> > angeben kannst beim Erzeugen dieses SmartPointers.
>
> hört sich interessant an. Aber die Korrektur über Zeigerarithmetik,
> müsste auch bei einem std::vector<boost::shared_pointer<TollerTyp>>
> für die jeweiligen char* Elemente in TollerTyp gemacht werden.

Nein, da beim Kopieren eines boost::shared_pointer<TollerTyp> die
Daten nicht kopieren werden, sondern nur die Referenzen. das ist
wie bei normalen Zeigern, nur brauchst du dich nicht um die
Speicherverwaltung zu kümmern.

> Zumindest könnte ich so einen shared_pointer nicht an eine C Funktion
> übergeben. Zumindest hab ich auf den schnellen Blick bei Boost nichts
> derartiges gesehen.

Brauchst du ja auch nicht. Du kannst vom boost::shared_ptr
per Funktion "get" an das Objekt drankommen.

Viel Erfolg noch und besten Gruss aus Bremen,

Daniel
0 new messages