Hallo Community, ich hänge aktuell an einem kleinen Cast Problem. Vielleicht kann mir da ein C++ Spezialist mal kurz helfen: void test() { void* x = NULL; vector<MyClass*> myClassList; myClassList.push_back(new MyClass()); vector<void*> myList; myList.push_back(&myClassList); x = &myList; vector<void*> cast1 = *reinterpret_cast<vector<void*> *>(x); vector<MyClass*> cast2 = reinterpret_cast<vector<MyClass*> *>(cast1[0]); cout << cast2.size(); } Für cast2 kommt komischerweise 0 heraus obwohl ein Element drin sein müsste. Habe ich irgendwo einen Leichtsinsfehler beim casten gemacht? Danke schonmal, IngC++
bin mir jetzt nicht ganz sicher, aber die vectoren werden hier noch mal kopiert.
1 | vector<MyClass*> cast2 = reinterpret_cast<vector<MyClass*> |
2 | *>(cast1[0]); |
sicher das du das willst? mit einem Typedef könnte man das auch lesbarer schreiben.
Peter II schrieb: > bin mir jetzt nicht ganz sicher, aber die vectoren werden hier > noch mal kopiert.vector<MyClass*> cast2 = > reinterpret_cast<vector<MyClass*> > *>(cast1[0]); > > sicher das du das willst? > mit einem Typedef könnte man das auch lesbarer schreiben. Ich nutze einen C++ 99 Compiler. Da muss ich explizit den Datentyp vornedran schreiben, bei C++ 11 Compiler kann man das auto Keyword benutzen. Aber du hast recht, mit typedefs wäre das ganze lesbarer. Dennoch bleibt mein Problem bestehen...
1 | #include <iostream> |
2 | #include <vector> |
3 | |
4 | struct MyClass { } ; |
5 | |
6 | using namespace std ; |
7 | |
8 | void test() |
9 | {
|
10 | void* x = NULL; |
11 | |
12 | vector<MyClass*> myClassList; |
13 | myClassList.push_back(new MyClass()); |
14 | |
15 | vector<void*> myList; |
16 | myList.push_back(&myClassList); |
17 | |
18 | x = &myList; |
19 | |
20 | vector<void*> cast1 = *reinterpret_cast<vector<void*> *>(x); |
21 | |
22 | vector<MyClass*> cast2 = reinterpret_cast<vector<MyClass*> *>(cast1[0]); |
23 | |
24 | cout << cast2.size(); |
25 | }
|
1 | $ g++ -Wall -c test.cc |
2 | test.cc: In function ‘void test()’: |
3 | test.cc:22:74: error: conversion from ‘std::vector<MyClass*>*’ to non-scalar type ‘std::vector<MyClass*>’ requested |
4 | vector<MyClass*> cast2 = reinterpret_cast<vector<MyClass*> *>(cast1[0]); |
:
Bearbeitet durch User
damit es nicht ganz nutzlos ist. Da fehlt ein * bei reinterpret_cast<std::vector<myClass*> *>(cast1[0]); du castest ja auf einen Zeiger. Den musst du natürlich auch dereferenzieren.
Mikro 7. schrieb: > #include <iostream> > #include <vector> > > struct MyClass { } ; > > using namespace std ; > > void test() > { > void* x = NULL; > > vector<MyClass*> myClassList; > myClassList.push_back(new MyClass()); > > vector<void*> myList; > myList.push_back(&myClassList); > > x = &myList; > > vector<void*> cast1 = *reinterpret_cast<vector<void*> *>(x); > > vector<MyClass*> cast2 = reinterpret_cast<vector<MyClass*> > *>(cast1[0]); > > cout << cast2.size(); > } > > $ g++ -Wall -c test.cc > test.cc: In function ‘void test()’: > test.cc:22:74: error: conversion from ‘std::vector<MyClass*>*’ to > non-scalar type ‘std::vector<MyClass*>’ requested > vector<MyClass*> cast2 = reinterpret_cast<vector<MyClass*> > *>(cast1[0]); Sorry, Tippfehler (hab den Derefenzierungsoperator vor dem reinterpret hier vergessen gehabt, im Code steht er jedoch) Mit vector<MyClass*> cast2 = *reinterpret_cast<vector<MyClass*> > *>(cast1[0]); Sollte es zwar bauen, aber das Problem mit dem ursprünglichen Vektor besteht weiterhin. Trotzdem danke schonmal
nicht"Gast" schrieb: > damit es nicht ganz nutzlos ist. > Da fehlt ein * bei > reinterpret_cast<std::vector<myClass*> *>(cast1[0]); > > du castest ja auf einen Zeiger. Den musst du natürlich auch > dereferenzieren. Ja danke, war nur ein Schreibfehler meinerseits. Im Originalcode hatte ich es nicht vergessen. Trotzdem gut aufgepasst :-)
IngC++ schrieb: > Mikro 7. schrieb: > #include <iostream> > #include <vector> > struct MyClass { } ; > using namespace std ; > void test() > { > void* x = NULL; > vector<MyClass*> myClassList; > myClassList.push_back(new MyClass()); > vector<void*> myList; > myList.push_back(&myClassList); > x = &myList; > vector<void*> cast1 = *reinterpret_cast<vector<void*> *>(x); > vector<MyClass*> cast2 = reinterpret_cast<vector<MyClass*> > *>(cast1[0]); > cout << cast2.size(); > } > $ g++ -Wall -c test.cc > test.cc: In function ‘void test()’: > test.cc:22:74: error: conversion from ‘std::vector<MyClass*>*’ to > non-scalar type ‘std::vector<MyClass*>’ requested > vector<MyClass*> cast2 = reinterpret_cast<vector<MyClass*> > *>(cast1[0]); > Sorry, Tippfehler (hab den Derefenzierungsoperator vor dem reinterpret hier vergessen gehabt, im Code steht er jedoch) Mit vector<MyClass*> cast2 = *reinterpret_cast<vector<MyClass*> *>(cast1[0]);
1 | #include <iostream> |
2 | #include <vector> |
3 | |
4 | struct MyClass { } ; |
5 | |
6 | using namespace std ; |
7 | |
8 | int main() |
9 | {
|
10 | void* x = NULL; |
11 | |
12 | vector<MyClass*> myClassList; |
13 | myClassList.push_back(new MyClass()); |
14 | |
15 | vector<void*> myList; |
16 | myList.push_back(&myClassList); |
17 | |
18 | x = &myList; |
19 | |
20 | vector<void*> cast1 = *reinterpret_cast<vector<void*> *>(x); |
21 | |
22 | vector<MyClass*> cast2 = *reinterpret_cast<vector<MyClass*> *>(cast1[0]); |
23 | |
24 | cout << cast2.size(); |
25 | |
26 | return 0 ; |
27 | }
|
1 | $ g++ -Wall -o test test.cc |
2 | $ ./test |
3 | 1 |
Hm komisch, ich könnte schwören das es bei mir heute nicht geklappt hat. Ich Check morgen nochmal meinen Code und Berichte. Danke schonmal für deine Hilfe!
IngC++ schrieb: > Ich nutze einen C++ 99 Compiler Was ist denn C++99? Ich kenne nur C++98, C++03, 11, 14 und 17... Dass so ein Code fürchterlich mies ist sollte klar sein. Die MyClass Instanz wird nicht freigegeben (Speicherleck). Statt NULL sollte man nullptr schreiben. reinterpret_cast ist nur selten notwendig (wenn schon in low-level Code für I/O und Speicherverwaltung) und meistens ein Indiz für schlechtes Design.
Dr. Sommer schrieb: > IngC++ schrieb: > Ich nutze einen C++ 99 Compiler > > Was ist denn C++99? Ich kenne nur C++98, C++03, 11, 14 und 17... > Dass so ein Code fürchterlich mies ist sollte klar sein. Die MyClass > Instanz wird nicht freigegeben (Speicherleck). Statt NULL sollte man > nullptr schreiben. reinterpret_cast ist nur selten notwendig (wenn schon > in low-level Code für I/O und Speicherverwaltung) und meistens ein Indiz > für schlechtes Design. 1. Tippfehler, meinte auch C++ 98. 2. Das war nur kleines Snippet welches die Problematik aufzeigen sollte. Selbstverständlich habe ich mich im Originalcode um Memory Leaks gekümmert. 3. Warum ist das ein Indiz für schlechtes Design? Sorry für meine Unwissenheit - komme aus der Managed Welt und hab bisher fürs Casten in C++ entweder Boxed, static_cast oder reinterpret_cast verwendet. Gerne lasse ich mich von einem besseren Weg überzeugen.
Nebenbei bemerkt gibt es nullptr erst ab C++ 11. Ich nutze C++ 98, von daher bin ich etwas eingeschränkter.
IngC++ schrieb: > Warum ist das ein Indiz für schlechtes Design? reinterpret_cast umgeht halt die Prüfungen des Compilers (Typsicherheit). Damit ist es sehr einfach, schwer zu findende Fehler einzubauen. u.a. durch Verwendung von templates und "sauberen" Klassen-Strukturen lässt sich das (fast) immer vermeiden, was typischerweise auch zu besser strukturierten und lesbaren Code führt. reinterpret_cast ist oft eine "schmutzige" Abkürzung, deswegen ist diese Anweisung auch so lang und hässlich ;-) IngC++ schrieb: > C++ entweder Boxed, static_cast oder reinterpret_cast verwendet. Boxed bei C++/CLI oder was meinst du? Du kannst ja mal deine typischen Fälle für reinterpret_cast zeigen, dann lässt sich bestimmt eine bessere Lösung finden. IngC++ schrieb: > Nebenbei bemerkt gibt es nullptr erst ab C++ 11. Ich nutze C++ 98, > von > daher bin ich etwas eingeschränkter. Das ist schade - C++11 bringt viele sehr nützliche Verbesserungen, insbesondere auch für den Embedded Bereich. nullptr ist eine davon, damit lassen sich Uneindeutigkeiten/Fehler in bestimmten Sonderfällen vermeiden. Wenn du viel C++ nutzen möchtest solltest du dir Gedanken machen ob du nicht einen modernen Compiler nutzen kannst, welcher C++11 beherrscht.
IngC++ schrieb: > Für cast2 kommt komischerweise 0 heraus obwohl ein Element drin sein > müsste. Habe ich irgendwo einen Leichtsinsfehler beim casten gemacht? Und weil es noch niemand erwähnt hat: von void* auf andere Zeiger-Typen reicht in C++ static_cast.
IngC++ schrieb: > 3. Warum ist das ein Indiz für schlechtes Design? weil du den Vektor kopierst. Verwende lieber eine Referenz darauf. Nur für den Cast eine Kopie von allem anzulegen ist sinnlos.
IngC++ schrieb: > 3. Warum ist das ein Indiz für schlechtes Design? Sorry für meine > Unwissenheit - komme aus der Managed Welt und hab bisher fürs Casten in > C++ entweder Boxed, static_cast oder reinterpret_cast verwendet. Gerne > lasse ich mich von einem besseren Weg überzeugen. Bei C++ wurde sehr viel Arbeit reingesteckt, dass man weitgehend ohne Casts auskommt. Immer wenn du einen Cast benötigst, solltest du erstmal überlegen, ob es nicht einen Weg ohne den Cast gibt. Der ist meistens der sauberere Weg. Wie schon gesagt wurde, ist vor allem für den reinterpret_cast lowlevel-Code, der direkt mit irgendwelcher Hardware bzw. Byte-orientierten Schnittstellen arbeitet, der Hauptanwendungszweck.
IngC++ schrieb: > Hallo Community, > > ich hänge aktuell an einem kleinen Cast Problem. Vielleicht kann mir da > ein C++ Spezialist mal kurz helfen: > > void test() > { > void* x = NULL; > > vector<MyClass*> myClassList; > myClassList.push_back(new MyClass()); > > vector<void*> myList; > myList.push_back(&myClassList); > > x = &myList; > > vector<void*> cast1 = *reinterpret_cast<vector<void*> *>(x); > > vector<MyClass*> cast2 = reinterpret_cast<vector<MyClass*> > *>(cast1[0]); > > > cout << cast2.size(); > > > } > > Für cast2 kommt komischerweise 0 heraus obwohl ein Element drin sein > müsste. Habe ich irgendwo einen Leichtsinsfehler beim casten gemacht? Warum verwendest Du bei der Definition von
1 | vector<void*> myList; |
nicht den richtigen DT? Abgesehen davon, dass das ganze abenteuerlich ist ...
Wilhelm M. schrieb: > nicht den richtigen DT? Abgesehen davon, dass das ganze abenteuerlich > ist ... Vermutlich soll das ganze spaeter Polymorphismus emulieren...
lalala schrieb: > Wilhelm M. schrieb: >> nicht den richtigen DT? Abgesehen davon, dass das ganze abenteuerlich >> ist ... > > Vermutlich soll das ganze spaeter Polymorphismus emulieren... Vermutlich war es nur ein Beispiel?
Torsten R. schrieb: > lalala schrieb: >> Wilhelm M. schrieb: >>> nicht den richtigen DT? Abgesehen davon, dass das ganze abenteuerlich >>> ist ... >> >> Vermutlich soll das ganze spaeter Polymorphismus emulieren... > > Vermutlich war es nur ein Beispiel? Vermute mal: Unwissenheit, dass man vectors of vectors of maps of ... machen kann.
Beitrag #5026933 wurde vom Autor gelöscht.
Sebastian V. schrieb im Beitrag #5026933: > Torsten R. schrieb: >> Vermutlich war es nur ein Beispiel? > > Hoffentlich ein Beispiel wie man es auf keinen Fall machen sollte. Es > gibt keine Garantie das ein vector<MyClass*> und vector<void*> irgendwie > vom Datenlayout kompatibel sind und das bei dem Cast irgendwas > sinnvolles bei rum kommt. Stimmt, und wenn Du Dir den Beispiel-Code mal genauer durchließt, dann wirst Du sehen, dass der Kollege das auch überhaupt nicht probiert hat.
Sebastian V. schrieb: > Ja habs auch gerade gesehen... Solange er die Frage nicht beantwortet, warum er jegliche Typinformation explizit wegwirft bei der Definition von myList ist jede Spekulation überflüssig. Den (fragwürdigen) Grund kann nur TO mitteilen...
:
Bearbeitet durch User
Erstmal danke für eure konstruktiven Beiträge. Grund für dieses fragwürdige Konstrukt ist das Arbeiten mit Threads Bzw. CreateThread. Diesem kann man meines Wissens als Argument nur einen void* mitgeben. Und da ich mehrere Argumente unterschiedlichen Datentyps übergeben muss packe ich diese in eine Liste von void* und caste die Elemente im Thread auf die gewünschten Typen zurück. Bin aber gerne für Vorschläge offen :-)
IngC++ schrieb: > Bin aber gerne für Vorschläge offen :-) Ok, es besteht aber trotzdem kein Grund, den Container "falsch" zu typisieren ... Willst Du pthreads verwenden? Macht auch wenig Sinn. Wir haben mittlerweile mehr als C++11 und damit http://en.cppreference.com/w/cpp/thread
Da ich ahne welche Beiträge kommen werden schonmal vornweg: Ja man kann auch ein struct oder eine Klasse mit den Inhalten definieren und deren Referenz per void* an den Thread übergeben. Wäre auch sauberer an der Stelle, bin da grad aber erst am ausprobieren welches die eleganteste Lösung wäre. Unabhängig von der Thread Geschichte möchte ich an einer anderen Stelle Polymorphismus "emulieren", d.h ich habe eine Liste von Instanzen die das gleiche "Interface" (in C++ habe ich das durch abstrakte Klassen mit pure Virtual Methods gelöst) besitzen und deren Methode einen void* als Argument erwarten. Jedes Interface kann aber eine andere konkretere Implementierung der Methode haben, so dass es nicht einen speziellen Datentyp gibt der übergeben werden kann sondern je nach Kontext ein anderer. Alternativ könnte man auch die Methoden mit den Datentypen überladen, nur habe ich dann bei manchen konkreten Implementierungen überladene Methoden zu implementieren die gar nicht gebraucht werden.
Wilhelm M. schrieb: > IngC++ schrieb: > > Bin aber gerne für Vorschläge offen :-) > > Ok, es besteht aber trotzdem kein Grund, den Container "falsch" zu > typisieren ... > Willst Du pthreads verwenden? Macht auch wenig Sinn. > Wir haben mittlerweile mehr als C++11 und damit > http://en.cppreference.com/w/cpp/thread Hallo Wilhelm, aufgrund bestimmter OS Anforderungen bin ich leider an C++ 98 gebunden, auch wenn ich selber gerne C++ 11 nehmen möchte. Ich benutze aktuell CreateThreads.
IngC++ schrieb: > Unabhängig von der Thread Geschichte möchte ich an einer anderen Stelle > Polymorphismus "emulieren", d.h ich habe eine Liste von Instanzen die > das gleiche "Interface" (in C++ habe ich das durch abstrakte Klassen mit > pure Virtual Methods gelöst) besitzen und deren Methode einen void* als > Argument erwarten. Jedes Interface kann aber eine andere konkretere > Implementierung der Methode haben, so dass es nicht einen speziellen > Datentyp gibt der übergeben werden kann sondern je nach Kontext ein > anderer. Alternativ könnte man auch die Methoden mit den Datentypen > überladen, nur habe ich dann bei manchen konkreten Implementierungen > überladene Methoden zu implementieren die gar nicht gebraucht werden. Hört sich nach double-dispatch an.
IngC++ schrieb: > Wilhelm M. schrieb: > IngC++ schrieb: > Bin aber gerne für Vorschläge offen :-) > Ok, es besteht aber trotzdem kein Grund, den Container "falsch" zu > typisieren ... > Willst Du pthreads verwenden? Macht auch wenig Sinn. > Wir haben mittlerweile mehr als C++11 und damit > http://en.cppreference.com/w/cpp/thread > > Hallo Wilhelm, > > aufgrund bestimmter OS Anforderungen bin ich leider an C++ 98 gebunden, > auch wenn ich selber gerne C++ 11 nehmen möchte. Ich benutze aktuell > CreateThreads. OS Anforderungen war jetzt das falsche Wort - nennen wir es Rahmenbedingungen. Klar könnte man rein theoretisch C++ 11 nehmen und die Runtime dazu mitliefern, aber das ist aktuell nicht gewollt.
Wilhelm M. schrieb: > IngC++ schrieb: > > Unabhängig von der Thread Geschichte möchte ich an einer anderen Stelle > Polymorphismus "emulieren", d.h ich habe eine Liste von Instanzen die > das gleiche "Interface" (in C++ habe ich das durch abstrakte Klassen mit > pure Virtual Methods gelöst) besitzen und deren Methode einen void* als > Argument erwarten. Jedes Interface kann aber eine andere konkretere > Implementierung der Methode haben, so dass es nicht einen speziellen > Datentyp gibt der übergeben werden kann sondern je nach Kontext ein > anderer. Alternativ könnte man auch die Methoden mit den Datentypen > überladen, nur habe ich dann bei manchen konkreten Implementierungen > überladene Methoden zu implementieren die gar nicht gebraucht werden. > > Hört sich nach double-dispatch an. Exakt das ist es.
IngC++ schrieb: > Unabhängig von der Thread Geschichte möchte ich an einer anderen Stelle > Polymorphismus "emulieren", d.h ich habe eine Liste von Instanzen die > das gleiche "Interface" (in C++ habe ich das durch abstrakte Klassen mit > pure Virtual Methods gelöst) besitzen und deren Methode einen void* als > Argument erwarten. Jedes Interface kann aber eine andere konkretere > Implementierung der Methode haben, so dass es nicht einen speziellen > Datentyp gibt der übergeben werden kann sondern je nach Kontext ein > anderer. Alternativ könnte man auch die Methoden mit den Datentypen > überladen, nur habe ich dann bei manchen konkreten Implementierungen > überladene Methoden zu implementieren die gar nicht gebraucht werden. Das würde ich aber für die Polymorphistik (es heißt übrigens Polymorphie) auf jeden Fall gegenüber einem void* vorziehen. Warum werden denn nicht alle gebraucht? Könnte man vielleicht in der Basisklasse eine default-Implementation anbieten? Was soll passieren, wenn versehentlich mal eine der "nicht gebrauchten" Kombinationen doch vorkommt? Evtl. lässt sich auch mit Templates was machen. Ist halt schwierig zu sagen, aber bei einem void* für sowas gehen bei mir alle roten Lampen an.
IngC++ schrieb: > Wilhelm M. schrieb: >> IngC++ schrieb: >> >> Unabhängig von der Thread Geschichte möchte ich an einer anderen Stelle >> Polymorphismus "emulieren", d.h ich habe eine Liste von Instanzen die >> das gleiche "Interface" (in C++ habe ich das durch abstrakte Klassen mit >> pure Virtual Methods gelöst) besitzen und deren Methode einen void* als >> Argument erwarten. Jedes Interface kann aber eine andere konkretere >> Implementierung der Methode haben, so dass es nicht einen speziellen >> Datentyp gibt der übergeben werden kann sondern je nach Kontext ein >> anderer. Alternativ könnte man auch die Methoden mit den Datentypen >> überladen, nur habe ich dann bei manchen konkreten Implementierungen >> überladene Methoden zu implementieren die gar nicht gebraucht werden. >> >> Hört sich nach double-dispatch an. > > Exakt das ist es. Und warum machst Du es dann nicht so? Andere Frage: musst Du wirklich einen void* Parameter an CreateThread übergeben? Sprich: arbeiten die Threads mit Kopien der Daten? Falls nicht, kann Du gleich ein gemeinsam-genutztes globales Objekt anlegen, auf das alle Thread zugreifen (Achtung: Synchronisation).
Zeig doch mal einen typischen Aufruf, wie die virtuelle Methode mit dem void* aufgerufen wird. Ich habe das Gefühl, wenn du weißt welchen Datentyp du übergeben musst, weißt du auch, um welche konkrete Klasse es sich handelt...? In C++11 gibt es std::thread, da musst du nix nach void* casten... Wenn ein Compiler für C++11 vorhanden ist (zB Microsoft Visual C++, GCC, Clang, ARMCC...) sollte es normalerweise kein Problem sein einfach auf C++11 umzustellen. Da muss man eigentlich nichts an einer "Runtime" ändern. C++ ist ja auch abwärts-kompatibel. Es gibt eigentlich wenig Grund sich an so alte Technik zu binden!
@Wilhelm M. >Andere Frage: musst Du wirklich einen void* Parameter an CreateThread >übergeben? Sprich: arbeiten die Threads mit Kopien der Daten? muss er - aber wie du bei void* auf "Kopie" kommst musst du noch mal erklären >Falls nicht, kann Du gleich ein gemeinsam-genutztes globales Objekt >anlegen, auf das alle Thread zugreifen (Achtung: Synchronisation). ist doch alles völlig unnötig wenn man es richtig macht - immer diese komischen Tips @IngC++ mach einfach eine saubere Datenklasse - und lass das mit dem vector da steckst du alle PODs/Vectoren als Kopie/Referenzen/Pointer usw. rein die du brauchst und dann ist es doch nur DatenKapselKlasse daten; CreateThread((void*)&daten) oder DatenKapselKlasse daten = new DatenKapselKlasse(...); CreateThread((void*)daten) und in deiner ThreadProc nur DatenKapselKlasse& daten = *(DatenKlasse*)void_parameter; ich verstehe absolut nicht warum du dafür so wirren vector<void*> Code schreiben musst - das ist unübersichtlich und höchst fehleranfällig und einfach grundlos
Dr. Sommer schrieb: > Zeig doch mal einen typischen Aufruf, wie die virtuelle Methode mit dem > void* aufgerufen wird. Ich habe das Gefühl, wenn du weißt welchen > Datentyp du übergeben musst, weißt du auch, um welche konkrete Klasse es > sich handelt...? > > In C++11 gibt es std::thread, da musst du nix nach void* casten... Das habe ich oben doch schon geschrieben! Überlesen? Das will er nicht!
Bert3 schrieb: > @Wilhelm M. > >>Andere Frage: musst Du wirklich einen void* Parameter an CreateThread >>übergeben? Sprich: arbeiten die Threads mit Kopien der Daten? Du hast das gar nicht verstanden! Die Frage ist: muss er wirklich mit gem. DS arbeiten oder kann er auch Kopien (ggf. CoW) benutzen.
Bert3 schrieb: > > DatenKapselKlasse daten; > CreateThread((void*)&daten) Wenn das ne lokale Variable ist, ist es schon falsch!!!
Bert3 schrieb: > > DatenKapselKlasse daten = new DatenKapselKlasse(...); > CreateThread((void*)daten) > > und in deiner ThreadProc nur > DatenKapselKlasse& daten = *(DatenKlasse*)void_parameter; > Oder er schreibt ein Singleton oder Monostate, dann kann er ganz auf die casts und die Zeiger verzichten (Achtung: Singleton aber richtig machen, sprich für Nebenläufigkeit).
:
Bearbeitet durch User
Bert3 schrieb: > @Wilhelm M. > >>Andere Frage: musst Du wirklich einen void* Parameter an CreateThread >>übergeben? Sprich: arbeiten die Threads mit Kopien der Daten? > > muss er - aber wie du bei void* auf "Kopie" kommst musst du noch mal > erklären > >>Falls nicht, kann Du gleich ein gemeinsam-genutztes globales Objekt >>anlegen, auf das alle Thread zugreifen (Achtung: Synchronisation). > > ist doch alles völlig unnötig wenn man es richtig macht - immer diese > komischen Tips Quatsch: klar braucht er Synchronisation. Ein std::vector<> ist unsynchronisiert.
Wilhelm M. schrieb: >> >> DatenKapselKlasse daten; >> CreateThread((void*)&daten) > > Wenn das ne lokale Variable ist, ist es schon falsch!!! solange er den Scope vor Thread-Ende nicht verlaesst ist da nichts falsch - kommt darauf an was/wie er macht
>Oder er schreibt ein Singleton oder Monostate, dann kann er ganz auf die >casts und die Zeiger verzichten (Achtung: Singleton aber richtig machen, >sprich für Nebenläufigkeit). warum soll er dafür ein Singleton oder Monotstate verwenden - wenn er x Threads mit unterschiedlichen Daten füllt ist das nix - und die verwendung von den Daten in einem Thread sind jetzt auch nicht sooo besonders das man dafür Sonder-Verwaltungen braucht - er muss eben nur darauf Achten das seine Variablen den Thread-Lauf ueberdauern
Bert3 schrieb: > Wilhelm M. schrieb: >>> >>> DatenKapselKlasse daten; >>> CreateThread((void*)&daten) >> >> Wenn das ne lokale Variable ist, ist es schon falsch!!! > > solange er den Scope vor Thread-Ende nicht verlaesst ist da nichts > falsch - kommt darauf an was/wie er macht Genau! Deswegen ist sowas fehlerträchtig und sollte man nicht machen!
Wilhelm M. schrieb: > Quatsch: klar braucht er Synchronisation. Ein std::vector<> ist > unsynchronisiert. nur wenn er schreibend zugreift - davon hat er nichts geschrieben btw: was ist mit void* und der Kopie von der du gesprochen hast
>Genau! Deswegen ist sowas fehlerträchtig und sollte man nicht machen!
ein Singleton bring einen Globalen Zustand rein - sollte er mit vielen
Threads und unterschiedlichen Daten hantieren ist das genau so blöd
kommt definitiv nur auf seinen Anwendungsfall an
aber er hat sowieso keine Probleme mit dem Scope - er will nur komischen
direkt einen vector auf void* cast und damit arbeiten
meine (Thread)DatenKapselung macht die Sache nur sauber und ob das dann
auf dem Stack, Heap oder in einem Singleton drinn steckt ist ein anderer
ganz Stiefel
Datenkapselung(was nutzt der Thread wenn es z.B. auch nur Referenzen sind) und Lifetime Management der Daten (Stack, Heap, "Singleton") sind 2 paar Stiefel
Bert3 schrieb: >>Genau! Deswegen ist sowas fehlerträchtig und sollte man nicht machen! > > ein Singleton bring einen Globalen Zustand rein - sollte er mit vielen > Threads und unterschiedlichen Daten hantieren ist das genau so blöd Nein, es gibt keine dangling-pointer -> großer Unterschied!!! Globaler Zustand: das schreibe ich doch die ganze Zeit: Kopien oder nicht für die Threads. Das wissen wir nicht ...
Der Thread muss 0 Ahnung davon haben voher sein Daten kommen - daher die DatenKapsel-Klasse - wie das dann Ausserhalb verwaltet wird muss der Thread-Proc 100% egal sein, ein Singleton zum leichten Zugriff ist hier definitiv nicht nötig weil es einfach trivial ist die Daten in den Thread zu bekommen, und globale und Threads sind meistens auch eine Krücke mit ihren ganz eigenen Problemen (x Threas, n Daten)
Bert3 schrieb: > Der Thread muss 0 Ahnung davon haben voher sein Daten kommen - daher die > DatenKapsel-Klasse - Dagegen habe ich doch gar nichts ... > wie das dann Ausserhalb verwaltet wird muss der > Thread-Proc 100% egal sein, Und deswegen sollte kein Abhängigkeit eingebaut werden, die Du mit der Variante der lokalen Variablen eben bekommst. > ein Singleton zum leichten Zugriff ist hier > definitiv nicht nötig weil es einfach trivial ist die Daten in den > Thread zu bekommen, und globale und Threads sind meistens auch eine > Krücke mit ihren ganz eigenen Problemen (x Threas, n Daten) entweder hat man gem. DS oder nicht. Wenn die Threads mit Kopien auskommen, ist ja alles gut, aber das wissen wir nicht ...
>Nein, es gibt keine dangling-pointer -> großer Unterschied!!!
wo soll denn da ein dangling-pointer herkommen?
Singleton + Thread ist eine scheinbar einfache Lösung für ein
nicht-existentes Problem das noch mehr Probleme macht - egal wie die
reale Situation bei IngC++ ist
Bert3 schrieb: >>Nein, es gibt keine dangling-pointer -> großer Unterschied!!! > > wo soll denn da ein dangling-pointer herkommen? Immer noch nicht kapiert? Der Code ist nicht refaktorierungsfest!
Bert3 schrieb: >>Nein, es gibt keine dangling-pointer -> großer Unterschied!!! > > wo soll denn da ein dangling-pointer herkommen? > > Singleton + Thread ist eine scheinbar einfache Lösung für ein > nicht-existentes Problem Das Problem liegt offenbar vor! > das noch mehr Probleme macht Wo? Welche? > - egal wie die > reale Situation bei IngC++ ist Die wir gar nicht kennen ...!!!
>Das Problem liegt offenbar vor! Wo schreibt er das? er hat nur ein Problem mit einem trivial-cast - den ich vermeiden würden >>das noch mehr Probleme macht >Wo? Welche? wenn du n Threads mit m Daten hast - bekommst du entweder n Singletons oder ein Singleton das n verwalten kann => Locking, Daten-Reset-Verwaltung, ... es ist eben ein sehr zustandsbehaftetes Konstrukt = was man bei Threading so stark wie möglich vermeiden sollte - Scope Probleme löst man nicht in dem man einfach globale Zustandsobjekte einbaut - selbst nicht als mögliche Lösung nennen alleine das du Singleton im Zusammenhang mit Threads geschrieben hast lässt mich ein wenig an deiner Erfahrung zweifeln, dann noch der "dangling-pointer" hinterher - wirkt (sicherlich ungewollt) bastlerisch >Die wir gar nicht kennen ...!!! da geben ich dir recht - soll er mal kommen
Bert3 schrieb: >>Das Problem liegt offenbar vor! > > Wo schreibt er das? er hat nur ein Problem mit einem trivial-cast - den > ich vermeiden würden > >>>das noch mehr Probleme macht >>Wo? Welche? > > wenn du n Threads mit m Daten hast - bekommst du entweder > n Singletons oder ein Singleton das n verwalten kann > => Locking, Daten-Reset-Verwaltung, Das hat man eh, wenn es shared data ist!!! ... es ist eben ein sehr > zustandsbehaftetes Konstrukt = was man bei Threading so stark wie > möglich vermeiden sollte - Scope Probleme löst man nicht in dem man > einfach globale Zustandsobjekte einbaut - selbst nicht als mögliche > Lösung nennen aber auch nicht, indem man es ignoriert (s.a. stabiler Code gegen Refactorierngsauswirkungen, Exception-Safety, in Deinem Vorschlag nicht gegeben) > alleine das du Singleton im Zusammenhang mit Threads geschrieben hast > lässt mich ein wenig an deiner Erfahrung zweifeln, dann noch der > "dangling-pointer" hinterher - wirkt (sicherlich ungewollt) bastlerisch Wenn einem die fachlichen Argumente fehlen ... Nun, das würde ich genauso zurück geben.
>> wenn du n Threads mit m Daten hast - bekommst du entweder >> n Singletons oder ein Singleton das n verwalten kann >> => Locking, Daten-Reset-Verwaltung, >Das hat man eh, wenn es shared data ist!!! klar - aber diese data races haben trotzdem nichts mit der Lifetime zu tun >> zustandsbehaftetes Konstrukt = was man bei Threading so stark wie >> möglich vermeiden sollte - Scope Probleme löst man nicht in dem man >> einfach globale Zustandsobjekte einbaut - selbst nicht als mögliche >> Lösung nennen >aber auch nicht, indem man es ignoriert (s.a. stabiler Code gegen >Refactorierngsauswirkungen, Exception-Safety, in Deinem Vorschlag nicht >gegeben) darauf bin ich doch gar nicht eingegangen - und dein Singleton hilft auch nur bei Teilproblemen - aber ist eben ein globales Zustandsobjekt - ich kann mir einfach keine Situation vorstellen wo ein Singleton eine höhere Sicherheit verursacht und auch der Einsatz wird von den meisten Firmen für die ich arbeite massiv eingeschränkt(oder ich argumentiere dagegen) - weil leicht nutzbar eben nicht clean und stabil bedeutet in alle diese Nachteile bin ich schon in dem einen oder anderen Projekt gelaufen: https://de.wikipedia.org/wiki/Singleton_(Entwurfsmuster)#Nachteile mein Lieblingsthema damit: (performante) Absicherung, Skalier- und Testbarkeit - bei den Themen ist ein Singleton immer ganz schnell das schlechteste Pattern >Wenn einem die fachlichen Argumente fehlen >Nun, das würde ich genauso zurück geben. deine Punkte stabiler Code gegen,Refactorierngsauswirkungen, Exception-Safety ignoriere ich nicht - nur wie ein Singleton da besonders gut hilft (ohne schlechte Einflüsse ins Design zu bringen) sehe ich nicht - und Singleton kam von dir
aus dem Wikipedia-Eintrag dazu - und weil er so schön meine Gedanke in Worte fasst: >Wegen der vielen Nachteile wird das Singleton-Muster (und auch das Idiom >Double-checked Locking) mitunter schon als Anti-Pattern bewertet. Für >Fälle, in denen tatsächlich technisch ein passender Bereich für ein >Singleton existiert, können Singletons aber sinnvoll sein – insbesondere >wenn sie sich auf andere „einmalige Strukturen“ wie zum Beispiel eine >Abstract Factory beziehen. Trotzdem: Das korrekte Design von Singletons >ist schwierig – in der Regel schwieriger als Designs ohne Singletons.
IngC++ schrieb: > Hallo Community, > > ich hänge aktuell an einem kleinen Cast Problem. Vielleicht kann mir da > ein C++ Spezialist mal kurz helfen: > > void test() > { > void* x = NULL; > > vector<MyClass*> myClassList; > myClassList.push_back(new MyClass()); > > vector<void*> myList; > myList.push_back(&myClassList); > > x = &myList; > > vector<void*> cast1 = *reinterpret_cast<vector<void*> *>(x); > > vector<MyClass*> cast2 = reinterpret_cast<vector<MyClass*> > *>(cast1[0]); > > > cout << cast2.size(); > > > } > > Für cast2 kommt komischerweise 0 heraus obwohl ein Element drin sein > müsste. Habe ich irgendwo einen Leichtsinsfehler beim casten gemacht? Auch wenn es mit widerstrebt, habe ich Deinen Code mal kompilier- und lauffähig gemacht ...
1 | void* x = nullptr; |
2 | |
3 | vector<MyClass*> myClassList; |
4 | myClassList.push_back(new MyClass()); // leak |
5 | |
6 | vector<void*> myList; |
7 | myList.push_back(&myClassList); |
8 | |
9 | x = &myList; // x : vector<void*>* |
10 | |
11 | vector<void*> cast1 = *reinterpret_cast<vector<void*>*>(x); // cast1 == myList |
12 | |
13 | // vector<MyClass*> cast2 = reinterpret_cast<vector<MyClass*>*>(cast1[0]); // Fehler
|
14 | |
15 | // cast1[0] : void* , ist aber polymorph: MyClass*
|
16 | MyClass* cast2 = reinterpret_cast<MyClass*>(cast1[0]); |
17 | |
18 | cast2->f(); |
19 | |
20 | // cout << cast2.size(); // falsch
|
21 | std::cout << cast1.size() << '\n'; |
Bert3 schrieb: > aus dem Wikipedia-Eintrag dazu - und weil er so schön meine Gedanke in > Worte fasst: > >>Wegen der vielen Nachteile wird das Singleton-Muster (und auch das Idiom >>Double-checked Locking) mitunter schon als Anti-Pattern bewertet. Für >Fälle, Wer redet über double-checked locking? In C++11 ist das nicht nötig! Genau wie dieses ganze Gekröse hier. Wer verwendet heute noch einen Compiler der nur C++98 kann? Schwer vorstellbar. Alle diese Probleme sind in std::thread / std::async gelöst, wenn es denn so low-level sein soll.
Wilhelm M. schrieb: > Auch wenn es mit widerstrebt, habe ich Deinen Code mal kompilier- und > lauffähig gemacht ... Etwas spät. Laufender Code wurde schon am Anfang gepostet^^ Wilhelm M. schrieb: > // cast1[0] : void* , ist aber polymorph: MyClass* > MyClass* cast2 = reinterpret_cast<MyClass*>(cast1[0]); das ist nicht richtig. cast1[0] ist immer noch vector<MyClass*>
nicht"Gast" schrieb: > Wilhelm M. schrieb: >> Auch wenn es mit widerstrebt, habe ich Deinen Code mal kompilier- und >> lauffähig gemacht ... > > Etwas spät. Laufender Code wurde schon am Anfang gepostet^^ Ja, stimmt, sorry, hatte ich ganz überblättert. > > Wilhelm M. schrieb: >> // cast1[0] : void* , ist aber polymorph: MyClass* >> MyClass* cast2 = reinterpret_cast<MyClass*>(cast1[0]); > > das ist nicht richtig. cast1[0] ist immer noch vector<MyClass*> Auch da hast Du recht! Nochmal sorry für die Verwirrung.
>Wer verwendet heute noch einen >Compiler der nur C++98 kann? Schwer vorstellbar. Targets von meinen Kunden sind z.B. VXWorks und QNX mit gcc 4.1.2 oder ältere EDLKs für embedded mit gcc 4.0.x oder ältere Redhats richtig schlimm ist immer der Wechsel zwischen den verschiedenen Kunden die unterschiedliche Standards vorgeben, teilweise wandel ich täglich zwischen C++98 bis C++14, mit gcc, clang und VStudio - und Exotenkompiler für Solaris, oder auch mal mit Boost, gar kein Boost usw. - sehr schön
Bert3 schrieb: > Ich denke er meint wohl eher C++03 Mmmh, dann würde ich ihm empfehlen sich entsprechende RAII-Wrapper ala std::thread, ... selbst zu schreiben. Und ein Ressourcenmgmt via selbstgebaute SmartPtr, sofern er eben nicht Boost, poco, o.ä. verwenden kann/will.
>Mmmh, dann würde ich ihm empfehlen sich entsprechende RAII-Wrapper ala >std::thread, ... selbst zu schreiben. bietet sich an >Und ein Ressourcenmgmt via >selbstgebaute SmartPtr, sofern er eben nicht Boost, poco, o.ä. verwenden >kann/will. wenn er angstbefreit ist kann er ja auto_ptr verwenden :)
Bert3 schrieb: >>Mmmh, dann würde ich ihm empfehlen sich entsprechende RAII-Wrapper ala >>std::thread, ... selbst zu schreiben. > > bietet sich an > >>Und ein Ressourcenmgmt via >>selbstgebaute SmartPtr, sofern er eben nicht Boost, poco, o.ä. verwenden >>kann/will. > > wenn er angstbefreit ist kann er ja auto_ptr verwenden :) Naja, dann braucht er später auch nicht auf C++11 zu migrieren ... ;-))
für jede WinCE Entwicklung ist auch mit VS2008 und C++03 das Ende erreicht mehr geht nicht - und es gibt sau viele die noch mit WinCE Geräten arbeiten/anbieten/entwickeln
Bert3 schrieb: > für jede WinCE Entwicklung ist auch mit VS2008 und C++03 das Ende > erreicht mehr geht nicht - und es gibt sau viele die noch mit WinCE > Geräten arbeiten/anbieten/entwickeln Herzliches Beileid!
>Herzliches Beileid!
bin nicht zu stark betroffen :)
musste nur mal eine komplette C+11 Entwicklung (mit shared/unique-ptr,
lambdas usw., ca. 200k LOC) auf WinCE backporten - ein riesen Spass
Bert3 schrieb: >>Herzliches Beileid! > > bin nicht zu stark betroffen :) > > musste nur mal eine komplette C+11 Entwicklung (mit shared/unique-ptr, > lambdas usw., ca. 200k LOC) auf WinCE backporten - ein riesen Spass Und ich freue mich seit geraumer Zeit über Concepts / Constraints, und mag gar nicht mehr ohne, und warte ungeduldig, dass auch modules (zumindest als lite version wie im clang) im gcc Einzug halten ;-)
und conditions, usw. alles was <thread> so hergibt - Grund: man hatte anders geplant, wollte die "alte" Platform nicht mehr supporten, dann kam ein Kunde, mit ordentlich Geld - los gehts!
Bert3 schrieb: > und conditions, usw. alles was <thread> so hergibt - Grund: man hatte > anders geplant, wollte die "alte" Platform nicht mehr supporten, dann > kam ein Kunde, mit ordentlich Geld - los gehts! Naja, die Bibliothekssachen kann man zur Not noch selbst bauen. Aber Concepts und auch z.B. Variadic Templates odet constexpr sind eben neue Sprachfeatures.
Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.