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:
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.
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% 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
.
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.
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%Tags und Links c++ field class-design