พารามิเตอร์เสริมที่มีมาโคร C ++


105

มีวิธีรับพารามิเตอร์เสริมด้วยมาโคร C ++ หรือไม่? การบรรทุกเกินพิกัดก็จะดีเช่นกัน


1
เหมือนกันสำหรับ C: stackoverflow.com/questions/11761703/…ควรจะเหมือนกันเนื่องจากโดยพื้นฐานแล้วพรี
โปรเซสเซอร์

บางทีฟังก์ชั่นโอเวอร์โหลดพารามิเตอร์เริ่มต้นเทมเพลตตัวแปรหรืออาจเป็นไปได้ว่า idiom พารามิเตอร์ที่ตั้งชื่อเป็นสิ่งที่คุณกำลังมองหา
smoothware

โปรดอัปเดตคำตอบที่คุณเลือกเป็นคำตอบที่ได้รับการโหวตสูงพร้อมวิธีแก้ปัญหาจริงไม่ใช่คำตอบที่ได้รับการโหวตต่ำNo you can't
Albert Renshaw

คำตอบ:


158

นี่เป็นวิธีหนึ่งที่ทำได้ ใช้รายการอาร์กิวเมนต์สองครั้งโดยก่อนอื่นเพื่อสร้างชื่อของมาโครตัวช่วยจากนั้นจึงส่งอาร์กิวเมนต์ไปยังมาโครตัวช่วยนั้น ใช้เคล็ดลับมาตรฐานเพื่อนับจำนวนอาร์กิวเมนต์ให้กับมาโคร

enum
{
    plain = 0,
    bold = 1,
    italic = 2
};

void PrintString(const char* message, int size, int style)
{
}

#define PRINT_STRING_1_ARGS(message)              PrintString(message, 0, 0)
#define PRINT_STRING_2_ARGS(message, size)        PrintString(message, size, 0)
#define PRINT_STRING_3_ARGS(message, size, style) PrintString(message, size, style)

#define GET_4TH_ARG(arg1, arg2, arg3, arg4, ...) arg4
#define PRINT_STRING_MACRO_CHOOSER(...) \
    GET_4TH_ARG(__VA_ARGS__, PRINT_STRING_3_ARGS, \
                PRINT_STRING_2_ARGS, PRINT_STRING_1_ARGS, )

#define PRINT_STRING(...) PRINT_STRING_MACRO_CHOOSER(__VA_ARGS__)(__VA_ARGS__)

int main(int argc, char * const argv[])
{
    PRINT_STRING("Hello, World!");
    PRINT_STRING("Hello, World!", 18);
    PRINT_STRING("Hello, World!", 18, bold);

    return 0;
}

สิ่งนี้ทำให้ง่ายขึ้นสำหรับผู้เรียกมาโคร แต่ไม่ใช่ผู้เขียน


1
มันค่อนข้างดี แต่ฉันไม่คิดว่ามันจะได้ผลถ้าฉันเพิ่งทำ PRINT_STRING ในกรณีนั้นจะไม่มีการพิมพ์เริ่มต้น (และนั่นเป็นกรณีที่ฉันต้องการใช้) ยังคง +1 สำหรับเด็ดจริงๆ
Cenoc

2
ใช้ได้กับฉันใน gcc (และมันฉลาดมาก!) :-) แต่ใช้ไม่ได้กับฉันใน Visual Studio :-(
Tim Gradwell

3
@TimGradwell - เกิดจากข้อผิดพลาดในคอมไพเลอร์ MSVC ซึ่งพวกเขาได้รับการยอมรับ แต่ไม่ได้รับการแก้ไขในเกือบทศวรรษ อย่างไรก็ตามวิธีการแก้ปัญหาที่มีอยู่
BeeOnRope

ฉลาด แต่ใช้ไม่ได้กับอาร์กิวเมนต์มาโครตัวแปรที่เป็นทางเลือกเนื่องจากสิ่งที่คุณทำใน `` GET_4th_ARG ''
searchengine27

คือการที่PRINT_STRING_MACRO_CHOOSERจำเป็นยัง? ฉันสามารถแทนที่ด้วยร่างกายภายในโดยตรงและเรียกสิ่งนี้ว่าทั้งหมดได้(__VA_ARGS__)หรือไม่?
Herrgott

85

ด้วยความเคารพอย่างสูงต่อ Derek Ledbetter สำหรับคำตอบของเขา - และด้วยความขอโทษที่รื้อฟื้นคำถามเก่า ๆ

ได้รับความเข้าใจในสิ่งที่มันทำและยกขึ้นที่อื่น ๆ ในความสามารถในการ preceed ๆ__VA_ARGS__กับการ##อนุญาตให้ฉันจะเกิดขึ้นกับการเปลี่ยนแปลง ...

// The multiple macros that you would need anyway [as per: Crazy Eddie]
#define XXX_0()                     <code for no arguments> 
#define XXX_1(A)                    <code for one argument> 
#define XXX_2(A,B)                  <code for two arguments> 
#define XXX_3(A,B,C)                <code for three arguments> 
#define XXX_4(A,B,C,D)              <code for four arguments>  

// The interim macro that simply strips the excess and ends up with the required macro
#define XXX_X(x,A,B,C,D,FUNC, ...)  FUNC  

// The macro that the programmer uses 
#define XXX(...)                    XXX_X(,##__VA_ARGS__,\
                                          XXX_4(__VA_ARGS__),\
                                          XXX_3(__VA_ARGS__),\
                                          XXX_2(__VA_ARGS__),\
                                          XXX_1(__VA_ARGS__),\
                                          XXX_0(__VA_ARGS__)\
                                         ) 

