Problem mit generischem C++ Code
-
Hallo C++ Community,
ich habe eine umfangreiche Klasse X mit etwa 50 Optionseinstellungen für eine Anwendung. Der Standardkonstruktor soll diese 50 Objekte beim Erzeugen eines Objektes von X initialisieren. Dafür muss ich alle Einstellungsobjekte in der Initialisierungsliste des ctor von X auflisten.
Nun kann es aber sein, dass weitere benötigte Einstellungen hinzu kommen und der Eintrag im ctor von X vergessen wird, deshalb hätte ich gern nur die Objekte in X als "Codequelle".
Ich habe mir am WE die Zähne daran ausgebissen, etwas aus Makros zu bauen, dass die Liste im ctor von X automatisch füllt.
Leider ohne Erfolg

Gibt es irgendwelche Design Tricks, mit denen so etwas realisierbar ist ? oder zusätzliche Tools, soetwas wie ein Präprozessor ?
Der Intelli-Sense sollte allerdings damit arbeiten können ...
Danke für eure Hilfe
-
Ja, das könnte man bestimmt irgendwie mit dem Präprozessor zusammenfrickeln, aber hier der supertolle Designtipp:
Fass Einstellungen nach sinnvollen Kategorien zu Klassen zusammen. Zwei Vorteile: Erstens keine krankhaft lange Parameterliste mehr. Zweitens keine Möglichkeit mehr versehentlich Parameter zu vergessen, weil das Typensystem dich davor schützt.
Ach ja, und dein eigentliches Problem behebt es auch.
-
SeppJ schrieb:
Fass Einstellungen nach sinnvollen Kategorien zu Klassen zusammen. Zwei Vorteile: Erstens keine krankhaft lange Parameterliste mehr.
Ach ja, und dein eigentliches Problem behebt es auch.
Hört sich mir eher nach einem Anti-Pattern an:
-
@SeppJ: Danke für deinen Vorschlag... ich möchte die Klasse X aber nicht weiter zerteilen, da die Objekte von X auch noch serialisiert werden sollen...
Hat sonst jemand Erfahrungen mit generischem C++ Code ?????
-
Hast du mal dran gedacht, die Klasse durch ein Script (z.B. in Python) zu generieren?
-
@Bashar: Ja, daran habe ich schon gedacht ... zwei quelltexte sind aber auch nicht einfacher zu handeln ... ich suche etwas wie einen Präprozessor, den man in den Pre-Build einbinden kann ... leider ist der C++ Präprozessor aber nicht "intelligent" genug, um schleifen auszuführen, die lange parameterlisten parsen und verarbeiten ....
-
Hat wirklich keiner sonst noch eine Idee ?

