void-pointer -- welcher cast?
-
Hallo zusammen,
ich benutze eine Bibliothek, die mir einen void-pointer (hat den Bezeichner "user_pointer" )zur Verfügung stellt,
den ich für meine Zwecke nutzen muss.
Ich habe jedoch vorher nicht viel mit void-pointern gemacht.Ich reserviere irgendwo in meinem Code Speicher für einen vector
und weise die Adresse dem void-pointer zu, ungefähr socell->set_user_pointer( new std::vector<PointHistory<dim> >( quadrature_formula.size() ) );Um nachher auf den Inhalt des void-pointers zuzugreifen,
wird in dem Tutorial zu der Bibliothek ein reinterpret_cast genutzt.
Ich habe vorher noch nie einen reinterpret_cast genutzt
und im Netz wird er als der mächtigste und gefährlichste der cast-Operatoren
bezeichnet.Bei anderen Beispielen zu void-pointern im Netz wird jedoch
der static_cast genutzt, z.B hier http://www.devx.com/tips/Tip/14446Welchen cast sollte man denn hier verwenden und warum?
-
Um void Pointer zu casten ist ein static_cast das richtige.
ABER
: Dein Code wird krachen. Dein Pointer muss auf das erste Element des Vectors zeigen, nicht auf den vector selbst. Überhaupt ist hier der vector nicht anzuraten, denn wie willst du ihn hinterher wieder loswerden, wenn du nur das erste Element kennst? Du müsstest zwei Zeiger speichern.
Besser wärecell->set_user_pointer( new PointHistory<dim> [quadrature_formula.size()]);
-
Hallo Rahul,
reinterpret_cast ist wohl das 'ehrlichere'.
Er bedeutet, daß der void Pointer ohne Veränderung
als das interpretiert wird, was angegeben wurde.
Das gilt übrigends für alle anderen Daten auch:
keine Veränderung der Bits.Ich glaube static_cast macht in diesem Fall (void* zu T*) das selbe.
In anderen Fällen wird u.U. der Pointer geändert.Gruß Frank
-
Danke für die Antworten, aber deine Warnung - SeppJ - verstehe ich nicht,
warum MUSS der user_pointer auf das erste Element des vectors zeigen?
(Das wird übrigens in dem Tutorial gemacht, aber genau das wollte ich abändern)Kann ich nicht mit dem cast auf den entsprechenden Zeiger auf die Elemente innerhalb des vectors zugreifen, also ungefähr so?
std::vector< PointHistory<dim> > * myVector = static_cast< std::vector< PointHistory<dim> > * >( cell->user_pointer() ); PointHistory<dim> myQuadraturePoint = myVector->[0];Oder was genau wird mir dabei um die Ohren fliegen?
-
Hallo Rahul,
Gegen eine Pointer auf einen vector als 'Userpointer' spricht meiner Meinung nach nichts.
Du musst halt beim zurück casten von void* auf den Vector alles richtig machen,
dann sollte auch ein delete möglich sein.
Ich habe so etwas noch nie gebraucht ...Wenn du es nicht probieren möchtest oder wenn du unsicher bist,
kannst du ja auch einen mit malloc angelegten pointer verwenden ...Gruß Frank
-
Nein, nein, nein!vector sieht ungefähr so aus (stark vereinfacht):
template<typename T> class vector { private: T *erstes_element; // anderer Verwaltungskram public: vector(size_t size): erstes_element(new T[size]){} // Eine Menge andere Funktionen };Wenn man nun einen Pointer auf einen vector zeigen lässt, dann zeigt er auf das Vectorobjekt (also den Verwaltungskram), nicht auf den allokierten Speicher des Vektors! Und der vector selbst ist halt nur ein paar Byte groß, der allokierte Speicher liegt irgendwo ganz woanders auf dem Heap!
Wenn man dann den Speicher wo der Vector liegt überschreibt, dann schreibt man:
1. Über den Verwaltungskram und nicht über die Datenelemente
2. Bereits nach wenigen Bytes in Speicher der einem nicht gehört.
-
@SeppJ:
Ja, ja, ja!
Das ist ein *USER-DATA* Pointer, den kann er zeigen lassen auf was er will.Die Library kümmert sich nicht darum was da drinnen steht, die bietet den Platz einfach nur an, damit der User bestimmte eigene Datenstrukturen an die Library-Objekte anhängen kann.
@Rahul0891:
Nimm static_cast.
-
Frank Erdorf schrieb:
reinterpret_cast ist wohl das 'ehrlichere'.
reinterpret_cast ist IMO weder erhlicher, noch hier angebracht.
Der Standard garantiert explizit dass bei der Konvertierung T* -> void* -> T* über static_cast zum Schluss wieder das rauskommt mit was man angefangen hat.
-
void-pointer -- welcher cast?Gar keiner!!! Überdenk dein Design!!!
-
Nochmals danke für die vielen Antworten...
Gar keiner!!! Überdenk dein Design!!!
Hmm, wie in meinem Anfangspost steht, ist das nicht mein Design,
sondern das Design der genutzten Bibliothek...Von diesen Zellen (cell) gibt es nachher 50.000 oder mehr
und der user_pointer ist die einzige Schnittstelle,
die nir zur Verfügung steht...
-
Es ist häufig so, dass C Librarys Callback Funktionen mit einem Parameter (Typ void) anbieten, um eigene Daten in der Funktion verfügbar zu machen.
Das ganze ist also vollkommen in Ordnung und hat mit schlechtem Design rein gar nichts zu tun. Ganz im Gegenteil.
-
ich weiß halt gerne ob sich beim cast die bits ändern oder nicht.
Und daß wenn möglich beim Lesen des Codes, so deutlich wie es geht.Tatsächlich geht hier technisch sowohl static_cast als auch reinterpret_cast.
Wenn ein einfaches Reinterpretieren ohne Wandlung reicht,
dann darf man das auch gerne hinschreiben.Wobei ich mir auch Fälle denken kann,
z.B. bei Code Änderungen, die mit static_cast auffallen würden,
aber mit reinterpret_cast nicht.Was soll's
Gruß Frank
-
hustbaer schrieb:
@SeppJ:
Ja, ja, ja!
Das ist ein *USER-DATA* Pointer, den kann er zeigen lassen auf was er will.Aber es ist nicht genug Platz dort, weil der vector selbst nur ein paar Byte hat! Man müsste
cell->set_user_pointer( new std::vector<PointHistory<dim> >[ (sizeof(<PointHistory<dim>) * quadrature_formula.size()) / sizeof (std::vector<PointHistory<dim> >) ] );machen, damit die Menge an Speicher da ist, die der Threadersteller haben will. Und selbst dann geht dies extrem schief, denn wenn man hinterher ein delete auf diesen Pointer macht, dann muss man ihn dafür in einen vector<PointHistory<dim> >* casten. Dies ruft aber den Destructor von Vector auf, welcher dann versucht völlig unsinnige Sachen zu machen, weil die Verwaltungsdaten mit was anderem überschrieben wurden. Das gibt eine totale Katastrophe! Das musst du doch sehen!
-
Hallo SeppJ
Du verstehts doch eigentlich alles in diesem Zusammenhang.
Die Library bietet 4 Bytes User Data an.
Dort kann man also einen void* speichern,
einen Zeiger auf irgendwas so zusagen.
Später kann man sich diese 4 Bytes, wieder holen und interpretieren.Wenn ich also einen Zeiger auf einen vector in die 4 bytes rein tue,
muss ich das beim Rausholen auch als Zeiger auf einen Vector interpretierenKlar zeigt das dann 'nur' auf die Verwaltungsstruktur des vectors und klar liegen
die eigentlichen Daten nochmals woanders.Vieleicht hat es dir geholfen,
wenn nicht, vieleicht hilft es morgen ...Übrigends ist dieser void* für generische Programmierung erforderlich.
Alternativ könnte man für soetwas boost:any einsetzten,
geht in diesem Fall aber nicht...Gruß Frank
-
Ich weiß wohl was du meinst, aber guck mal auf den ersten Post, der threadersteller will garantiert nicht, dass der void* auf einen vector zeigt, sondern auf quadrature_formula.size() Instanzen von PointHistory<dim>. Vermutlich weil die Bibliothek ein Array von PointHistory<dim>[quadrature_formula.size()] erwartet. Und das geht eben nicht, wenn man auf einen vector zeigt.
-
So wie ich das sehe hat er folgendes vor:
void* user_ptr = 0; void foo() { std::vector<int>* vec = static_cast<std::vector<int>*>(user_ptr); for (std::vector<int>::const_iterator i = vec->begin(); i != vec->end(); ++i) std::cout << *i << std::endl; delete vec; } int main() { int tmp[] = { 1, 2, 3, 4, 5 }; user_ptr = new std::vector<int>(tmp, tmp + sizeof(tmp) / sizeof(int)); foo(); }Das funktioniert sogar wunderbar!
-
Okay, um hier weiteren Spekulationen einen Riegel vorzuschieben
und da ich das Problem nicht genau erfasse, noch ein paar Zusatzinformationen.Die Bibliothek sollte nix mit user_pointer veranstalten,
es ist wie hustbaer geschrieben hat...Die Library kümmert sich nicht darum was da drinnen steht, die bietet den Platz einfach nur an, damit der User bestimmte eigene Datenstrukturen an die Library-Objekte anhängen kann.
um es genauer zu beschreiben, soll es mir Daten über bestimmte Schleifenzyklen
transportieren.PointHistory ist dabei ein struct, in dem wieder eine Klasse Tensor<dim>
eingebettet ist, das habe ich so aus dem Tutorial 1zu1 übernommen.Vielleicht klärt das schon ein paar Mißverständnisse...
-
Frank Erdorf schrieb:
ich weiß halt gerne ob sich beim cast die bits ändern oder nicht.
Und daß wenn möglich beim Lesen des Codes, so deutlich wie es geht.Tatsächlich geht hier technisch sowohl static_cast als auch reinterpret_cast.
Wenn ein einfaches Reinterpretieren ohne Wandlung reicht,
dann darf man das auch gerne hinschreiben.reinterpret_cast muss in dem Fall nicht funktionieren.
void* muss gross genug sein um alle möglichen T*, U* etc. "aufnehmen" zu können.
Zeiger auf andere Typen, eben T*, U* etc., können (dürfen) allerdings kleiner als ein void* sein.
Dadurch ist auch die Bit-Kompatibilität von T* zu void* bzw. umgekehrt nicht garantiert.Daher ist reinterpret_cast in dem Fall strenggenommen falsch.
Daher ist static_cast angesagt.Und wann und wo ein Zeiger seine "interne Representation" ändert, bzw. ändern kann/darf, darüber mache ich mir schon länger keine Gedanken mehr

-
@SeppJ:
Doch, genau das will er
Ich verstehe auch nicht ganz was dich da auf eine falsche Fährte gelockt hat. Ist dir noch nie ein UserData Zeiger untergekommen?
Ich hätte gedacht dass die Technik so verbreitet ist, dass sie jeder kennen sollte, und auch intuitiv verstehen.Ich kenne das schon vom Amiga OS, später dann von der WinAPI (meist als "dwUserdata" vom Typ "DWORD_PTR"), und selbst im .NET Framework trifft man es an einigen Stellen (Property "Tag" vom Type "object"). Und natürlich aus diversen anderen Libraries.
-
................ schrieb:
Es ist häufig so, dass C Librarys Callback Funktionen mit einem Parameter (Typ void) anbieten, um eigene Daten in der Funktion verfügbar zu machen.
Das ganze ist also vollkommen in Ordnung und hat mit schlechtem Design rein gar nichts zu tun. Ganz im Gegenteil.
Aus C-Sicht: Ja.
Aus C++-Sicht ist das schlechtes Design. Moderne Callbacks sind typsicher und viel flexibler (schau dir mal
boost::functionundboost::bindan). Auch abstrakte Interfaces sind eine Möglichkeit. In C++ würde ich nichtvoid*einsetzen, wenn sich das Problem besser lösen lässt. Aber zur Kompatibilität mit C- oder älteren C++-APIs hat man eben keine Wahl.