สำหรับผู้ที่ไม่ใช่ผู้เชี่ยวชาญเช่นฉันที่สะดุดกับคำตอบ แต่ไม่เห็นว่ามันทำงานอย่างไรฉันจะดำเนินการตามขั้นตอนจริงโดยเริ่มจากรหัสต่อไปนี้ ...

XXX();
XXX(1); 
XXX(1,2); 
XXX(1,2,3); 
XXX(1,2,3,4); 
XXX(1,2,3,4,5);      // Not actually valid, but included to show the process 

กลายเป็น...

XXX_X(, XXX_4(), XXX_3(),  XXX_2(),    XXX_1(),      XXX_0()         );
XXX_X(, 1,       XXX_4(1), XXX_3(1),   XXX_2(1),     XXX_1(1),       XXX_0(1)          );
XXX_X(, 1,       2,        XXX_4(1,2), XXX_3(1,2),   XXX_2(1,2),     XXX_1(1,2),       XXX_0(1,2)        );
XXX_X(, 1,       2,        3,          XXX_4(1,2,3), XXX_3(1,2,3),   XXX_2(1,2,3),     XXX_1(1,2,3),     XXX_0(1,2,3)      );
XXX_X(, 1,       2,        3,          4,            XXX_4(1,2,3,4), XXX_3(1,2,3,4),   XXX_2(1,2,3,4),   XXX_1(1,2,3,4),   XXX_0(1,2,3,4)    );
XXX_X(, 1,       2,        3,          4,            5,              XXX_4(1,2,3,4,5), XXX_3(1,2,3,4,5), XXX_2(1,2,3,4,5), XXX_1(1,2,3,4,5), XXX_0(1,2,3,4,5) );

ซึ่งกลายเป็นเพียงข้อโต้แย้งที่หก ...

XXX_0(); 
XXX_1(1); 
XXX_2(1,2); 
XXX_3(1,2,3); 
XXX_4(1,2,3,4); 
5; 

PS: ลบ #define สำหรับ XXX_0 เพื่อรับข้อผิดพลาดในการคอมไพล์ [เช่น: ถ้าไม่อนุญาตตัวเลือกไม่มีอาร์กิวเมนต์]

PPS: น่าจะดีที่มีสถานการณ์ที่ไม่ถูกต้อง (เช่น 5) เป็นสิ่งที่ทำให้เกิดข้อผิดพลาดในการคอมไพล์ที่ชัดเจนกว่าสำหรับโปรแกรมเมอร์!

PPPS: ฉันไม่ใช่ผู้เชี่ยวชาญดังนั้นฉันมีความสุขมากที่ได้รับฟังความคิดเห็น (ดีไม่ดีหรืออื่น ๆ )!


3
คุณอาจได้รับข้อผิดพลาดในการคอมไพล์ที่ชัดเจนหากคุณแปลงอาร์กิวเมนต์ที่เลือกซึ่งควรจะเป็นชื่อ MACRO เป็นสตริงโดยใช้ # (เครื่องหมายปอนด์) และเปรียบเทียบกับอักขระ n ตัวแรกกับคำนำหน้าที่คาดไว้และหากไม่มีการจับคู่ให้พิมพ์ข้อมูล ข้อผิดพลาด
AturSams

1
ว้าวฉันไม่รู้ว่ามันได้ผลหรือเปล่า แต่อย่างน้อยก็สร้างสรรค์มาก!
การชดใช้อย่าง จำกัด

