Gibt es eine Möglichkeit, eine Aktion auf N C ++ - Klassenmitglieder in einer Schleife über Member-Namen (wahrscheinlich über Pre-Prozessor) anzuwenden?

7

Das Problem:

Ich habe eine C ++ Klasse mit gajillion (& gt; 100) Mitgliedern, die sich nahezu identisch verhalten:

  • gleicher Typ

  • In einer Funktion hat jedes Mitglied den gleichen genauen Code wie andere Mitglieder, z. Zuweisung aus einer Karte in einem Konstruktor, wobei der Kartenschlüssel der gleiche wie der Mitgliedsschlüssel ist

  • Diese Identität des Verhaltens wird über viele-viele Funktionen (& gt; 20) wiederholt, natürlich ist das Verhalten in jeder Funktion anders, so dass es keine Möglichkeit gibt, Dinge zu berücksichtigen.

  • Die Liste der Mitglieder ist sehr flüssig, mit ständigen Hinzufügungen und manchmal Löschungen, einige ( aber nicht alle ) werden von wechselnden Spalten in einer DB-Tabelle gesteuert.

Wie Sie sich vorstellen können, stellt dies einen großen Nachholbedarf bei der Codeerstellung und -pflege dar, denn um ein neues Mitglied hinzuzufügen, müssen Sie jeder Funktion Code hinzufügen wo analoge Mitglieder verwendet werden.

Beispiel einer Lösung, die ich möchte

Tatsächlicher C ++ Code, den ich brauche (zB im Konstruktor) :

%Vor%

C ++ code Ich möchte schreiben können :

%Vor%

Anforderungen

  • Die Lösung muss mit GCC kompatibel sein (mit Nmake als make-System, wenn es darauf ankommt). Kümmern Sie sich nicht um andere Compiler.

  • Die Lösung kann auf einer Pre-Prozessor-Ebene oder etwas Kompilierbares sein. Mir geht es beiden gut. Aber bis jetzt haben mich alle meine Forschungen auf die Schlussfolgerung gebracht, dass letzteres in C ++ einfach unmöglich ist (ich so vermisse jetzt Perl, da ich gezwungen bin, C ++ zu machen!)

  • Die Lösung muss zumindest in gewissem Maße "Industriestandard" sein (zB Boost ist super, aber ein benutzerdefiniertes Perlskript, das Joe-Quick-Fingers einmal erstellt und in seinem Blog gepostet hat, ist es nicht. Verdammt, kann ich Schreiben Sie einfach das Perl-Skript, das viel mehr ein Perl-Experte als ein C ++ - Experte ist - ich kann einfach keine Bigwigs im Software-Engineering bei meiner BigCompany bekommen, um es zu benutzen :))

  • Die Lösung sollte es mir ermöglichen, eine Liste von IDs zu deklarieren (idealerweise in nur einer Header-Datei anstatt in jeder "#FOR_EACH_WORD" -Direktive wie im obigen Beispiel)

  • Die Lösung darf nicht auf den Konstruktor "Objekt aus einer DB-Tabelle erstellen" beschränkt sein. Es gibt viele Funktionen, die meisten davon keine Konstruktoren, die das brauchen.

  • Eine Lösung von "Machen Sie alle Werte in einem einzigen Vektor, und führen Sie dann eine 'für' Schleife über den Vektor" ist eine offensichtliche, und kann nicht verwendet werden - der Code in einer Bibliothek von vielen verwendet Apps, die Mitglieder sind öffentlich, und das Umschreiben dieser Apps zur Verwendung von Vektor-Mitgliedern anstelle von benannten Mitgliedern kommt leider nicht in Frage.

DVK 02.09.2009, 13:18
quelle

12 Antworten

6

Boost.Preprocessor schlägt viele praktische Makros vor, um solche Operationen durchzuführen. Bojan Resnik hat bereits Eine Lösung , die diese Bibliothek verwendet, geht aber davon aus, dass jeder Mitgliedsname auf die gleiche Weise erstellt wird.

Da Sie ausdrücklich eine Liste von IDs deklarieren müssen, ist hier eine Lösung, die Ihre Anforderungen besser erfüllen sollte.

%Vor%

Wie Sie sehen können, müssen Sie nur ein Makro schreiben, das das Muster darstellt, das Sie wiederholen möchten, und es an das Makro BOOST_PP_SEQ_FOR_EACH übergeben.

    
Luc Touraille 02.09.2009, 15:52
quelle
9

Boost enthält eine großartige Präprozessorbibliothek, mit der Sie solchen Code generieren können:

%Vor%     
Bojan Resnik 02.09.2009 13:58
quelle
4

