2012-05-17 31 views
13

Vì vậy, tôi có cây khổng lồ này về cơ bản là một chuyển đổi lớn/trường hợp với các phím chuỗi và các cuộc gọi chức năng khác nhau trên một đối tượng phổ biến tùy thuộc vào khóa và một phần siêu dữ liệu.Metaprogramming C/C++ sử dụng preprocessor

Mỗi entry về cơ bản trông như thế này

} else if (strcmp(key, "key_string") == 0) { 
    ((class_name*)object)->do_something(); 
} else if (... 

nơi do_something có thể có lời gọi khác nhau, vì vậy tôi không thể chỉ cần sử dụng con trỏ hàm. Ngoài ra, một số phím yêu cầu đối tượng được truyền tới một lớp con.

Bây giờ, nếu tôi viết mã này bằng ngôn ngữ cấp cao hơn, tôi sẽ sử dụng từ điển lambdas để đơn giản hóa điều này.

Nó xảy ra với tôi rằng tôi có thể sử dụng các macro để đơn giản hóa này để một cái gì đó giống như

case_call("key_string", class_name, do_something()); 
case_call(/* ... */) 

nơi case_call sẽ là một vĩ mô mà sẽ mở rộng mã này vào đoạn mã đầu tiên.

Tuy nhiên, tôi rất nhiều trên hàng rào cho dù đó sẽ được coi là phong cách tốt. Ý tôi là, nó sẽ giảm công việc đánh máy và cải thiện DRYness của mã, nhưng sau đó nó thực sự có vẻ lạm dụng hệ thống macro một chút.

Bạn sẽ đi xuống con đường đó, hay đúng hơn là loại toàn bộ nội dung? Và lý do của bạn để làm như vậy là gì?

Sửa

Một số làm rõ:

Mã này được sử dụng như một lớp keo giữa một API kịch bản đơn giản mà truy cập các khía cạnh khác nhau của một C++ API thuộc tính quan trọng có giá trị như đơn giản. Các thuộc tính được thực hiện theo nhiều cách khác nhau trong C++ mặc dù: Một số có phương thức getter/setter, một số được đặt trong một cấu trúc đặc biệt. Các hành động script tham chiếu các đối tượng C++ được đúc vào một lớp cơ sở chung. Tuy nhiên, một số hành động chỉ khả dụng trên một số lớp con nhất định và phải được loại bỏ.

Xa hơn nữa trên đường, tôi có thể thay đổi API C++ thực tế, nhưng hiện tại, nó phải được coi là không thể thay đổi. Ngoài ra, điều này phải làm việc trên một trình biên dịch nhúng, do đó, tăng hoặc C++ 11 là (đáng buồn) không có sẵn.

+5

Tại sao phiếu bầu đóng lại - đây là câu hỏi rất hợp lệ. Tôi đang thiếu gì ở đây? –

+3

Đó là một câu hỏi hay, nhưng "Câu hỏi này không phù hợp với định dạng Hỏi & Đáp của chúng tôi. Chúng tôi hy vọng câu trả lời thường liên quan đến sự kiện, tài liệu tham khảo hoặc chuyên môn cụ thể; * câu hỏi này có khả năng thu hút ý kiến, tranh luận, tranh luận, bỏ phiếu hoặc mở rộng Thảo luận. * " – Fanael

+0

Nó đã ở mức +5, vì vậy mà kinda nói rằng những người khác, bản thân tôi đã bao gồm, quan tâm đến kết quả. Câu trả lời dưới đây đã khá đẹp. –

Trả lời

6

Tôi khuyên bạn nên đảo ngược một chút vai trò. Bạn đang nói rằng đối tượng đã là một số lớp biết cách xử lý một tình huống nhất định, vì vậy hãy thêm một virtual void handle(const char * key) vào lớp cơ sở của bạn và để đối tượng kiểm tra trong quá trình thực hiện nếu nó áp dụng cho nó và làm bất cứ điều gì là cần thiết.

Điều này sẽ không chỉ loại bỏ chuỗi if-else-if dài hơn, nhưng cũng sẽ an toàn hơn và giúp bạn linh hoạt hơn trong việc xử lý các sự kiện đó.

+0

Điều này không loại bỏ chuỗi if-else-if nếu nó chỉ di chuyển/sắp xếp lại nó. Tuy nhiên, đây vẫn là một ý tưởng thực sự tốt. –

+1

@MooingDuck Nó sẽ không loại bỏ các ifs cá nhân, nhưng nó sẽ loại bỏ danh sách dài, trung tâm của các trường hợp phải được xử lý. Mỗi kiểm tra sẽ ở trong lớp nơi xử lý thực tế xảy ra, do đó các địa điểm phải được thay đổi để thêm chức năng mới sẽ gần nhau. – Fozi

+0

Đó là một ý tưởng thú vị. Nó sẽ đòi hỏi tôi phải sửa đổi các lớp thực hiện, mà vì nhiều lý do không phải là mong muốn ngay bây giờ, nhưng điều này chắc chắn là một ý tưởng tốt. – bastibe

6

Điều đó dường như với tôi việc sử dụng macro thích hợp. Họ, sau khi tất cả, làm cho sự lặp lại cú pháp eliding. Tuy nhiên, khi bạn có sự lặp lại cú pháp, nó không phải lúc nào cũng là lỗi của ngôn ngữ - có lẽ có những lựa chọn thiết kế tốt hơn ngoài kia sẽ cho phép bạn tránh được quyết định này hoàn toàn.

Trí tuệ nói chung là sử dụng một phím lập bản đồ bảng để hành động:

std::map<std::string, void(Class::*)()> table; 

Sau đó nhìn lên và gọi hành động trong một đi:

object->*table[key](); 

Hoặc sử dụng find để kiểm tra thất bại:

const auto i = table.find(key); 
if (i != table.end()) 
    object->*(i->second)(); 
else 
    throw std::runtime_error(...); 

Nhưng nếu bạn nói không có chữ ký chung cho các chức năng (ví dụ:, bạn không thể sử dụng con trỏ hàm thành viên) thì những gì bạn thực sự nên phụ thuộc vào các chi tiết cụ thể của dự án của bạn mà tôi không biết. Có thể là macro là cách duy nhất để tách biệt sự lặp lại mà bạn đang thấy hoặc có thể là cách tốt hơn để thực hiện nó.

Tự hỏi: tại sao các chức năng của tôi có các đối số khác nhau không? Tại sao tôi có sử dụng phôi không? Nếu bạn đang gửi đi loại đối tượng, rất có thể bạn cần giới thiệu một giao diện chung.

+0

"nơi do_something có thể có các lời gọi khác nhau, vì vậy tôi không thể chỉ sử dụng con trỏ hàm." <- có vẻ như không phải tất cả các hàm đều là 'void (Class :: *)()'. Một số có thể yêu cầu đối số. Ít nhất đó là những gì tôi hiểu. – mfontanini

+0

@fontanini: Ah, bỏ lỡ điều đó. Sẽ chỉnh sửa. –

+0

Trên thực tế, tôi có hai loại invocations riêng biệt, vì vậy điều này thực sự sẽ hoạt động nếu tôi sử dụng hai bản đồ. – bastibe

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