4
เหตุใดอาร์กิวเมนต์แรกจึงว่างเปล่าเสมอ ทำไมเราถึงมองข้ามมันไปไม่ได้: XXX_X(,##__VA_ARGS__,` ... XXX_X (, XXX_4 (), XXX_3 (), XXX_2 (), XXX_1 (), XXX_0 ()); `
rahman

2
อาร์กิวเมนต์แรกว่าง (ลูกน้ำ) มีความสำคัญ ## __ VA_ARGS__ ถ้ามันนำหน้าด้วยลูกน้ำ - มันจะลบลูกน้ำออกถ้า ## __ VA_ARGS__ ขยายเป็นอะไร คุณสามารถดูได้ในตัวอย่าง "กลายเป็น ... " เป็นบรรทัดแรก (ไม่มีอาร์กิวเมนต์) มีเพียง 6 พารามิเตอร์ แต่ส่วนที่เหลือจะได้รับ 7 เคล็ดลับนี้ช่วยให้มั่นใจได้ว่าสถานการณ์ไม่มีข้อโต้แย้งทำงาน
David Sorkovsky

@Eric - เกิดจากข้อบกพร่องในคอมไพเลอร์ของไมโครซอฟท์ แต่คุณสามารถดูคำถามนี้เพื่อหาวิธีแก้ปัญหาได้
BeeOnRope

31

มาโคร C ++ ไม่ได้เปลี่ยนจาก C เนื่องจาก C ไม่มีการโอเวอร์โหลดและอาร์กิวเมนต์เริ่มต้นสำหรับฟังก์ชันจึงไม่มีมาโครเหล่านี้อย่างแน่นอน ดังนั้นเพื่อตอบคำถามของคุณ: ไม่คุณลักษณะเหล่านี้ไม่มีอยู่สำหรับมาโคร ทางเลือกเดียวของคุณคือกำหนดมาโครหลายชื่อที่แตกต่างกัน (หรือไม่ใช้มาโครเลย)

ในฐานะที่เป็นแนวทาง: ใน C ++ โดยทั่วไปถือว่าเป็นแนวทางปฏิบัติที่ดีในการย้ายออกจากมาโครให้มากที่สุด หากคุณต้องการคุณสมบัติเช่นนี้มีโอกาสดีที่คุณจะใช้มาโครมากเกินไป


4
โปรดทราบว่าสาเหตุที่มาโคร "โอเวอร์โหลด" ไม่ได้เป็นเพราะมาโครไม่มีประเภทใด ๆ โดยธรรมชาติ มาโครขยายได้ง่าย
mk12

2
ถึงแม้ว่าผมจะใช้มาโครน้อยที่สุดเท่าที่เป็นไปได้ผมพบว่าการแก้จุดบกพร่องผ่านผลลัพธ์การสืบค้นได้รับค่อนข้างง่ายขึ้นเล็กน้อยกับสิ่งที่ต้องการ__FILE__และ__LINE__และเช่น ...
คริสเตียนเซเวอริน

ไม่ใช่คำตอบที่ดี นี่เป็นคำตอบที่ดี: stackoverflow.com/q/27049491/893406
v.oddou

การคอมไพล์ตามเงื่อนไขและการดีบัก / การบันทึกเป็นโดเมนที่มาโครมีประโยชน์และถูกต้องตามกฎหมาย โปรแกรมเมอร์ที่จริงจังทุกคนรู้ดี สิ่งที่ควรปฏิบัติคือหลีกเลี่ยงการใช้มาโครเพื่อกำหนดค่าคงที่และทำสิ่งที่เข้ารหัสระดับ C เพื่อสร้างเทมเพลตคอนเทนเนอร์ ฉันหวังว่า C ++ จะเพิ่มคุณสมบัติอื่น ๆ ให้กับมาโครด้วย พวกมันตั้งฉากกับแม่แบบ แน่นอนว่าดีที่สุดคือโค้ดเล็ตที่อนุญาตให้ฉันเพิ่มเครื่องกำเนิดไฟฟ้าลงในคอมไพเลอร์สำหรับภาษาเฉพาะของโดเมน (แง่มุม)
Lothar

1
ฉันคิดว่านี่ไม่ใช่คำตอบที่ดีเพราะมาโครเป็นสิ่งที่สมบูรณ์นอกเหนือจากตัวเลือกภาษา C ++ ใด ๆ เพราะจะได้รับการจัดการก่อนคอมไพเลอร์ ดังนั้นคุณสามารถทำสิ่งอื่น ๆ ได้และไม่มีคอมไพลเลอร์หรือตัวเชื่อมโยงที่ต้องปรับโค้ดให้เหมาะสมเพราะอาจจะไม่ได้เพิ่มประสิทธิภาพ
alabamajack

26

ด้วยความเคารพอย่างยิ่งต่อDerek Ledbetter , David Sorkovsky , Syphorlateสำหรับคำตอบของพวกเขาพร้อมกับวิธีการอันชาญฉลาดในการตรวจจับอาร์กิวเมนต์มาโครที่ว่างเปล่าโดยJens Gustedtที่

https://gustedt.wordpress.com/2010/06/08/detect-empty-macro-arguments/

ในที่สุดฉันก็ออกมาพร้อมกับบางสิ่งบางอย่างที่รวมเอากลเม็ดทั้งหมดไว้เพื่อแก้ปัญหา

  1. ใช้เฉพาะมาโครC99 มาตรฐานเพื่อให้ฟังก์ชันโอเวอร์โหลดไม่มีส่วนขยาย GCC / CLANG / MSVC ที่เกี่ยวข้อง (เช่นการกลืนด้วยเครื่องหมายจุลภาคโดยนิพจน์เฉพาะ, ##__VA_ARGS__สำหรับ GCC / CLANG และการกลืนโดยนัยโดย##__VA_ARGS__สำหรับ MSVC) ดังนั้นอย่าลังเลที่จะผ่านสิ่งที่ขาดหายไป--std=c99ยังคอมไพเลอร์ของคุณหากคุณต้องการ =)
  2. ใช้ได้กับอาร์กิวเมนต์เป็นศูนย์และไม่ จำกัด จำนวนอาร์กิวเมนต์หากคุณขยายเพิ่มเติมเพื่อให้เหมาะกับความต้องการของคุณ
  3. ทำงานข้ามแพลตฟอร์มอย่างสมเหตุสมผลอย่างน้อยก็ทดสอบ

    • GNU / Linux + GCC (GCC 4.9.2 บน CentOS 7.0 x86_64)
    • GNU / Linux + CLANG / LLVM , (CLANG / LLVM 3.5.0 บน CentOS 7.0 x86_64)
    • OS X + Xcode , (XCode 6.1.1 บน OS X Yosemite 10.10.1)
    • Windows + Visual Studio (Visual Studio 2013 Update 4 บน Windows 7 SP1 64 บิต)

สำหรับคนขี้เกียจให้ข้ามไปที่โพสต์สุดท้ายเพื่อคัดลอกแหล่งที่มา ด้านล่างนี้คือคำอธิบายโดยละเอียดซึ่งหวังว่าจะช่วยและสร้างแรงบันดาลใจให้กับทุกคนที่กำลังมองหา__VA_ARGS__วิธีแก้ปัญหาทั่วไปเช่นฉัน =)

นี่คือวิธีดำเนินการ ครั้งแรกกำหนดผู้ใช้มองเห็นมากเกินไป "ฟังก์ชั่น" ผมตั้งชื่อมันcreateและความหมายฟังก์ชั่นที่เกิดขึ้นจริงrealCreateและนิยามแมโครที่มีจำนวนที่แตกต่างกันของการขัดแย้งCREATE_2, CREATE_1, CREATE_0ที่แสดงด้านล่าง:

#define create(...) MACRO_CHOOSER(__VA_ARGS__)(__VA_ARGS__)

void realCreate(int x, int y)
{
  printf("(%d, %d)\n", x, y);
}

#define CREATE_2(x, y) realCreate(x, y)
#define CREATE_1(x) CREATE_2(x, 0)
#define CREATE_0() CREATE_1(0)

MACRO_CHOOSER(__VA_ARGS__)ส่วนหนึ่งในที่สุดมีมติให้ความหมายของชื่อแมโครและครั้งที่สอง(__VA_ARGS__)ส่วนประกอบด้วยรายการพารามิเตอร์ของพวกเขา ดังนั้นการเรียกร้องของผู้ใช้เพื่อcreate(10)แก้ไขไปCREATE_1(10)ที่CREATE_1ส่วนหนึ่งมาจากMACRO_CHOOSER(__VA_ARGS__)และ(10)ส่วนหนึ่งมาจากการที่สอง(__VA_ARGS__)ส่วนหนึ่งมาจากการที่สอง

MACRO_CHOOSERใช้เคล็ดลับว่าถ้า__VA_ARGS__ว่างเปล่านิพจน์ต่อไปนี้จะถูกตัดแบ่งเป็นโทรแมโครที่ถูกต้องโดย preprocessor นี้:

NO_ARG_EXPANDER __VA_ARGS__ ()  // simply shrinks to NO_ARG_EXPANDER()

อย่างชาญฉลาดเราสามารถกำหนดการเรียกมาโครที่เป็นผลลัพธ์นี้เป็น

#define NO_ARG_EXPANDER() ,,CREATE_0

สังเกตเครื่องหมายจุลภาคสองอันจะมีการอธิบายเร็ว ๆ นี้ มาโครที่มีประโยชน์ต่อไปคือ

#define MACRO_CHOOSER(...) CHOOSE_FROM_ARG_COUNT(NO_ARG_EXPANDER __VA_ARGS__ ())

ดังนั้นการโทรของ

create();
create(10);
create(20, 20);

จะขยายเป็น

CHOOSE_FROM_ARG_COUNT(,,CREATE_0)();
CHOOSE_FROM_ARG_COUNT(NO_ARG_EXPANDER 10 ())(10);
CHOOSE_FROM_ARG_COUNT(NO_ARG_EXPANDER 20, 20 ())(20, 20);

ตามที่ชื่อมาโครแนะนำเราจะนับจำนวนอาร์กิวเมนต์ในภายหลัง นี่คือเคล็ดลับอีกประการหนึ่ง: ตัวประมวลผลก่อนทำการเปลี่ยนข้อความธรรมดาเท่านั้น มันอนุมานจำนวนอาร์กิวเมนต์ของการเรียกมาโครจากจำนวนลูกน้ำที่เห็นภายในวงเล็บ "อาร์กิวเมนต์" จริงที่คั่นด้วยเครื่องหมายจุลภาคไม่จำเป็นต้องเป็นไวยากรณ์ที่ถูกต้อง สามารถเป็นข้อความใดก็ได้ กล่าวคือในตัวอย่างข้างต้นNO_ARG_EXPANDER 10 ()จะนับเป็น 1 อาร์กิวเมนต์สำหรับการเรียกกลาง NO_ARG_EXPANDER 20และ20 ()นับเป็น 2 อาร์กิวเมนต์สำหรับการเรียกด้านล่างตามลำดับ

หากเราใช้มาโครตัวช่วยต่อไปนี้เพื่อขยายเพิ่มเติม

##define CHOOSE_FROM_ARG_COUNT(...) \
  FUNC_RECOMPOSER((__VA_ARGS__, CREATE_2, CREATE_1, ))
#define FUNC_RECOMPOSER(argsWithParentheses) \
  FUNC_CHOOSER argsWithParentheses

ต่อท้าย,หลังจากที่CREATE_1มีการทำงานรอบสำหรับ GCC / เสียงดังกราวปราบปราม (การบวกเท็จ) ข้อผิดพลาดบอกว่าISO C99 requires rest arguments to be usedเมื่อผ่าน-pedanticการคอมไพเลอร์ของคุณ FUNC_RECOMPOSERเป็นงานรอบสำหรับ MSVC หรือมันไม่สามารถนับจำนวนของอาร์กิวเมนต์ (เช่นจุลภาค) ในวงเล็บของสายแมโครได้อย่างถูกต้อง ผลลัพธ์จะได้รับการแก้ไขต่อไป

FUNC_CHOOSER (,,CREATE_0, CREATE_2, CREATE_1, )();
FUNC_CHOOSER (NO_ARG_EXPANDER 10 (), CREATE_2, CREATE_1, )(10);
FUNC_CHOOSER (NO_ARG_EXPANDER 20, 20 (), CREATE_2, CREATE_1, )(20, 20);

อย่างที่คุณเห็นนกอินทรีขั้นตอนเดียวที่เราต้องการคือใช้เคล็ดลับการนับอาร์กิวเมนต์มาตรฐานเพื่อเลือกชื่อเวอร์ชันมาโครที่ต้องการในที่สุด:

#define FUNC_CHOOSER(_f1, _f2, _f3, ...) _f3

ซึ่งจะแก้ไขผลลัพธ์เป็น

CREATE_0();
CREATE_1(10);
CREATE_2(20, 20);

และให้การเรียกใช้ฟังก์ชันจริงที่ต้องการ:

realCreate(0, 0);
realCreate(10, 10);
realCreate(20, 20);

เมื่อรวมทั้งหมดเข้าด้วยกันด้วยการจัดเรียงคำสั่งใหม่เพื่อให้อ่านง่ายขึ้นแหล่งที่มาทั้งหมดของตัวอย่าง 2 อาร์กิวเมนต์อยู่ที่นี่:

#include <stdio.h>

void realCreate(int x, int y)
{
  printf("(%d, %d)\n", x, y);
}

#define CREATE_2(x, y) realCreate(x, y)
#define CREATE_1(x) CREATE_2(x, 0)
#define CREATE_0() CREATE_1(0)

#define FUNC_CHOOSER(_f1, _f2, _f3, ...) _f3
#define FUNC_RECOMPOSER(argsWithParentheses) FUNC_CHOOSER argsWithParentheses
#define CHOOSE_FROM_ARG_COUNT(...) FUNC_RECOMPOSER((__VA_ARGS__, CREATE_2, CREATE_1, ))
#define NO_ARG_EXPANDER() ,,CREATE_0
#define MACRO_CHOOSER(...) CHOOSE_FROM_ARG_COUNT(NO_ARG_EXPANDER __VA_ARGS__ ())
#define create(...) MACRO_CHOOSER(__VA_ARGS__)(__VA_ARGS__)

int main()
{
  create();
  create(10);
  create(20, 20);
  //create(30, 30, 30);  // Compilation error
  return 0;
}

แม้ว่าผู้พัฒนา API จะซับซ้อนน่าเกลียดและเป็นภาระ แต่ก็มีวิธีแก้ปัญหาสำหรับการโอเวอร์โหลดและการตั้งค่าพารามิเตอร์เสริมของฟังก์ชัน C / C ++ สำหรับพวกเราที่คลั่งไคล้ การใช้งาน API ที่ล้นมือที่กำลังจะมาถึงกลายเป็นเรื่องสนุกและน่าพอใจมาก =)