Sie könnten so etwas tun: Erstellen Sie eine Adapterklasse oder ändern Sie die vorhandene Klasse, um einen Vektor von Zeigern auf diese Felder zu haben, fügen Sie die Adressen aller in Frage kommenden Elementvariablen zu diesem Vektor im Klassenkonstruktor hinzu und führen Sie sie bei Bedarf aus die for-Schleife auf diesem Vektor. Auf diese Weise ändern Sie die Klasse für externe Benutzer nicht (oder fast nicht) und verfügen über eine gute Funktion für die Schleife.

    
sharptooth 02.09.2009 13:24
quelle
4

Natürlich ist die offensichtliche Frage: Warum haben Sie eine Klasse mit 100 Mitgliedern? Es scheint nicht wirklich vernünftig.

Angenommen, es ist trotzdem gesund - haben Sie boost preprocessor library ? Ich habe es nie selbst benutzt (wie es ein Freund sagte: Das führt zur dunklen Seite), aber nach dem, was ich gehört habe, sollte es das Werkzeug für den Job sein.

    
sbi 02.09.2009 13:57
quelle
3

Verwende perllos auf deinem eigenen Rechner Perl, um den Konstruktor zu erstellen. Dann bitten Sie, Ihr Gehalt zu erhöhen, da Sie so einen riesigen Code erfolgreich verwalten.

    
Pavel Shved 02.09.2009 13:39
quelle
2

Sie können den Präprozessor verwenden, um die Mitglieder zu definieren und später dieselbe Definition für den Zugriff zu verwenden:

%Vor%

Es hat für mich funktioniert, ist aber schwer zu debuggen.

    
xtofl 02.09.2009 13:32
quelle
2

Sie können auch ein Besuchermuster basierend auf Zeiger-zu-Mitgliedern implementieren. Nach der Präprozessor-Lösung ist diese viel debuggefähiger.

%Vor%     
xtofl 02.09.2009 13:42
quelle
1

Warum nicht zur Laufzeit? (Ich hasse Makro-Hackerei wirklich)

Was Sie wirklich in gewisser Hinsicht fragen, sind Klassenmetadaten.

Also würde ich etwas wie:

versuchen %Vor%

(ziemlich sicher, dass ich die Syntax richtig verstanden habe, aber das ist ein Maschinerie-Beitrag, kein Code-Beitrag)

RE: in einer Funktion hat jedes Mitglied den gleichen genauen Code wie andere Mitglieder, z. Zuweisung aus einer Map in einem Konstruktor, wobei der Map-Schlüssel dem Member-Schlüssel entspricht

Dies wird oben behandelt.

RE: Die Liste der Mitglieder ist sehr flüssig, mit ständigen Hinzufügungen und manchmal Löschungen, einige (aber nicht alle) werden durch das Ändern von Spalten in einer DB-Tabelle ausgelöst.

Wenn Sie einen neuen AMember hinzufügen, sagen Sie newMember, müssen Sie lediglich den MetaData-Konstruktor mit einem:

aktualisieren %Vor%

RE: Diese Identität des Verhaltens wird über viele-viele Funktionen (& gt; 20) wiederholt, natürlich ist das Verhalten in jeder Funktion anders, so dass es keine Möglichkeit gibt, Dinge zu berücksichtigen.

Sie haben die Maschinerie, um das gleiche Idiom anzuwenden, um die Funktionen zu bauen

zB: setAllValuesTo (const AMember & amp; value)

%Vor%

Wenn Sie ein kleines bisschen kreativ mit Funktionszeigern oder Vorlagenfunktionalen sind, können Sie die Mutationsoperation ausklammern und so ziemlich alles, was Sie wollen, zu A-Zählern von YourClass auf einer Sammelbasis tun. Umschließen Sie diese allgemeinen Funktionen (die einen Funktions- oder Funktionszeiger benötigen), um Ihre aktuelle Gruppe von 20 öffentlichen Methoden in der Schnittstelle zu implementieren.

Wenn Sie mehr Metadaten benötigen, erweitern Sie einfach die Codemain der MetaData-Map über einen Zeiger auf Member hinaus. (Natürlich würde sich dann das oben genannte Ich ändern)

Hoffe, das hilft.

    
pgast 03.09.2009 09:01
quelle
0

Sie können etwas wie sein tun:

%Vor%

Das passt nicht ganz zu Ihrer Beschreibung, aber am nächsten dazu, dass Sie nicht tippen.

    
Michael Krelin - hacker 02.09.2009 13:22
quelle
0

Wahrscheinlich würde ich versuchen, Laufzeit-Polymorphismus (dynamischer Versand) zu nutzen. Erstelle eine Elternklasse für diese Mitglieder mit einer Methode, die die üblichen Dinge erledigt. Die Mitglieder leiten ihre Klasse von dieser Elternklasse ab. Diejenigen, die eine andere Implementierung der Methode benötigen, implementieren ihre eigenen. Wenn sie das gemeinsame Zeug auch brauchen, dann können sie innerhalb der Methode zu der Basisklasse herabstufen und ihre Version der Methode aufrufen.

Dann müssen Sie innerhalb Ihrer ursprünglichen Klasse nur das Mitglied für jede Methode aufrufen.

    
T.E.D. 02.09.2009 14:03
quelle
0

