Wie wird eine Klasse mit "annotierten" Feldern entworfen?

9

Stellen Sie sich vor, wir haben eine Art Protokoll mit Hunderten von Nachrichtentypen, von denen jeder von einer C ++ - Klasse modelliert werden soll. Da jede Klasse in der Lage sein sollte, jedes Feld automatisch zu verarbeiten, ist es eine natürliche Lösung, nur ein std::tuple mit allen erforderlichen Typen zu haben:

%Vor%

Das ist alles in Ordnung und gut. Nun möchte ich jedoch jedem Feld einen Namen geben, und ich möchte in der Lage sein, den Namen zu verwenden, wenn ich auf das Feld in meinem Code verweise, sowie eine textliche Darstellung davon zu erhalten. Naiv oder in C hätte ich vielleicht geschrieben:

%Vor%

Auf diese Weise verlieren wir die rekursive automagische Verarbeitungsleistung des Tupels, aber wir können jedes Feld wörtlich benennen. In C ++ können wir beide mittels einer enum machen:

%Vor%

Nun kann ich sagen, Message m; m.get<Message::header>() = 12; usw., und ich kann über die Felder rekrutieren und jedem Ausdruck einen eigenen Wert geben, dem ein eigener Name vorangestellt ist, usw.


Nun die Frage: Wie kann ich solchen Code effizient und ohne Wiederholung erstellen?

Idealerweise möchte ich das sagen können:

%Vor%

Gibt es einen Weg, Preprocessor, Boost und C ++ 11 zu kombinieren, um so etwas zu erreichen, ohne externe Generierungswerkzeuge? (Ich denke, Boost.Preprocessor nennt diese "horizontale" und "vertikale" Wiederholung. Ich muss die Felddaten irgendwie "transponieren".) Das Schlüsselmerkmal hier ist, dass ich nie irgendeine der Informationen wiederholen muss, und das Modifizieren oder Hinzufügen Ein Feld erfordert nur eine einzige Änderung.

    
Kerrek SB 27.03.2012, 20:20
quelle

4 Antworten

3

Sie können dies mit den Präprozessorsequenzen von boost machen.

%Vor%

Sie müßten über jedes Paar iterieren, um die Definitionen zu generieren. Ich habe keinen Beispielcode zur Hand, obwohl ich wahrscheinlich einige arrangieren kann, wenn es interessant ist.

An einem Punkt hatte ich einen Generator für etwas wie das, das auch die ganze Serialisierung für die Felder erzeugte. Ich fühlte irgendwie, dass es ein bisschen zu weit ging. Ich fühle mich wie konkrete Definitionen und deklarative Besucher auf den Feldern ist mehr geradlinig. Es ist ein bisschen weniger magisch für den Fall, dass jemand den Code hinter mir behalten muss. Ich weiß nicht, dass du offensichtlich eine Situation hast, ich hatte nach der Umsetzung noch Vorbehalte. :)

Es wäre cool, wieder mit den C ++ 11 Funktionen zu schauen, obwohl ich keine Chance hatte.

Aktualisierung:

Es gibt noch ein paar Knackser, aber das funktioniert meistens.

%Vor%

Es hat Probleme mit get's declltype. Ich habe nicht wirklich Tupel verwendet, um zu wissen, was dort zu erwarten ist. Ich glaube nicht, dass es etwas damit zu tun hat, wie Sie die Typen oder Felder erzeugen.

Hier ist was der Präprozessor mit -E erzeugt:

%Vor%     
Tom Kerr 27.03.2012, 20:40
quelle
1

Dies ist keine Antwort, sondern lediglich eine andere (gruselige) Idee, die es zu berücksichtigen gilt. Ich habe eine inl Datei, die ich einmal geschrieben habe, dass ein bisschen sorta vage ähnlich ist. Es ist hier: Ссылка

Das Grundkonzept ist der Aufrufer dies:

%Vor%

und die Datei inl erstellt einen benutzerdefinierten Bitfeldtyp mit benannten Elementen, indem SEPERATOR neu definiert und dann erneut BITTYPES verwendet wird. Die können dann leicht verwendet werden, einschließlich einer Funktion ToString .

%Vor%

Die Inline-Datei selbst ist ein furchteinflößender Code, aber ein Ansatz, der ungefähr so ​​aussieht, könnte die Verwendung des Aufrufers vereinfachen.

Wenn ich später Zeit habe, werde ich sehen, ob ich etwas an dieser Idee für das, was Sie wollen, machen kann.

    
Mooing Duck 27.03.2012 20:34
quelle
1

Basierend auf Tom Kerrs Vorschlag, habe ich nach Boost.Precessor Sequenzen gesucht. Folgendes habe ich mir ausgedacht:

%Vor%

Kompilieren der gesamten Sache mit gcc -std=c++11 -E -P (und Neuformatierung) ergibt:

%Vor%     
Kerrek SB 27.03.2012 23:29
quelle
0

Sie könnten etwas Ähnliches tun wie BOOST_SERIALIZATION_NVP (aus der Boost.Serialization-Bibliothek). Das Makro erstellt eine (kurzlebige) Wrapper-Struktur, die den Namen des Arguments und den Wert miteinander verknüpft. Dieses Name-Wert-Paar wird dann vom Bibliothekscode verarbeitet (Name ist eigentlich nur bei der XML-Serialisierung wichtig, andernfalls wird er verworfen).

So könnte Ihr Code wie folgt aussehen:

%Vor%     
doublep 27.03.2012 20:44
quelle

Tags und Links