หากมีความเป็นไปได้เพิ่มเติมสำหรับแนวทางนี้โปรดแจ้งให้เราทราบที่

https://github.com/jason-deng/C99FunctionOverload

ขอขอบคุณเป็นพิเศษอีกครั้งสำหรับผู้คนที่ยอดเยี่ยมทุกคนที่เป็นแรงบันดาลใจและทำให้ฉันทำงานชิ้นนี้ให้สำเร็จ! =)


3
หนึ่งขยายเป็น 3 หรือ 4 ฟังก์ชันได้อย่างไร
Phylliida

@Phylliida ideone.com/jD0Hm5 - รองรับอาร์กิวเมนต์ตั้งแต่ศูนย์ถึงห้า
xx

9

สำหรับใครก็ตามที่กำลังค้นหาโซลูชัน VA_NARGS ที่ทำงานร่วมกับ Visual C ++ ได้อย่างเจ็บปวด มาโครต่อไปนี้ใช้งานได้อย่างไม่มีที่ติสำหรับฉัน (ยังมีพารามิเตอร์เป็นศูนย์!) ใน Visual c ++ express 2010:

#define VA_NUM_ARGS_IMPL(_1,_2,_3,_4,_5,_6,_7,_8,_9,_10,_11,_12,_13,_14,_15,_16,_17,_18,_19,_20,_21,_22,_23,_24,N,...) N
#define VA_NUM_ARGS_IMPL_(tuple) VA_NUM_ARGS_IMPL tuple
#define VA_NARGS(...)  bool(#__VA_ARGS__) ? (VA_NUM_ARGS_IMPL_((__VA_ARGS__, 24,23,22,21,20,19,18,17,16,15,14,13,12,11,10,9,8,7,6,5,4,3,2,1))) : 0

