C ++: Strukturelement in einer switch-Anweisung

8

Ich schreibe einen Mikroprozessor-Emulator in C ++, und eines meiner Ziele war es, den Code sehr lesbar zu machen. Um Opcodes zu implementieren, habe ich eine Struktur, die ich benutze, um einzelne Prozessoranweisungen darzustellen, und sie enthält sowohl den Opcode als auch wie weit der Programmzähler vorgeschoben werden soll. Die Idee war, verwandte Informationen über jede Anweisung zu gruppieren.

%Vor%

Mein ursprünglicher Plan war, alle Opcodes zu definieren, die diese Struktur verwenden, da ich den Eindruck hatte, dass const der Verwendung von #define für C ++ - Konstanten vorgezogen wurde. Außerdem wäre ich in der Lage, alle zugehörigen Attribute eines Opcodes sauber zu gruppieren.

Es scheint jedoch, dass dies nicht für Switch-Anweisungen funktioniert, wie ich es ursprünglich beabsichtigt hatte. Der folgende Code wird nicht kompiliert, und Visual Studio gibt den Fehler "Case-Ausdruck nicht konstant".

%Vor%

Ich habe auch den neuesten Visual Studio Compiler (MSVC November 2013 CTP) heruntergeladen, um constexpr von C ++ 11 zu nutzen, aber ich hatte das gleiche Problem, und es wird nicht kompiliert. Hier konvertierte ich meine Struktur in eine Klasse und versuchte, constexpr zu nutzen, um sicherzustellen, dass die Mitglieder von instruction als Kompilierzeitkonstanten verwendet werden konnten.

%Vor%

Ich bin mir nicht sicher, was ich zu diesem Zeitpunkt tun soll, da der Compiler anscheinend nicht versteht, dass die Strukturwerte als Konstanten zugewiesen sind.

Gibt es eine Möglichkeit, ein Strukturelement in einer switch-Anweisung zu verwenden, oder sollte ich einfach zu #define wechseln? Oder gibt es einen besseren Weg, dies zu tun, während man noch etwas Organisation behält? Ich würde wirklich jede Hilfe oder Einsicht schätzen, die Sie anbieten können, danke!

BEARBEITEN: Tut mir leid, ich hätte klarstellen sollen, dass next_instruction nur ein int ist, nicht ein instruction struct / object

    
dcoffill 12.09.2014, 06:04
quelle

3 Antworten

10

Ich habe Ihren Code in Qt Creator 3.1.2 mit MinGW 4.8.3 Compiler getestet. Das Ersetzen von const durch constexpr in jeder Befehlsdefinition machte den Compiler glücklich:

%Vor%

Bearbeiten, um einige Zitate hinzuzufügen :

Die C ++ Programmiersprache (Fourh Edition) sagt über Labels in switch-Anweisungen:

  

" Der Ausdruck in den Case-Labels muss ein konstanter Ausdruck sein   des Integral- oder Aufzählungstyps. "(9.4.2 switch Statements").

Aus Abschnitt 10.4 Konstante Ausdrücke:

  

C ++ bietet zwei verwandte Bedeutungen von "konstant":

     
  • constexpr: Auswerten zur Kompilierzeit
  •   
  • const: In diesem Bereich nicht ändern
  •   

Grundsätzlich besteht die Rolle von constexpr in der Aktivierung und Sicherstellung   Kompilierzeit Auswertung, während const die primäre Rolle ist, zu spezifizieren   Unveränderlichkeit in Schnittstellen.

     

[...]

     

10.4.2 const in konstanten Ausdrücken

     

[...] Ein mit einem konstanten Ausdruck initiiertes const kann in a verwendet werden   konstanter Ausdruck. Ein Const unterscheidet sich von einem Constexpr dadurch, dass es möglich ist   durch etwas initialisiert werden, das kein konstanter Ausdruck ist; darin   Fall, das const kann nicht als konstanter Ausdruck verwendet werden.

Labels in switch-Anweisungen benötigen constexpr, damit die Auswertung zur Kompilierzeit erfolgt. Es scheint also so zu sein, dass const instruction HALT {0x76,1} die Auswertung der Kompilierungszeit nicht sicherstellt, während constexpr instruction HALT {0x076,1} dies tut.

    
davidaf 12.09.2014 07:17
quelle
5

Wenn Sie mit Vorlagen abenteuerlich werden wollen, könnte eine mögliche Lösung ohne das gefürchtete Makro wie folgt sein.

%Vor%     
Abhijit 12.09.2014 06:29
quelle
2

Wenn Sie einen Moment von den Bäumen zurücktreten, ist es ziemlich sicher, dass Sie einen 8080 / Z80-Emulator schreiben. Also benutze keinen Schalter. Ordnen Sie Ihre Befehlsstrukturen in einem Array an und verwenden Sie den Opcode als Index.

%Vor%

Jetzt wird Ihr Code einfach

%Vor%

Sie haben die Möglichkeit, entweder den Opcode zu verwerfen oder eine Vorabprüfung durchzuführen, um sicherzustellen, dass sich jeder Opcode an der richtigen Stelle in der Tabelle befindet.

Dies hat auch den Vorteil, dass es Ihren Code daran hindert, eine monolithische Routine zu sein, indem er in eine Routine pro Opcode aufgeteilt wird. Wenn Sie einen Z80-Emulator schreiben, wird dies aufgrund der 0xCB-, 0xDD-, 0xED- und 0xFD-Gruppen zu einem Hauptproblem, das in einem Schaltermuster einen zweiten Schalter innerhalb jedes der Fallbehandler für diese vier Pseudo-Opcodes erfordert.

    
dgnuff 12.09.2014 16:05
quelle