Ich würde eine kleine Befehlszeilen-App empfehlen, die in welcher Sprache auch immer Sie oder Ihr Team am besten beherrschen.

Fügen Sie Ihren Quelldateien eine Art Vorlagensprache hinzu. Für so etwas brauchen Sie keinen vollwertigen Parser oder etwas Ähnliches zu implementieren. Suchen Sie einfach nach einem leicht identifizierbaren Zeichen am Anfang einer Zeile und einigen zu ersetzenden Schlüsselwörtern.

Verwenden Sie die Befehlszeilen-App, um die Vorlagen Quelldateien in echte Quelldateien zu konvertieren. In den meisten Build-Systemen sollte dies durch das Hinzufügen einer Build-Phase oder einfach dem Build-System einfach gesagt werden: "Verwenden Sie MyParser.exe, um Dateien vom Typ * .tmp"

zu verarbeiten

Hier ist ein Beispiel von dem, worüber ich spreche:

MyClass.tmp

%Vor%

Ich habe "▐" als Beispiel verwendet, aber jedes Zeichen, das sonst nie als erstes Zeichen in einer Zeile erscheinen würde, ist vollkommen akzeptabel.

Nun würden Sie diese .tmp-Dateien als Ihre Quelldateien behandeln und den tatsächlichen C ++ - Code automatisch generieren lassen.

Wenn Sie schon mal den Satz "Code schreiben, der Code schreibt" gehört haben, dann heißt das:)

    
e.James 02.09.2009 14:07
quelle
0

Hier gibt es schon viele gute Antworten und Ideen, aber um der Vielfalt willen werde ich eine andere vorstellen.

In der Codedatei für MyClass wäre:

%Vor%

Was es dann möglich macht, den gewünschten Konstruktor als:

zu schreiben %Vor%

Und natürlich, für alle anderen Funktionen, die auf allen Mitgliedern funktionieren, würden Sie ähnliche Schleifen schreiben.

Nun gibt es ein paar Probleme mit dieser Technik:

  • Operationen auf Mitgliedern verwenden eine Laufzeitschleife im Gegensatz zu anderen Lösungen, die eine abgerollte Folge von Operationen ergeben würden.
  • Das hängt absolut davon ab, dass jedes Mitglied denselben Typ hat. Während dies von der OP erlaubt wurde, sollte man noch prüfen, ob sich das in Zukunft ändern könnte. Einige der anderen Lösungen haben diese Einschränkung nicht.
  • Wenn ich mich richtig erinnere, ist offsetof nur für die Arbeit mit POD-Typen nach dem C ++ - Standard definiert. In der Praxis habe ich es nie versagen gesehen. Jedoch habe ich nicht alle C ++ Compiler dort draußen benutzt. Insbesondere habe ich GCC nie benutzt. Sie müssten dies also in Ihrer Umgebung testen, um sicherzustellen, dass es tatsächlich wie vorgesehen funktioniert.

Ob dies Probleme sind oder nicht, müssen Sie gegen Ihre eigene Situation beurteilen.

Nun, unter der Annahme, dass diese Technik verwendbar ist, gibt es einen schönen Vorteil. Diese GetMemberX-Funktionen können in öffentliche statische / Member-Funktionen Ihrer Klasse umgewandelt werden, wodurch dieses generische Mitglied Zugriff auf mehr Stellen in Ihrem Code erhält.

%Vor%

Und falls nützlich, könnten Sie auch eine GetMemberPtrByID -Funktion hinzufügen, um nach einer gegebenen String-ID zu suchen und einen Zeiger auf das entsprechende Element zurückgeben.

Ein Nachteil dieser Idee ist bisher, dass das Risiko besteht, dass ein Member zur Klasse, aber nicht zum Array MyClassMembers hinzugefügt werden kann. Diese Technik könnte jedoch mit der Makro-Lösung von xtofl kombiniert werden, so dass eine einzelne Liste sowohl die Klasse als auch das Array auffüllen könnte.

ändert sich in der Kopfzeile:

%Vor%

und Änderungen in der Codedatei:

%Vor%

Hinweis: Ich habe Fehler aus meinen Beispielen hier vergessen. Je nachdem, wie dies verwendet wird, möchten Sie möglicherweise sicherstellen, dass die Array-Grenzen nicht mit Debug-Modus-Asserts und / oder Release-Modus-Prüfungen überschritten werden, die NULL-Zeiger für fehlerhafte Indizes zurückgeben würden. Oder einige Ausnahmen, falls zutreffend.

Wenn Sie sich natürlich keine Gedanken darüber machen, dass die array-Grenzen durch Fehler überprüft werden, kann GetMemberPtr tatsächlich in etwas anderes geändert werden, das einen Verweis auf das Element zurückgibt.

    
TheUndeadFish 05.09.2009 02:06
quelle

Tags und Links