หากคุณต้องการมาโครที่มีพารามิเตอร์เสริมคุณสามารถทำได้:

//macro selection(vc++)
#define SELMACRO_IMPL(_1,_2,_3, N,...) N
#define SELMACRO_IMPL_(tuple) SELMACRO_IMPL tuple
#define mymacro1(var1) var1
#define mymacro2(var1,var2) var2*var1
#define mymacro3(var1,var2,var3) var1*var2*var3
#define mymacro(...) SELMACRO_IMPL_((__VA_ARGS__, mymacro3(__VA_ARGS__), mymacro2(__VA_ARGS__), mymacro1(__VA_ARGS__))) 

ที่ได้ผลสำหรับฉันเช่นกันใน vc. แต่ใช้ไม่ได้กับพารามิเตอร์ศูนย์

int x=99;
x=mymacro(2);//2
x=mymacro(2,2);//4
x=mymacro(2,2,2);//8

ฉันได้รับunresolved external symbol _bool referenced in function _main
Avidan Borisov

ใช่ว่าอาจเกิดขึ้นได้ในบางกรณี คุณต้องทราบว่าบูล (#__ VA_ARGS__)? แตกต่างจากมาโครอื่น ๆ เนื่องจากมีการประเมินในขณะทำงาน ขึ้นอยู่กับกรณีของคุณคุณสามารถละเว้นส่วนนั้นของรหัสได้
Syphorlate

2
ฉันลงเอยด้วยpastebin.com/H3T75dcnซึ่งทำงานได้อย่างสมบูรณ์ (0 อาร์กิวเมนต์ด้วย)
Avidan Borisov

ขอบคุณสำหรับลิงค์และใช่คุณสามารถทำได้โดยใช้ sizeof เช่นกัน แต่สำหรับฉันมันไม่ได้ผลในบางกรณี แต่หลักการเหมือนกัน (การประเมินแบบบูลีน)
Syphorlate

คุณช่วยยกตัวอย่างที่มันล้มเหลวได้ไหม?
Avidan Borisov


5
#include <stdio.h>

#define PP_NARG(...) \
    PP_NARG_(__VA_ARGS__,PP_RSEQ_N())
#define PP_NARG_(...) \
    PP_ARG_N(__VA_ARGS__)
#define PP_ARG_N( \
    _1, _2, _3, _4, _5, _6, _7, _8, _9,_10, \ 
    _11,_12,_13,_14,_15,_16,_17,_18,_19,_20, \
    _21,_22,_23,_24,_25,_26,_27,_28,_29,_30, \
    _31,_32,_33,_34,_35,_36,_37,_38,_39,_40, \
    _41,_42,_43,_44,_45,_46,_47,_48,_49,_50, \
    _51,_52,_53,_54,_55,_56,_57,_58,_59,_60, \
    _61,_62,_63,N,...) N
