G
knivil schrieb:
Wo ist der Vorteil gegenueber einem einfachen Ueberladen der Streamoperatoren?
Die ganzen Konvertierungsgeschichten sollen zwar weitestgehend unabhängig sein (für den Fall, dass ich sie später nochmal brauche), aber ich benutzte sie im Moment hauptsächlich für meine Ini library. Dabei brauche ich eben Konvertierungen von und zu strings.
Ich hab zuerst einfach die stream operatoren benutzt. Das Problem fing dann bei strings an: der >> operator liest nur bis zum nächsten Leerzeichen. Weiterhin wollte ich sowohl std::wstring also auch std::string unabhängig von dem benutzten Charset unterstützen. Dann ging es weiter mit char und wchar_t und signed und unsigned char. >> für std::wstring z.B. zu überladen geht nicht, weil die STL bereits einen vorgibt.
Das ganze war als eine (nicht Klassen) Templatefunktion implementiert, welche dann von der Ini-Klasse die Schreib/Lese-Funktion aufrief, nach dem die Konvertierung vollzogen wurde.
Da ich es furchtbar fand, immer diese Funktion zu überladen (für wstring, string, etc.) (es stand noch ein bisschen mehr drin), hab ich die Konvertierung ausgelagert: eben in ConvertFromString() und ConvertToString().
Weiterhin gibt es immer noch eine Version von ConvertFromString() und ConvertToString(), welche die Stream operatoren aufrufen.
Der Client kann dann also selbst entscheiden, ob er >> bzw. << oder eben die Konvertierungsfunktionen überläd.
Ich hab mir auch gedacht, dass man evtl. nur für die Ini-Lib solch eine Konvertierung möchte, und bei anderen "Sachen" eben eine andere. Wenn diese "anderen" Sachen jetzt den stream operator benutzten, und man diesen für die Ini-Lib überladen hat, geht das nicht mehr.
----
knivil schrieb:
Hmm, mir ist das irgendwie zuviel Code.
Meinst du, dass ich zu viel Code gepostet habe? Sorry
knivil schrieb:
Zumal ich gerade nicht den Sinn hinter dem ganzen sehe, da bei der Nutzung deiner Funktionen dem Anwender keinerlei Arbeit abgenommen wird. Fuer jede Klasse muss trotzdem die Serialisierung haendisch implementiert werden, nur dass jetzt deine tollen Makros benutzt werden.
Serialisierungen müssen doch in C++ immer per Hand implementiert werden, weil so was wie Reflection fehlt?
Und ich weiß nicht, aber für mich ist das eine deutige Arbeitserleichterung, wenn ich statt
template <class StrCls>
void ConvertToString(const STestStruct& Input, StrCls& Output)
{
using namespace Conversion;
Output = ConvertFromString<std::string, StrCls>("("); // Gut, kann man noch in eine eigene Funktion packen...
StructVariableToString(_T("TestInt"), Input.TestInt, Input, Output);
StructVariableToString(_T("TestBool"), Input.TestBool, Input, Output);
StructVariableToString(_T("TestWStr"), Input.TestWStr, Input, Output);
StructVariableToString(_T("NestedStruct"), Input.NestedStruct, Input, Output);
StructVariableToString(_T("CustomNameIntVect"), Input.TestIntVector, Input, Output);
StructVariableToString(_T("TestNest2Deque"), Input.TestNest2Deque, Input, Output);
EndStructToStrConversion(Output);
}
template <class StrCls>
void ConvertFromString(const StrCls& StrValue, STestStruct& Output)
{
using namespace Conversion;
CheckStructLikeStringForErrors(StrValue, Output);
StructVariableFromString(StrValue, _T("TestInt"), Output, Output.TestInt);
StructVariableFromString(StrValue, _T("TestBool"), Output, Output.TestBool);
StructVariableFromString(StrValue, _T("TestWStr"), Output, Output.TestWStr);
StructVariableFromString(StrValue, _T("NestedStruct"), Output, Output.NestedStruct);
StructVariableFromString(StrValue, _T("CustomNameIntVect"), Output, Output.TestIntVector);
StructVariableFromString(StrValue, _T("TestNest2Deque"), Output, Output.TestNest2Deque);
}
folgendes schreiben kann:
BDC_BEGIN_BIDIR_STRUCT_STRING_CONVERSION(STestStruct)
BFC_VAR_CONVERSION_WSN(TestInt);
BFC_VAR_CONVERSION_WSN(TestWStr);
BFC_VAR_CONVERSION_WSN(TestBool);
BFC_VAR_CONVERSION_WSN(NestedStruct);
BFC_VAR_CONVERSION(_T("CustomNameIntVect"), TestIntVector);
BFC_VAR_CONVERSION_WSN(TestNest2Deque);
BDC_END_BIDIR_STRUCT_STRING_CONVERSION()
knivil schrieb:
Was ist, wenn ich in meinen Klassen Member habe, die nicht von deinen Makros abgedeckt werden?
Falls du Set-Funktionen meinst, dann müsste ich entweder noch solch ein Makro hinzufügen, oder die Konvertierungsfunktion muss als friend deklariert werden, oder man darf die Makros einfach nicht verwenden?
Falls du weitere Structs, Klassen, etc. meinst, dann müssen weitere ConvertFromString()/ConvertToString() Versionen erstellt werden, für denn Fall, dass man diese Membervariable mit im String hat.
Ist das falsch?
knivil schrieb:
Nennt sich Refactoring und das ist bitter noetig. Vielleicht ist das ein Fall fuer The daily WTF
Das Problem: Ich hab leider keine Ahnung, was ich jetzt genau wie verbessern könnte. Ich bin kein professioneller Programmierer, und hab bloß ein paar Bücher gelesen, und versuche, Vorschläge wie sie z.B. in Code Complete oder Meyers vorkommen zu beherzigen.
Wie soll ich das jetzt in dem konkreten Fall das umgestalten??
Stream operatoren überladen finde ich aus den genannten Gründen hier ungünstig.
Ich könnte noch die beiden Konvertierungsfunktionen zusammenlegen, aber dann geht "const" bei dem Inputobject verloren, und ich hab ein "if" drin.
Ein zusätzlicher bool-template-parameter würde letztendlich auch in 2 verschiedenen Funktionen (halt mit selben Namen) resultieren, und hätte noch den Nachteil, dass ich für die bool-Spezialisierung alls template-parameter mit spezialisieren müsste.
Mehr Ideen hab ich nicht...
Du sagst:
knivil schrieb:
Wenn das so ist, dann gibt es elegantere Methoden in C++
aber ich hab leider keine Ahnung, welche das sein könnten...
Ich verstehe einfach nicht, was ihr jetzt konkret ändern würdet
Ich will nicht um jeden Preis an meinem Design festhalten (insbesondere nicht, da es scheinbar schlecht zu sein scheint), aber ich komm einfach nicht drauf, wie man es besser machen könnte
Darum stelle ich diese nervigen (und scheinbar uneinsichtigen) Fragen...