2014-09-12 20 views
8

Tôi đang viết bộ mô phỏng vi xử lý trong C++ và một trong những mục tiêu của tôi là làm cho mã trở nên dễ đọc. Để thực hiện opcodes, tôi có một cấu trúc mà tôi đang sử dụng để đại diện cho các hướng dẫn bộ xử lý riêng lẻ, và nó chứa cả mã opcode và cách xa bộ đếm chương trình. Ý tưởng là để nhóm thông tin liên quan về mỗi hướng dẫn.C++: cấu trúc thành viên trong câu lệnh switch

struct instruction 
{ 
    const int opcode; // instruction opcode 
    const int op_size; // how far to advance Program Counter 
}; 

const instruction HALT{0x76, 1}; 
const instruction NOP {0x00, 1}; 

kế hoạch ban đầu của tôi là để xác định tất cả các opcodes sử dụng struct này, như tôi đã theo ấn tượng rằng const được ưa thích để sử dụng #define cho C++ hằng số. Ngoài ra, tôi sẽ có thể nhóm sạch tất cả các thuộc tính liên quan của một opcode.

Tuy nhiên, có vẻ như điều này sẽ không hoạt động đối với báo cáo chuyển đổi, như tôi dự định ban đầu. Đoạn mã sau sẽ không biên dịch, và Visual Studio cung cấp lỗi "biểu thức trường hợp không liên tục".

switch (next_instruction) { // next_instruction is an int parsed from a file 
    case HALT.opcode: 
     // do stuff 
     break; 
    case NOP.opcode: 
     // do stuff 
     break; 
    default: 
     std::cout << "Unrecognized opcode" << std::endl; 
      break; 
    } 

Tôi cũng đã tải về mới nhất Visual Studio biên dịch (MSVC Tháng 11 2013 CTP) để cố gắng tận dụng constexpr từ C++ 11, nhưng tôi đã có cùng một vấn đề, và nó sẽ không biên dịch. Ở đây tôi chuyển đổi cấu trúc của tôi thành một lớp và cố gắng tận dụng constexpr, để đảm bảo rằng các thành viên của một instruction có thể được sử dụng làm hằng số thời gian biên dịch.

class Instruction 
{ 
    public: 
    constexpr Instruction(int code, int size) : opcode(code), op_size(size) {} 
    const int opcode; // instruction opcode 
    const int op_size; // how far to advance Program Counter 
}; 

constexpr Instruction HALT(0x76, 1); 
constexpr Instruction NOP (0x00, 1); 

Tôi không thực sự chắc chắn phải làm gì vào thời điểm này, vì dường như trình biên dịch không hiểu rằng các giá trị struct được gán làm hằng số.

Vậy có cách nào để sử dụng thành viên cấu trúc trong câu lệnh chuyển đổi hay tôi chỉ cần chuyển sang sử dụng #define? Ngoài ra, có cách nào tốt hơn để làm điều này trong khi vẫn giữ lại một số tổ chức? Tôi thực sự đánh giá cao bất kỳ trợ giúp hoặc thông tin chi tiết nào bạn có thể cung cấp, cảm ơn!

EDIT: Xin lỗi, tôi nên đã làm cho nó rõ ràng hơn rằng next_instruction chỉ là một int, không phải là một instruction struct/đối tượng

+0

Phiên bản 'constexpr' của bạn sẽ hoạt động. Vì vậy, nó có thể là trình biên dịch của bạn không thực hiện điều này một cách chính xác. Tuy nhiên, bạn nên hiển thị định nghĩa 'next_instruction'. – juanchopanza

+0

'constexpr' chỉ được triển khai trong Visual Studio Next (14), Visual Studio 2013 không triển khai nó. Ngoài ra, phiên bản phát hành mới nhất của VS là bản cập nhật 2013 3 chứ không phải là CTP tháng 11. – Drop

+0