#define PP_RSEQ_N() \
    63,62,61,60,                   \
    59,58,57,56,55,54,53,52,51,50, \
    49,48,47,46,45,44,43,42,41,40, \
    39,38,37,36,35,34,33,32,31,30, \
    29,28,27,26,25,24,23,22,21,20, \
    19,18,17,16,15,14,13,12,11,10, \
    9,8,7,6,5,4,3,2,1,0

#define PP_CONCAT(a,b) PP_CONCAT_(a,b)
#define PP_CONCAT_(a,b) a ## b

#define THINK(...) PP_CONCAT(THINK_, PP_NARG(__VA_ARGS__))(__VA_ARGS__)
#define THINK_0() THINK_1("sector zz9 plural z alpha")
#define THINK_1(location) THINK_2(location, 42)
#define THINK_2(location,answer) THINK_3(location, answer, "deep thought")
#define THINK_3(location,answer,computer) \
  printf ("The answer is %d. This was calculated by %s, and a computer to figure out what this"
          " actually means will be build in %s\n", (answer), (computer), (location))

int
main (int argc, char *argv[])
{
  THINK (); /* On compilers other than GCC you have to call with least one non-default argument */
}

การปฏิเสธความรับผิด: ส่วนใหญ่ไม่เป็นอันตราย


