2012-02-07 43 views
21

Vì vậy, tôi đã có một macro hoạt động tốt trong GCC, nhưng không phải trong trình biên dịch C++ của Microsoft. Tôi hy vọng ai đó có thể biết một cách giải quyết, hoặc có lẽ có thể giải thích cho tôi tại sao nó cư xử theo cách này.Mở rộng macro MSVC++ variadic

Tôi chắc chắn macro này không chính xác là "chuẩn", nhưng nó thực sự sẽ giúp tôi.

Dưới đây là một ví dụ chức năng macro:

#define VA_NARGS_IMPL(_1, _2, _3, _4, _5, N, ...) N 
#define VA_NARGS(...) VA_NARGS_IMPL(__VA_ARGS__, 5, 4, 3, 2, 1) 

#define FULLY_EXPANDED(count, ...) \ 
    MAC## count (__VA_ARGS__) 

#define SEMI_EXPANDED(count, ...) FULLY_EXPANDED(count, __VA_ARGS__) 

#define EXPAND_THESE(...) SEMI_EXPANDED(VA_NARGS(__VA_ARGS__), __VA_ARGS__) 

#define ACTUAL_MACRO(x) parent->GetProperty<x>(); 
#define MAC1(a) ACTUAL_MACRO(a) 
#define MAC2(a,b) MAC1(a) ACTUAL_MACRO(b) 
#define MAC3(a,b,c) MAC2(a,b) ACTUAL_MACRO(c) 
#define MAC4(a,b,c,d) MAC3(a,b,c) ACTUAL_MACRO(d) 
#define MAC5(a,b,c,d,e) MAC4(a,b,c,d) ACTUAL_MACRO(e) 

Dưới đây là làm thế nào tôi có thể sử dụng macro này:

struct MyStructure 
{ 
    void Foo() 
    { 
    EXPAND_THESE(Property1, Property2, Property3, Property4) 
    } 

    Base * parent; 
} 

Sau đây là cách GCC mở rộng trên:

struct MyStructure 
{ 
    void Foo() 
    { 
    parent->GetProperty<Property1>(); 
    parent->GetProperty<Property2>(); 
    parent->GetProperty<Property3>(); 
    parent->GetProperty<Property4>(); 
    } 

    Base * parent; 
} 

Nhưng Microsoft vì lý do nào đó mở rộng tất cả __VA_ARGS__ của tôi thành một đối số:

struct MyStructure 
{ 
    void Foo() 
    { 
    parent->GetProperty<Property1, Property2, Property3, Property4>(); 
    } 

    Base * parent; 
} 

Có ai biết tại sao điều này không? Có một số trick tôi có thể kéo để có được Microsoft để mở rộng này như GCC? Có thể quăng thêm vài cặp ngoặc đơn?

Các macro như thế này thực sự có thể giúp tôi thay thế một loạt mã "keo", nhưng vì vấn đề này, tôi không thể di chuyển nó vào dự án VS của mình. Mọi sự trợ giúp sẽ rất được trân trọng!

Cảm ơn.

+5

It 's [lỗi] (http://connect.microsoft.com/VisualStudio/feedback/details/380090/variadic-macro-replacement) và tôi don' t nghĩ họ có kế hoạch sửa chữa nó bất cứ lúc nào sớm. –

+0

