const Char* geht nicht
-
Ich lese aus XML-Dateien Resources ein. Das sind jede Menge unterschiedliche Klassentypen an die 100 Stück. Zusätzlich muss die Lib von Endanwendern noch an Klassentypen erweiterbar sein. Diese kommen Aufgrund der puren Vielzahl an unterschiedlichen Klassen alle in einen Vector der Zeiger vom Typ Base (der allen Klassen gemeinsamen Basisklasse) hält. Jetzt soll es natürlich auch Strings, Doubles, Ints, ... und eben auch Chars als Resourcen geben. Das geht aber nur wenn auch sie alle von der gleichen Basis abgeleitet sind. Darum die Wrapper-Klassen. Im Moment ist meine String-Klasse noch ein Wrapper um wchar_t. Es ist aber nur konsequent hier nun meine eigene Char-Klasse zu nehmen.
Und jetzt kommt mir bitte nicht mit das kann man auch mit normalen C++-Mitteln machen oder du benutzt die falsche Programmiersprache. Natürlich kann man das mit den normalen C++-Mitteln auch schaffen, aber ich möchte in disem Leben nochmal mit dem Programm fertig werden. Ich warte ja nur darauf, dass jetzt noch die Leute aus dem Assembler-Forum kommen und mir raten, dass kann man das mit Assembler-Mitteln sogar noch eleganter als mit C++ erledigen kann ;). Natürlich kann man das. Ist halt alles eine Frage des Codeumfangs bzw. der Zeit.
-
Dann willst du keine char Klasse sondern eine String Klasse.
Und alle Probleme sind gelöst.
-
Vielleicht ist das ein Anwendungsfall von boost::any.
-
Streamer schrieb:
Ist halt alles eine Frage des Codeumfangs bzw. der Zeit.
Und genau aus diesem Grund solltest du auch Code wiederverwenden. In deinem Fall die String-Klasse
std::wstring. Du kannst ja immer noch eine Klasse darum herumkapseln, die von der gemeinsamen Basis erbt. Aber die gesamte Zeichenkettenfunktionalität auf der Grundlage vonwchar_t*neu zu implementieren... Naja.314159265358979 schrieb:
Vielleicht ist das ein Anwendungsfall von boost::any.
Kaum.
-
Nexus schrieb:
Kaum.
Gut, dann halt nicht. ^^
Ich habe boost::any noch nie gebraucht, war nur mal geraten.
-
@314159265358979
Das hatte ich auch schonmal durchgespielt finde die derzeitige Lösung allerdings nach anfänglicher Skepsis wegen den Wrapper-Klassen um die skalaren Datentypen ziemlich gut.@Shade Of Mine
Im Prinzip hast du recht. Mich wurmt aber, dass dieses blödeconst Char* c = L"AAAAAA";nicht geht.
Char charArray[6] = {L'A', L'A', L'A', L'A', L'A', L'A'}; Char* c = charArray;ist ja nun wirklich niemanden zuzumuten. Ich selber brauche das auch gar nicht, da ich in der Lib selber nur mit Strings arbeite. Aber vielleicht will ein Anwender die Char-Klasse mal so verwenden. Ein anderes Problem ist die Schnittstelle zur Win32-API wo ja noch viel mit const char* gearbeitet wird. Aber gut da könnte ich dann wie du schon gesagt hast mit meiner String Klasse eben eine entsprechende Umwandlung von String nach const char* anbieten.
Allerdings hat Nexus ja die Kapselung angemahnt. D.h. innerhalb meiner String-Klasse, die ein Wrapper um die Char-Klasse darstellt, hätte ich ja dann gar keinen Zugriff mehr auf den wchar_t* Member der Char-Klasse. Eine Rückgabe dessen durch die String-Klasse wäre so nicht mehr möglich, oder doch? Alles ziemlich verwirrend.
-
Was spricht denn gegen
const wstring c = L"AAAA";?
Du willst schließlich einen String kapseln und kein Zeichen.
-
[quote="Nexus"]Und genau aus diesem Grund solltest du auch Code wiederverwenden. In deinem Fall die String-Klasse
std::wstring. Du kannst ja immer noch eine Klasse darum herumkapseln, die von der gemeinsamen Basis erbt. Aber die gesamte Zeichenkettenfunktionalität auf der Grundlage vonwchar_t*neu zu implementieren... Naja.
Aber wenn ich sowieso schon alle Klassen wrappe dann kann ich sie auch gleich (zumindest im Fall der String-Klasse) selber implementieren, zumal ich in gewissen Scenarien mit der std::wstring Implementation eh nicht zufrieden bin. Z.B. beim Auffinden von Substrings in sehr langen Zeichenketten. Hier habe ich nun die ideale Gelegenheit z.B. den Boyer-Moore-Algorithmus in die Substring-Funktion zu integrieren. Außerdem finde ich den Codestyle der Standard Library zusammen mit dem Template-Fetischismus nicht sonderlich ansehlich. Zumindest integriert es sich schlecht in meine Library weshalb ich die Standard Library nach Möglichkeit nicht verwende.
-
Streamer schrieb:
Im Prinzip hast du recht. Mich wurmt aber, dass dieses blöde
const Char* c = L"AAAAAA";nicht geht.
Mich nicht. Der Code macht so absolut keinen Sinn. Du versteifst dich auf Syntax. Das ist falsch. Du musst in Konzepten denken. Syntax ist nur Zucker zum drüber streuen. Nimm einfach
CharPtr c = L"AAAAAA";Warum muss es Char* sein?
Vergiss die Syntax, denke daran was du Konzeptionell willst. Und für mich klingt es stark danach, dass du einen String haben willst. Dass in C strings über char* implementiert sind, ist ein Syntax Detail. Das Konzept String ist unabhängig von Zeigern.
-
Streamer schrieb:
Zumindest integriert es sich schlecht in meine Library weshalb ich die Standard Library nach Möglichkeit nicht verwende.
Furchtbar schlechte Idee.
When in Rome do as the Romans do.
-
Manchaml möchte man aber auch eine Zeichenkette Zeichen für Zeichen auswerten können. Z.B. wenn man eine Xml-Datei parst. So brauche ich nicht den gesamten String/Vektor an die Funktionen zu übergeben sondern nur den Zeiger auf die aktuelle Position.
void ÜberspringeLeerzeichen(Char*& c) { while (*c == L' ') { ++c; } } int main() { Char* c = L"Hier sollen warum auch immer alle Leerzeichen übersprugen werden!"; ÜberspringeLeerzeichen(c); }
-
Wieso muss nochmal alles von einer gemeinsamen Basisklasse abgleitet werden?
-
Streamer schrieb:
Manchaml möchte man aber auch eine Zeichenkette Zeichen für Zeichen auswerten können. Z.B. wenn man eine Xml-Datei parst. So brauche ich nicht den gesamten String/Vektor an die Funktionen zu übergeben sondern nur den Zeiger auf die aktuelle Position.
void ÜberspringeLeerzeichen(Char*& c) { while (*c == L' ') { ++c; } } int main() { Char* c = L"Hier sollen warum auch immer alle Leerzeichen übersprugen werden!"; ÜberspringeLeerzeichen(c); }Und das kannst du mit std::string nicht?
-
Streamer schrieb:
Manchaml möchte man aber auch eine Zeichenkette Zeichen für Zeichen auswerten können. Z.B. wenn man eine Xml-Datei parst. So brauche ich nicht den gesamten String/Vektor an die Funktionen zu übergeben sondern nur den Zeiger auf die aktuelle Position.
Schau dir mal das Konzept "Iterator" an.
Das ist genau das was man in so einer Situation verwendet.
-
314159265358979 schrieb:
Und das kannst du mit std::string nicht?
Ja doch natürlich, ich will es eben aber auch mit meiner Char-Klasse können. Zudem müsste ich mal testen ob das ganze mit einem std::wstring noch genauso schnell ist.
camper schrieb:
Wieso muss nochmal alles von einer gemeinsamen Basisklasse abgleitet werden?
Damit ich anstatt
class ResourceManager { protected: std::vector<MyClass1> myClass1Storage; std::vector<MyClass2> myClass2Storage; std::vector<MyClass3> myClass3Storage; ... std::vector<MyClass100> myClass100Storage; };dies hier schreiben kann
class ResourceManager { protected: std::vector<MyBaseClass*> myClassStorage; };und wegen der Erweiterbarkeit natürlich.
-
Streamer schrieb:
Ja doch natürlich, ich will es eben aber auch mit meiner Char-Klasse können. Zudem müsste ich mal testen ob das ganze mit einem std::wstring noch genauso schnell ist.
Die Iterationsgeschwindigkeit ist unabhängig vom darunterliegenden Typ :p
-
314159265358979 schrieb:
Die Iterationsgeschwindigkeit ist unabhängig vom darunterliegenden Typ :p
Das ist ja schonmal gut zu wissen. Ob das ganze aber schneller ist wie ein roher Zeiger den ich über ein Array rattern lasse bezweifel ich. Es wird sich wahrscheinlich beides nicht viel geben.
Was ich halt vermeiden möchte ist ein absoluter Typen-Mischmasch. Da mal String und Char dort wieder std::wstring bzw. wchar_t. An anderer Stelle wieder const wchar_t* wegen Win32-API. Wenn ich sowieso eine Char/String Klasse brauche nehme ich auch die.
-
Streamer schrieb:
Was ich halt vermeiden möchte ist ein absoluter Typen-Mischmasch. Da mal String und Char dort wieder std::wstring bzw. wchar_t. An anderer Stelle wieder const wchar_t* wegen Win32-API. Wenn ich sowieso eine Char/String Klasse brauche nehme ich auch die.
Dann wrappe std::string nur. Verdopple keine Funktionalitaet. Und so kannst du auch easy in deinem Resourcen Manager eine Polymorphe Stringklasse haben aber nach aussen fast nur std::string hergeben.
-
Streamer schrieb:
Aber wenn ich sowieso schon alle Klassen wrappe dann kann ich sie auch gleich (zumindest im Fall der String-Klasse) selber implementieren, zumal ich in gewissen Scenarien mit der std::wstring Implementation eh nicht zufrieden bin. Z.B. beim Auffinden von Substrings in sehr langen Zeichenketten. Hier habe ich nun die ideale Gelegenheit z.B. den Boyer-Moore-Algorithmus in die Substring-Funktion zu integrieren.
Genau das kannst du wunderbar mit einer Wrapperklasse um
std::wstringtun. Die STL-Klassen bieten alle eine Iterator-Schnittstelle an, mit der du sehr ähnlich wie mit Zeigern arbeiten kannst, nur auf einer höheren Abstraktionsebene. Du kannst beliebige Algorithmen definieren, die auf einemstd::wstringoperieren. Das ist noch lange kein Grund, all die Mühseligkeiten (manuelle Speicherverwaltung, Kopiersemantik, separate Grössenspeicherung) eineswchar_t*auf sich zu nehmen.Streamer schrieb:
Außerdem finde ich den Codestyle der Standard Library zusammen mit dem Template-Fetischismus nicht sonderlich ansehlich. Zumindest integriert es sich schlecht in meine Library weshalb ich die Standard Library nach Möglichkeit nicht verwende.
Arbeite nicht gegen die Standardbibliothek, sondern mit ihr. Du kannst ja immer noch kapseln. Aber die STL ist konzeptionell wirklich enorm durchdacht, du solltest dich nicht von den Templates abschrecken lassen, nur weil du bisher vor allem andere Abstraktionsmechanismen (Vererbung und Polymorphie) kennengelernt hast.
-
Streamer schrieb:
camper schrieb:
Wieso muss nochmal alles von einer gemeinsamen Basisklasse abgleitet werden?
Damit ich anstatt
class ResourceManager { protected: std::vector<MyClass1> myClass1Storage; std::vector<MyClass2> myClass2Storage; std::vector<MyClass3> myClass3Storage; ... std::vector<MyClass100> myClass100Storage; };dies hier schreiben kann
class ResourceManager { protected: std::vector<MyBaseClass*> myClassStorage; };und wegen der Erweiterbarkeit natürlich.
Und was kann diese Basisklasse? Und wie greifst du dann auf die Einzelelemente zu? dynamic_cast oder typeid-Orgien?
Ich hatte eigentlich den Eindruck, dass Container aus Basisklassenzeigern in C++ schon seit 15 Jahren aus der Mode gekommen sind. Weil sie langsam und unwartbar sind, und ganz allgemein nicht wirklich funktionieren. (Genauer: Container, in denen die Einzelelemente nur zu dem Zweck, sie in Containern zu speichern, von einer Basisklasse abgeleitet werden).