มีข้อผิดพลาดในรหัสของคุณ กรุณาทำ:%s/MY_MACRO_/THINK_/g:)
João Portela

ยังใช้ไม่ได้กับศูนย์อาร์กิวเมนต์โดยใช้ g ++i686-apple-darwin10-g++-4.2.1 (GCC) 4.2.1 (Apple Inc. build 5664)
João Portela

1
ไม่มีอาร์กิวเมนต์เป็นศูนย์สำหรับมาโครตัวแปรเนื่องจากโทเค็นว่างเป็นตัวยึดที่ถูกต้อง
Paul Fultz II

3

นั่นไม่ใช่สิ่งที่พรีโปรเซสเซอร์ออกแบบมาสำหรับ

ที่กล่าวว่าถ้าคุณต้องการที่จะใส่ลงไปในพื้นที่อย่างจริงจังที่ท้าทายความสามารถในการเขียนโปรแกรมแมโครที่มีจำนวนเล็กน้อยของการอ่านที่คุณควรจะดูที่การเพิ่มห้องสมุด preprocessor ท้ายที่สุดมันจะไม่เป็น C ++ หากไม่มีระดับการเขียนโปรแกรมที่เข้ากันได้กับทัวริงสามระดับอย่างสมบูรณ์ (ตัวประมวลผลก่อนตัวประมวลผลแม่แบบการเขียนโปรแกรมและระดับพื้นฐาน C ++)!


3
#define MY_MACRO_3(X,Y,Z) ...
#define MY_MACRO_2(X,Y) MY_MACRO(X,Y,5)
#define MY_MACRO_1(X) MY_MACRO(X,42,5)

คุณจะรู้ว่าคุณจะผ่านไปกี่ args ดังนั้นจึงไม่จำเป็นต้องมีการโอเวอร์โหลด


2
ฉันถามเกี่ยวกับการมีอยู่ของคุณลักษณะนี้จริงๆ
Cenoc

3

รหัสของ Derek Ledbetter รุ่นที่กระชับยิ่งขึ้น:

enum
{
    plain = 0,
    bold = 1,
    italic = 2
};


void PrintString(const char* message = NULL, int size = 0, int style = 0)
{
}


#define PRINT_STRING(...) PrintString(__VA_ARGS__)


int main(int argc, char * const argv[])
{ 
    PRINT_STRING("Hello, World!");
    PRINT_STRING("Hello, World!", 18);
    PRINT_STRING("Hello, World!", 18, bold);

    return 0;
}

3

ในฐานะแฟนตัวยงของสัตว์ประหลาดมาโครที่น่ากลัวฉันต้องการขยายคำตอบของ Jason Deng และทำให้มันใช้งานได้จริง (เพื่อให้ดีขึ้นหรือแย่ลง) ต้นฉบับไม่น่าใช้มากนักเพราะคุณต้องปรับเปลี่ยนตัวอักษรตัวใหญ่ทุกครั้งที่คุณต้องการสร้างมาโครใหม่และจะยิ่งแย่ไปกว่านั้นหากคุณต้องการอาร์กิวเมนต์ที่แตกต่างกัน

ดังนั้นฉันจึงสร้างเวอร์ชันที่มีคุณสมบัติเหล่านี้:

  • 0 กรณีอาร์กิวเมนต์ใช้ได้ผล
  • 1 ถึง 16 อาร์กิวเมนต์โดยไม่มีการแก้ไขส่วนที่ยุ่ง
  • ง่ายต่อการเขียนฟังก์ชันมาโครเพิ่มเติม
  • ผ่านการทดสอบใน gcc 10, เสียงดัง 9, Visual Studio 2017

ตอนนี้ฉันสร้างอาร์กิวเมนต์ได้สูงสุด 16 อาร์กิวเมนต์ แต่ถ้าคุณต้องการมากกว่านั้น (ตอนนี้คุณแค่โง่ ... ) คุณสามารถแก้ไข FUNC_CHOOSER และ CHOOSE_FROM_ARG_COUNT จากนั้นเพิ่มเครื่องหมายจุลภาคใน NO_ARG_EXPANDER