Liên kết trùng lặp: [Làm thế nào để khắc phục các vấn đề liên quan đến macro Vĩ mô với "quá tải macro" trong MSVC++ (Microsoft Visual studio)?] (Https://stackoverflow.com/q/48710758/514235) - @JesseGood Thx để chỉ ra lỗi. – iammilind

Trả lời

17

Thật trùng hợp, tôi tình cờ gặp phải vấn đề này ngay hôm nay, và sau nỗ lực đủ, tôi nghĩ mình đã tìm được giải pháp cho mục đích của riêng mình. Lỗi này được MSVC xử lý __VA_ARGS__ dưới dạng một mã thông báo duy nhất trong danh sách đối số. Nhưng bạn có thể làm việc xung quanh điều này bằng cách không sử dụng nó trực tiếp trong danh sách đối số cuộc gọi macro. This comment cho thấy sự bắt đầu của một câu trả lời cho vấn đề của mình:

#define VA_NARGS(...) VA_NUM_ARGS_IMPL_((__VA_ARGS__, 5,4,3,2,1)) 
#define VA_NARGS_IMPL_(tuple) VA_NUM_ARGS_IMPL tuple 
#define VA_NARGS_IMPL(_1,_2,_3,_4,_5,N,...) N 

Nhưng sau đó tôi nghi ngờ bạn có thể sẽ gặp phải vấn đề đảm bảo rằng bị đầy đủ mở rộng để thực tế "N" mà bạn muốn, và không để VA_NARGS_IMPL (arg1, arg2, 5, 4, 3, 2, 1) , Nói. Tôi thấy rằng mã của tôi (trông giống như của bạn) đã phải thay đổi để mở rộng MAC##code tất cả dưới dạng một đơn vị, và sau đó phải được kết hợp riêng với danh sách đối số. Dưới đây là đoạn code mà tôi tìm thấy làm việc cho tôi:

#define ASSERT_HELPER1(expr) singleArgumentExpansion(expr) 
#define ASSERT_HELPER2(expr, explain) \ 
    twoArgumentExpansion(expr, explain) 

/* 
* Count the number of arguments passed to ASSERT, very carefully 
* tiptoeing around an MSVC bug where it improperly expands __VA_ARGS__ as a 
* single token in argument lists. See these URLs for details: 
* 
* http://connect.microsoft.com/VisualStudio/feedback/details/380090/variadic-macro-replacement 
* http://cplusplus.co.il/2010/07/17/variadic-macro-to-count-number-of-arguments/#comment-644 
*/ 
#define COUNT_ASSERT_ARGS_IMPL2(_1, _2, count, ...) \ 
    count 
#define COUNT_ASSERT_ARGS_IMPL(args) \ 
    COUNT_ASSERT_ARGS_IMPL2 args 
#define COUNT_ASSERT_ARGS(...) \ 
    COUNT_ASSERT_ARGS_IMPL((__VA_ARGS__, 2, 1, 0)) 
/* Pick the right helper macro to invoke. */ 
#define ASSERT_CHOOSE_HELPER2(count) ASSERT_HELPER##count 
#define ASSERT_CHOOSE_HELPER1(count) ASSERT_CHOOSE_HELPER2(count) 
#define ASSERT_CHOOSE_HELPER(count) ASSERT_CHOOSE_HELPER1(count) 
/* The actual macro. */ 
#define ASSERT_GLUE(x, y) x y 
#define ASSERT(...) \ 
    ASSERT_GLUE(ASSERT_CHOOSE_HELPER(COUNT_ASSERT_ARGS(__VA_ARGS__)), \ 
       (__VA_ARGS__)) 

int foo() 
{ 
    ASSERT(one); // singleArgumentExpansion(one) 
    ASSERT(two, "foopy"); // twoArgumentExpansion(two, "foopy") 
} 

tâm của tôi là quá nhiều chuyện lãng mạn sau vài giờ giải quyết các vấn đề của riêng tôi để sau đó đi và hoàn toàn giải quyết của bạn, tôi xin lỗi để nói. :-) Nhưng tôi nghĩ điều này là đủ để giúp bạn làm được điều gì đó, với một công việc nhỏ.

16

Tôi biết câu hỏi này là hơn hai tuổi, nhưng tôi nghĩ rằng tôi sẽ cố gắng để đưa ra một câu trả lời được đánh bóng nhiều hơn đối với những người vẫn còn vấp ngã này, như tôi đã làm.

Câu trả lời của Jeff Walden hoạt động và tất cả, nhưng bạn phải khai báo FOO_CHOOSE_HELPER/1/2 cho mỗi macro FOO bạn muốn có đối số variadic. Tôi đã phát triển một lớp trừu tượng để giải quyết vấn đề này.Hãy xem xét những điều sau đây:

#define GLUE(x, y) x y 