Ngoài ra, mã của bạn có mùi "Type Switch antipattern". Bạn có thể có thể sử dụng đa hình ở đây (động hoặc tĩnh) thay vì điều kiện: [Cách để loại bỏ chuyển đổi trong mã] (http://stackoverflow.com/questions/126409/ways-to-eliminate-switch-in-code). Hầu hết thời gian nó có thể nhanh hơn (bạn sẽ cần phải cấu hình nó). – Drop

Trả lời

5

Nếu bạn wan't để có được mạo hiểm với các mẫu, một giải pháp khả thi nếu không có sự sợ hãi macro có thể như sau.

template<int code, int size> 
struct InstructionType 
{ 
    static const int opcode = code ; 
    static const int op_size = size; 
}; 
struct Instruction 
{ 
    int opcode; 
    int op_size; 
}; 
typedef InstructionType<0x76, 1> HALT; 
typedef InstructionType<0x00, 1> NOP; 


int main() 
{ 
    Instruction next_instruction; 
    switch (next_instruction.opcode) { 
    case HALT::opcode: 
     // do stuff 
     break; 
    case NOP::opcode: 
     // do stuff 
     break; 
    default: 
     std::cout << "Unrecognized opcode" << std::endl; 
     break; 
    } 
} 
+0

@Rimas: Tại sao bạn dự định tạo các đối tượng không cần thiết? – Abhijit

10

Tôi đã thử nghiệm mã của bạn trong Qt Creator 3.1.2 bằng trình biên dịch MinGW 4.8.3. Chỉ cần thay thế const bởi constexpr trong từng nét hướng dẫn làm trình biên dịch hạnh phúc:

struct instruction 
{ 
    const int opcode; // instruction opcode 
    const int op_size; // how far to advance Program Counter 
}; 

// Replacing "const" by "constexpr" int these two lines 
constexpr instruction HALT{0x76, 1}; 
constexpr instruction NOP {0x00, 1}; 

int main() { 
    int next_instruction = 0x76; 
    switch (next_instruction) { // next_instruction is an int parsed from a file 
     case HALT.opcode: 
      // do stuff 
      break; 
     case NOP.opcode: 
      // do stuff 
      break; 
     default: 
      std::cout << "Unrecognized opcode" << std::endl; 
       break; 
     } 
} 

Chỉnh sửa để thêm một số dấu ngoặc kép:

C++ Programming Language (Fourh Edition) nói về nhãn trong báo cáo chuyển đổi:

Biểu thức trong trường hợp nhãn phải là biểu thức không đổi của loại tích phân hoặc liệt kê. ”(9.4.2 chuyển báo cáo").

Từ Phần 10.4 Expressions không đổi:

C++ cung cấp hai ý nghĩa liên quan của “liên tục”:

  • constexpr: Đánh giá tại thời gian biên dịch
  • const: Không sửa đổi trong phạm vi này

Về cơ bản , vai trò của constexpr là bật và đảm bảo đánh giá thời gian biên dịch, trong khi đó vai trò chính của const là chỉ định bất biến trong giao diện.

[...]

10.4.2 const trong Expressions liên tục

[...] Một const khởi tạo với một biểu thức hằng số có thể được sử dụng trong một biểu thức hằng . Một const khác với một constexpr ở chỗ nó có thể được khởi tạo bởi cái gì đó không phải là một biểu thức liên tục; trong trường hợp đó , const không thể được sử dụng như là một biểu thức liên tục.

Nhãn trong báo cáo chuyển đổi requieres constexpr để đánh giá được thực hiện tại thời gian biên dịch. Vì vậy, có vẻ như const instruction HALT {0x76,1} không đảm bảo đánh giá thời gian biên dịch trong khi constexpr instruction HALT {0x076,1} thực hiện.

2

Quay trở lại từ cây chỉ trong chốc lát, đây là một cược khá an toàn khi bạn đang viết trình mô phỏng 8080/Z80. Vì vậy, không sử dụng một chuyển đổi ở tất cả. Sắp xếp các cấu trúc lệnh của bạn trong một mảng và sử dụng opcode để được thực hiện như một chỉ mục.

struct instruction 
{ 
    const int opcode; // instruction opcode 
    const int op_size; // how far to advance Program Counter 
    const void (*emulator)(parameter list); // code for this opcode 
}; 

void illegal(parameter list) 
{ 
    std::cout << "Unrecognized opcode" << std::endl; 
} 

instruction opcodes[] = // assuming 8080 for now 
{ 
{0x00, 1, nop_emulator}, // NOP 
{0x01, 3, lxib_emulator}, // LXI B 
etc. 
{0x08, 1, illegal},  // 0x08 is invalid on the 8080 
etc. 
}; 

Bây giờ mã của bạn chỉ trở nên

opcodes[next_instruction].emulator(parameter list); 

Bạn có thể chọn một trong hai loại bỏ các opcode, hoặc làm một precheck để đảm bảo rằng tất cả các opcode là ở địa điểm chính xác trong bảng.

Điều này cũng có lợi thế là nó sẽ dừng mã của bạn khỏi một thói quen nguyên khối bằng cách chia nhỏ nó thành một thường trình cho mỗi mã vạch. Nếu bạn viết một trình mô phỏng Z80, điều này sẽ trở thành mối quan tâm chính vì các nhóm 0xCB, 0xDD, 0xED và 0xFD, trong một mẫu chuyển đổi sẽ yêu cầu một công tắc thứ hai trong mỗi trình xử lý trường hợp cho bốn mã giả này.

Các vấn đề liên quan