โปรดดูคำตอบที่ยอดเยี่ยมของ Jason Deng สำหรับรายละเอียดเพิ่มเติมเกี่ยวกับการใช้งาน แต่ฉันจะใส่รหัสที่นี่:

#include <stdio.h>

void realCreate(int x, int y)
{
    printf("(%d, %d)\n", x, y);
}

// This part you put in some library header:
#define FUNC_CHOOSER(_f0, _f1, _f2, _f3, _f4, _f5, _f6, _f7, _f8, _f9, _f10, _f11, _f12, _f13, _f14, _f15, _f16, ...) _f16
#define FUNC_RECOMPOSER(argsWithParentheses) FUNC_CHOOSER argsWithParentheses
#define CHOOSE_FROM_ARG_COUNT(F, ...) FUNC_RECOMPOSER((__VA_ARGS__, \
            F##_16, F##_15, F##_14, F##_13, F##_12, F##_11, F##_10, F##_9, F##_8,\
            F##_7, F##_6, F##_5, F##_4, F##_3, F##_2, F##_1, ))
#define NO_ARG_EXPANDER(FUNC) ,,,,,,,,,,,,,,,,FUNC ## _0
#define MACRO_CHOOSER(FUNC, ...) CHOOSE_FROM_ARG_COUNT(FUNC, NO_ARG_EXPANDER __VA_ARGS__ (FUNC))
#define MULTI_MACRO(FUNC, ...) MACRO_CHOOSER(FUNC, __VA_ARGS__)(__VA_ARGS__)

// When you need to make a macro with default arguments, use this:
#define create(...) MULTI_MACRO(CREATE, __VA_ARGS__)
#define CREATE_0() CREATE_1(0)
#define CREATE_1(x) CREATE_2(x, 0)
#define CREATE_2(x, y) \
    do { \
        /* put whatever code you want in the last macro */ \
        realCreate(x, y); \
    } while(0)


int main()
{
    create();
    create(10);
    create(20, 20);
    //create(30, 30, 30);  // Compilation error
    return 0;
}

2

คุณสามารถใช้BOOST_PP_OVERLOADจากboostห้องสมุด

ตัวอย่างจากเอกสารการเพิ่มอย่างเป็นทางการ :

#include <boost/preprocessor/facilities/overload.hpp>
#include <boost/preprocessor/cat.hpp>
#include <boost/preprocessor/facilities/empty.hpp>
#include <boost/preprocessor/arithmetic/add.hpp>

#define MACRO_1(number) MACRO_2(number,10)
#define MACRO_2(number1,number2) BOOST_PP_ADD(number1,number2)

#if !BOOST_PP_VARIADICS_MSVC

#define MACRO_ADD_NUMBERS(...) BOOST_PP_OVERLOAD(MACRO_,__VA_ARGS__)(__VA_ARGS__)

#else

// or for Visual C++

#define MACRO_ADD_NUMBERS(...) \
  BOOST_PP_CAT(BOOST_PP_OVERLOAD(MACRO_,__VA_ARGS__)(__VA_ARGS__),BOOST_PP_EMPTY())

#endif

MACRO_ADD_NUMBERS(5) // output is 15
MACRO_ADD_NUMBERS(3,6) // output is 9

0

ขึ้นอยู่กับสิ่งที่คุณต้องการคุณสามารถทำได้ด้วยvar argsด้วยมาโคร ตอนนี้พารามิเตอร์ที่เป็นทางเลือกหรือการโอเวอร์โหลดมาโครไม่มีสิ่งนั้น


-1

ไม่มีตัวอย่างข้างต้น (จาก Derek Ledbetter, David Sorkovsky และ Joe D) ที่จะนับอาร์กิวเมนต์ด้วยมาโครที่ใช้งานได้สำหรับฉันโดยใช้ Microsoft VCC 10 __VA_ARGS__อาร์กิวเมนต์นี้ถือเป็นอาร์กิวเมนต์เดียวเสมอ (โทเค็น - เพิ่มขนาดด้วย## or not) ดังนั้น อาร์กิวเมนต์ที่เปลี่ยนไปซึ่งตัวอย่างเหล่านั้นใช้ไม่ได้ผล

ดังนั้นคำตอบสั้น ๆ ตามที่ระบุไว้ข้างต้น: ไม่คุณไม่สามารถโอเวอร์โหลดมาโครหรือใช้อาร์กิวเมนต์ที่เป็นทางเลือกได้


1
คุณสามารถทำได้ แต่ใน C99 หรือ C ++ 11 เท่านั้น (เนื่องจากมี __VA_ARGS__) VC2010 คือ C89 / C ++ 03 (โดยบางบิตของ C ++ 11 เริ่มเปิดขึ้น แต่ยังไม่พบ)
puetzk
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.