#define RETURN_ARG_COUNT(_1_, _2_, _3_, _4_, _5_, count, ...) count 
#define EXPAND_ARGS(args) RETURN_ARG_COUNT args 
#define COUNT_ARGS_MAX5(...) EXPAND_ARGS((__VA_ARGS__, 5, 4, 3, 2, 1, 0)) 

#define OVERLOAD_MACRO2(name, count) name##count 
#define OVERLOAD_MACRO1(name, count) OVERLOAD_MACRO2(name, count) 
#define OVERLOAD_MACRO(name, count) OVERLOAD_MACRO1(name, count) 

#define CALL_OVERLOAD(name, ...) GLUE(OVERLOAD_MACRO(name, COUNT_ARGS_MAX5(__VA_ARGS__)), (__VA_ARGS__)) 

Với kiến ​​trúc này, bạn có thể định nghĩa các macro variadic như vậy:

#define ERROR1(title) printf("Error: %s\n", title) 
#define ERROR2(title, message)\ 
    ERROR1(title);\ 
    printf("Message: %s\n", message) 
#define ERROR(...) CALL_OVERLOAD(ERROR, __VA_ARGS__) 

#define ASSERT1(expr) singleArgumentExpansion(expr) 
#define ASSERT2(expr, explain) twoArgumentExpansion(expr, explain) 
#define ASSERT(...) CALL_OVERLOAD(ASSERT, __VA_ARGS__) 

Với câu trả lời của Jeff bạn sẽ phải xác định các macro như sau:

#define ERROR1(title) printf("Error: %s\n", title) 
#define ERROR2(title, message)\ 
    ERROR1(title);\ 
    printf("Message: %s\n", message) 

#define ERROR_CHOOSE_HELPER2(count) ERROR##count 
#define ERROR_CHOOSE_HELPER1(count) ERROR_CHOOSE_HELPER2(count) 
#define ERROR_CHOOSE_HELPER(count) ERROR_CHOOSE_HELPER1(count) 

#define ERROR(...) GLUE(ERROR_CHOOSE_HELPER(COUNT_ARGS_MAX5(__VA_ARGS__)),\ 
    (__VA_ARGS__)) 

#define ASSERT1(expr) singleArgumentExpansion(expr) 
#define ASSERT2(expr, explain) twoArgumentExpansion(expr, explain) 

#define ASSERT_CHOOSE_HELPER2(count) ASSERT##count 
#define ASSERT_CHOOSE_HELPER1(count) ASSERT_CHOOSE_HELPER2(count) 
#define ASSERT_CHOOSE_HELPER(count) ASSERT_CHOOSE_HELPER1(count) 

#define ASSERT(...) GLUE(ASSERT_CHOOSE_HELPER(COUNT_ARGS_MAX5(__VA_ARGS__)),\ 
    (__VA_ARGS__)) 

Nó không phải là một vấn đề lớn, tuy nhiên tôi thích mã của tôi càng ngắn gọn càng tốt. Nó cũng giúp theo cấp số nhân, nếu bạn đang sử dụng một số macro variadic, để giảm trùng lặp mã và các biến chứng có thể gây ra. Theo như tôi biết, phương pháp này cũng là di động. Tôi đã thử nghiệm nó trên nhiều trình biên dịch phổ biến nhất và chúng tạo ra kết quả tương tự.

Ví dụ sử dụng:

int foo() 
{ 
    ASSERT(one); // singleArgumentExpansion(one) 
    ASSERT(two, "foopy"); // twoArgumentExpansion(two, "foopy") 

    ERROR("Only print a title"); 
    ERROR("Error title", "Extended error description"); 
} 
+0

Lưu ý, tôi cần xóa ';' vào cuối '#define CALL_OVERLOAD' hoặc tôi gặp lỗi 'error: expected') 'before'; ' token' với gcc4.9 – ideasman42

+0

Dựa trên điều này, đây là ví dụ sử dụng tối đa 16 args để triển khai macro ELEM dựa trên var-args, http://stackoverflow.com/a/24837037/432509 (có thể quan tâm) – ideasman42

+0

@ ideasman42 Cảm ơn bạn đã chỉ ra rằng, cập nhật nó. –

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