-
pro_develop schrieb:
@SeppJ: Danke für deinen Vorschlag... ich möchte die Klasse X aber nicht weiter zerteilen, da die Objekte von X auch noch serialisiert werden sollen...
Das ist kein Argument.
-
volkard schrieb:
pro_develop schrieb:
@SeppJ: Danke für deinen Vorschlag... ich möchte die Klasse X aber nicht weiter zerteilen, da die Objekte von X auch noch serialisiert werden sollen...
Das ist kein Argument.
Ich würde sogar sagen, dass ist ein Argument für das Zerteilen.
-
Hallo
Beim Erfassen von Einstellungen unterscheide ich immer zwischen ein paar Werten oder größeren Mengen von Werten. Bei 50 Einstellungen finde ich perönlich ein dynamisches Verfahren mit Schlüssel-Wert Paaren sinnvoller als eine statische Struktur. Eine std::map<std::string, std::string> ist in der Lage wirklich alles zu speichern und hat den Vorteil, dass man den Inhalt etwa als XML importieren und exportieren kann. Eine geeignete Zugriffsfunktion hilft hier sehr:
typedef std::map<std::string, std::string> StringMap; StringMap _strmap; std::string get_setting(std::string const & key) { StringMap::iterator iter = _strmap.find(key); if(iter == _strmap.end()) return std::string(); else return iter->second; } std::string get_setting(std::string const & key, std::string const & defaultvalue) { StringMap::iterator iter = _strmap.find(key); if(iter == _strmap.end()) return defaultvalue; else return iter->second; }Und nur wenn mich jetzt keiner dafür auslacht ... hier eine andere "generische" Möglichkeit (die aber sicher nicht standard-konform ist, aber unter MSVC funktioniert

template<class T, unsigned OFFSET> struct DefaultValue { static const bool IsDefined = false; }; #define DEFINE_DEFAULT_VALUE(structname, membertype, membername, membervalue) \ template<> struct DefaultValue< structname, offsetof(structname, membername) > \ { \ static const bool IsDefined = true; \ typedef membertype Type; \ static const membertype Value; \ }; \ const membertype DefaultValue<structname, offsetof(structname, membername) >::Value(membervalue); template<class T, unsigned OFFSET, bool DEFINED> struct DoInit; template<class T, unsigned OFFSET> struct DoInit<T, OFFSET, true> { static void Do(T* ptrStruct) { typedef typename DefaultValue<T, OFFSET>::Type MemberType; MemberType * ptrMember = reinterpret_cast<MemberType*>(reinterpret_cast<char *>(ptrStruct) + OFFSET); *ptrMember = DefaultValue<T, OFFSET>::Value; } }; template<class T, unsigned OFFSET> struct DoInit<T, OFFSET, false> { static void Do(T* ptrStruct) { } }; template<class T, unsigned OFFSET> struct TryInit { static void Do(T* ptrStruct) { DoInit<T, OFFSET, DefaultValue<T, OFFSET>::IsDefined>:: Do(ptrStruct); TryInit<T, OFFSET - 1>:: Do(ptrStruct); } }; template<class T> struct TryInit<T, 0> { static void Do(T* ptrStruct) { DoInit<T, 0, DefaultValue<T, 0>::IsDefined>:: Do(ptrStruct); } }; template<class T> struct Init : public T { Init() { TryInit<T, sizeof(T)>:: Do(this); } };Und so funktioniert's:
Die Initialisierungs-Werte sind in der template-Spezialisierung DefaultValue mit dem member-offset als Parameter gespeichert (Es hat mich echt gewundert, dass offsetof statisch ausgewertet werden kann :p ) Das Init-Template läuft nun alle offsets von sizeof(T) bis 0 durch und versucht an dieser Stelle einen Wert zu schreiben, falls einer definiert wurde:struct X { int a; int b; std::string s; }; DEFINE_DEFAULT_VALUE(X, int, a, 24); DEFINE_DEFAULT_VALUE(X, int, b, 42); DEFINE_DEFAULT_VALUE(X, std::string, s, "Hello World"); int main() { Init<X> x; }Der Code soll hier aber nur als Beispiel dienen, dass es möglich ist Member über Meta-Klassen zu finden. Ich würde das aber in der Praxis nicht so machen

Dieses Beispiel war ein Experiment für struct-Serialisierung, wobei ich es aber dann sinnvoller empfand, die Member zu numerieren und der Metaklasse eine Accessor-Methode für den Member zu geben ... dafür muss aber die Numerierung stabil bleiben, was beim ständigen erweitern von Strukturen aufwendig wird ... vor allem bei über 50 Member
... alles in allem: Metacode ist cool
aber komplexlg XOR
-
Hallo xor,
danke für deine Antwort.
Dein erster Vorschlag, war auch mein Ansatz. Aber damit habe ich ja den Intelli-Sense unter MSVC nicht zur Verfügung, selbst wenn ich Operatoren überlade, um die einzelnen Elemente zu selektieren.
z.B.
Einstellungen.Port = 12; Einstellungen.Baudrate = 9600; Einstellungen.InterfaceName = "Serial Interface to AVR Board"Bei dem zweiten Beispiel hat sich mir die Frage aufgedrängt, wie man so ein komplexes Gebilde noch administrieren kann ????
Da muss ich mich ja erst wieder ne Stunde einlesen, falls ich die Struktur ändern möchte
Ein Kollege hat mir hier heute noch einen Tipp gegeben... das geht aber in den Aufbau des Maschinencodes hinein ... dabei müsste ich den Aufbau der Klasse aus dem Code lesen und dann die Adressen der Member Variablen zum Zugriff verwenden ... klingt auch ziemlich heftig ...
Hat jemand mit dem Thema (Reflections) Erfahrungen ?
-
Hallo.
Gegen das vergessen der Initialisierung.
Hier hilft eine kleine Klasse die einen Parameter besitzt und über einen Konstruktor 'zwangsweise' initialisiert werden muss.
der operator () hilft dann das man transparent auf die Typen zugreifen kann wie eigentlich vorgesehen.
Vielleicht ist es das was Du suchst.Versuch mal im Beispiel in der Classe cOptions noch so ein Konstrukt anzulegen 'OHNE' in zu Initialisieren. Da hat der Compiler aber schwer was dagegen.

Lichtlein
template< typename t > class cParaMustInit { public: cParaMustInit( const t T ) : mT(T) {} t operator () () { return mT; } private: cParaMustInit(); cParaMustInit(const cParaMustInit &); t mT; }; class cOptions { public: cOptions(int Baudrate, char* Name) : mBaudrate(Baudrate), mName(Name) {} cParaMustInit<int> mBaudrate; cParaMustInit<char*> mName; }; int main(int argc, char *argv[]) { cOptions Options(10, "huhu"); Options.mBaudrate = 10; Options.mName = "Jetzt mal ein Name"; return 0; }