ฉันจะแสดงค่าของ #define ในเวลาคอมไพล์ได้อย่างไร


125

ฉันกำลังพยายามหาว่า Boost เวอร์ชันใดที่คิดว่าใช้อยู่ ฉันต้องการทำสิ่งนี้:

#error BOOST_VERSION

แต่พรีโปรเซสเซอร์ไม่ขยาย BOOST_VERSION

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


7
สำหรับผู้เยี่ยมชมในอนาคต ... Chris Barry ให้วิธีแก้ปัญหาทั่วไปในตอนท้าย (ปราศจากสิ่งเฉพาะของ Boost)
jww

คำตอบ:


119

ฉันรู้ว่านี่เป็นเวลานานหลังจากข้อความค้นหาเดิม แต่อาจยังมีประโยชน์

สามารถทำได้ใน GCC โดยใช้ตัวดำเนินการ stringify "#" แต่ต้องใช้สองขั้นตอน

#define XSTR(x) STR(x)
#define STR(x) #x

จากนั้นค่าของมาโครสามารถแสดงด้วย:

#pragma message "The value of ABC: " XSTR(ABC)

โปรดดูที่ 3.4 สตริงในเอกสารออนไลน์ของ gcc

มันทำงานอย่างไร:

ตัวประมวลผลก่อนเข้าใจสตริงที่ยกมาและจัดการต่างจากข้อความปกติ การต่อสายอักขระเป็นตัวอย่างของการปฏิบัติพิเศษนี้ ข้อความ pragma ต้องการอาร์กิวเมนต์ที่เป็นสตริงที่ยกมา เมื่อมีองค์ประกอบมากกว่าหนึ่งในอาร์กิวเมนต์พวกเขาทั้งหมดจะต้องเป็นสตริงเพื่อให้สามารถใช้การต่อสตริงได้ ตัวประมวลผลก่อนไม่สามารถสันนิษฐานได้ว่าสตริงที่ไม่ได้ใส่เครื่องหมายคำพูดควรได้รับการปฏิบัติราวกับว่าถูกยกมา หากเป็นเช่นนั้น:

#define ABC 123
int n = ABC;

จะไม่รวบรวม

ตอนนี้พิจารณา:

#define ABC abc
#pragma message "The value of ABC is: " ABC

ซึ่งเทียบเท่ากับ

#pragma message "The value of ABC is: " abc

สิ่งนี้ทำให้เกิดคำเตือนก่อนตัวประมวลผลเนื่องจากไม่สามารถเชื่อม abc (ไม่ใส่เครื่องหมายคำพูด) กับสตริงที่อยู่ก่อนหน้า

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

#define STR(x) #x
char *s1 = "abc";
char *s2 = STR(abc);

จะกำหนดค่าที่เหมือนกันให้กับ s1 และ s2 หากคุณเรียกใช้ gcc -E คุณจะเห็นสิ่งนี้ในเอาต์พุต บางที STR อาจจะดีกว่าชื่อ ENQUOTE

วิธีนี้ช่วยแก้ปัญหาในการใส่เครื่องหมายคำพูดรอบ ๆ รายการที่ไม่ได้ใส่เครื่องหมายคำพูดปัญหาตอนนี้คือถ้าอาร์กิวเมนต์เป็นมาโครมาโครจะไม่ถูกขยาย นี่คือสาเหตุที่ต้องใช้มาโครตัวที่สอง XSTR ขยายอาร์กิวเมนต์จากนั้นเรียก STR เพื่อใส่ค่าที่ขยายลงในเครื่องหมายคำพูด


3
ฉันสงสัยว่าทำไมต้องใช้สองขั้นตอน
Vincent Fourmond

4
@ VincentFourmond หากไม่มีระยะ XSTR มาโครจะไม่ขยาย ดังนั้นหากคุณทำ # กำหนด ABC 42 \ n STR (ABC) คุณจะได้ "ABC" ดูgcc.gnu.org/onlinedocs/cpp/Stringification.html
rob05c

นอกจากนี้ยังใช้งานได้ดีกับ Xcode 8 __IPHONE_9_3เช่นแทนที่เอบีซีด้วย
funroll

คำศัพท์ GCC ดูเหมือนจะเปลี่ยนไปและด้วย URL ซึ่งตอนนี้คือhttps://gcc.gnu.org/onlinedocs/cpp/Stringizing.html#Stringizing
Chris Barry

119

BOOST_PP_STRINGIZE ดูเหมือนจะเป็นทางออกที่ยอดเยี่ยมสำหรับ C ++ แต่ไม่ใช่สำหรับ C ทั่วไป

นี่คือทางออกของฉันสำหรับ GNU CPP:

/* Some test definition here */
#define DEFINED_BUT_NO_VALUE
#define DEFINED_INT 3
#define DEFINED_STR "ABC"

/* definition to expand macro then apply to pragma message */
#define VALUE_TO_STRING(x) #x
#define VALUE(x) VALUE_TO_STRING(x)
#define VAR_NAME_VALUE(var) #var "="  VALUE(var)

/* Some example here */
#pragma message(VAR_NAME_VALUE(NOT_DEFINED))
#pragma message(VAR_NAME_VALUE(DEFINED_BUT_NO_VALUE))
#pragma message(VAR_NAME_VALUE(DEFINED_INT))
#pragma message(VAR_NAME_VALUE(DEFINED_STR))

คำจำกัดความข้างต้นส่งผลให้:

test.c:10:9: note: #pragma message: NOT_DEFINED=NOT_DEFINED
test.c:11:9: note: #pragma message: DEFINED_BUT_NO_VALUE=
test.c:12:9: note: #pragma message: DEFINED_INT=3
test.c:13:9: note: #pragma message: DEFINED_STR="ABC"

สำหรับ"กำหนดเป็น interger" , "กำหนดเป็นสตริง"และ"ที่กำหนดไว้ แต่ไม่มีค่า"ตัวแปรที่พวกเขาทำงานได้ดี สำหรับตัวแปรที่"ไม่ได้กำหนด"เท่านั้นโดยจะแสดงเหมือนกับชื่อตัวแปรดั้งเดิมทุกประการ คุณต้องเคยชิน - หรืออาจมีใครบางคนสามารถให้ทางออกที่ดีกว่านี้ได้


! ที่ดีเยี่ยม มีประสบการณ์เกี่ยวกับ ARM RVCT หรือไม่? ดูเหมือนว่าจะไม่มีคุณลักษณะ "Stringification" เป็น GCC infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.dui0491c/…
xdan

2
ทางออกที่ดี อย่างไรก็ตามหากต้องการแสดงขนาดของค่าที่คำนวณเวลาคอมไพล์เช่นขนาดของโครงสร้างที่ซับซ้อนสามารถทำได้หรือไม่ วิธีการที่แนะนำในคำตอบนี้ดูเหมือนจะสร้างขึ้นDEFINED_INT=(sizeof(MY_STRUCT))โดยไม่มีsizeofการประเมินตัวดำเนินการ
Carl

(การเพิ่มความคิดเห็น: ไม่คาดคิดเนื่องจากเป็นคอมไพเลอร์แทนที่จะเป็นตัวประมวลผลล่วงหน้าที่จะประเมินsizeofอย่างไรก็ตามยังคงสงสัยว่ามีวิธีที่ชาญฉลาดในการบรรลุสิ่งนี้หรือไม่)
คาร์ล

@xdan ทางออกที่ดีน่าเสียดายที่ไม่รองรับสิ่งต่างๆเช่น#define masks {0xff, 0xaf, 0x0f}
Simon Bagley

59

หากคุณใช้ Visual C ++ คุณสามารถใช้#pragma message:

#include <boost/preprocessor/stringize.hpp>
#pragma message("BOOST_VERSION=" BOOST_PP_STRINGIZE(BOOST_VERSION))

แก้ไข:ขอบคุณ LB สำหรับลิงค์

เห็นได้ชัดว่าเทียบเท่ากับ GCC คือ (ไม่ผ่านการทดสอบ):

#pragma message "BOOST_VERSION=" BOOST_PP_STRINGIZE(BOOST_VERSION)

5
ที่เรียกว่า pragmas วินิจฉัยgcc.gnu.org/onlinedocs/gcc/…
LB40

4
คงจะดีไม่น้อยหากคุณใส่คำจำกัดความBOOST_PP_STRINGIZEที่ดีและสั้นและคัดลอก / วางได้
Timmmm

ทำงานได้ดีภายใต้ gcc :)
Thomas Legris

14

เท่าที่ผมรู้ว่า '#error' เท่านั้นจะพิมพ์สตริงในความเป็นจริงคุณไม่จำเป็นต้องแม้กระทั่งคำพูดที่ใช้

คุณได้ลองเขียนโค้ดที่ไม่ถูกต้องโดยเจตนาโดยใช้ "BOOST_VERSION" หรือไม่ อาจจะมีบางอย่างเช่น "blah [BOOST_VERSION] = foo;" จะบอกคุณบางอย่างเช่น "string literal 1.2.1 ไม่สามารถใช้เป็นที่อยู่อาร์เรย์ได้" มันจะไม่ใช่ข้อความแสดงข้อผิดพลาด แต่อย่างน้อยมันก็จะแสดงค่าที่เกี่ยวข้องให้คุณเห็น คุณสามารถเล่นไปเรื่อย ๆ จนกว่าคุณจะพบข้อผิดพลาดในการคอมไพล์ที่บอกค่า


ไม่ได้ผลเนื่องจาก BOOST_VERSION เป็นจำนวนเต็ม แต่ฉันต้องดูด้วยคำสั่งนี้: std::vector<BOOST_VERSION>;ใน gcc 4.4.1 ขอบคุณ!
Jim Hunziker

โปรดทราบว่าด้วย Visual C ++ คุณจะต้องใช้คำตอบของ Bojan Resnik
Raphaël Saint-Pierre

ฉันพยายามทำให้มันใช้งานได้ แต่ข้อความแสดงข้อผิดพลาดที่ GCC ให้มานั้นไม่สามารถอธิบายได้อย่างน่าเศร้า แต่ +1 สำหรับการกล่าวถึงมัน
Chris Lutz

14

โดยไม่ต้องเพิ่ม:

  1. กำหนดมาโครเดียวกันอีกครั้งและคอมไพเลอร์ HIMSELF จะเตือน

  2. จากคำเตือนคุณสามารถดูตำแหน่งของคำจำกัดความก่อนหน้าได้

  3. vi ของคำจำกัดความก่อนหน้า

ambarish@axiom:~/cpp$ g++ shiftOper.cpp
shiftOper.cpp:7:1: warning: "LINUX_VERSION_CODE" redefined
shiftOper.cpp:6:1: warning: this is the location of the previous definition

#define LINUX_VERSION_CODE 265216
#define LINUX_VERSION_CODE 666

int main ()
{

}

อันนี้ง่ายกว่าและตรงไปตรงมา
Tmx

1
ตัวมันเอง : คอมไพเลอร์ไม่มีเพศ
Sky

สิ่งนี้ใช้ไม่ได้กับมาโครที่กำหนดไว้ล่วงหน้าเช่น__cplusplus.
ManuelAtWork

11

ใน Microsoft C / C ++ คุณสามารถใช้ในตัว_CRT_STRINGIZE()เพื่อพิมพ์ค่าคงที่ stdafx.hไฟล์จำนวนมากของฉันมีการรวมกันของสิ่งเหล่านี้:

#pragma message("_MSC_VER      is " _CRT_STRINGIZE(_MSC_VER))
#pragma message("_MFC_VER      is " _CRT_STRINGIZE(_MFC_VER))
#pragma message("_ATL_VER      is " _CRT_STRINGIZE(_ATL_VER))
#pragma message("WINVER        is " _CRT_STRINGIZE(WINVER))
#pragma message("_WIN32_WINNT  is " _CRT_STRINGIZE(_WIN32_WINNT))
#pragma message("_WIN32_IE     is " _CRT_STRINGIZE(_WIN32_IE))
#pragma message("NTDDI_VERSION is " _CRT_STRINGIZE(NTDDI_VERSION)) 

และแสดงผลลัพธ์ดังนี้:

_MSC_VER      is 1915
_MFC_VER      is 0x0E00
_ATL_VER      is 0x0E00
WINVER        is 0x0600
_WIN32_WINNT  is 0x0600
_WIN32_IE     is 0x0700
NTDDI_VERSION is 0x06000000

5
#define a <::BOOST_VERSION>
#include a
MSVC2015 : ข้อผิดพลาดร้ายแรง C1083: ไม่สามารถเปิดไฟล์รวม: ':: 106200': ไม่มีไฟล์หรือไดเร็กทอรีดังกล่าว

ใช้งานได้แม้ว่าpreprocess to fileจะเปิดใช้งานแม้ว่าจะมีโทเค็นที่ไม่ถูกต้องอยู่ก็ตาม:

#define a <::'*/`#>
#include a
MSVC2015 : ข้อผิดพลาดร้ายแรง C1083: ไม่สามารถเปิดไฟล์รวม: '::' * / `# ': ไม่มีไฟล์หรือไดเร็กทอรี
GCC4.x : คำเตือน: อักขระสิ้นสุดการสิ้นสุด [-Winvalid-pp-token]
# กำหนด a <:: * / `#>

Build error: #include expects "FILENAME" or <FILENAME>เหมืองแร่เพียงแค่พูดว่า ถอนหายใจ
endolith

@endolith คอมไพเลอร์และเวอร์ชันอะไร
Andry

DP8051 Keil 9.51 :)
endolith

@endolith ดูเหมือนว่าคอมไพเลอร์นี้มีข้อ จำกัด ในการประมวลผลล่วงหน้า: keil.com/support/man/docs/c51/c51_pp_directives.htmแต่ในด้านของฉันมันเกือบจะทำงานได้ตามที่คาดไว้ฉันเพิ่งลบอักขระที่ไม่ถูกต้องบางตัวเช่น':*** WARNING C318 IN LINE 2 OF test.c: can't open file '::*/`'
Andry

ขอบคุณสิ่งนี้ช่วยฉันได้เนื่องจากข้อความ pragma ไม่ได้ถูกนำไปใช้ในคอมไพเลอร์ที่ฉันใช้
CodeMonkey

3

คุณยังสามารถประมวลผลไฟล์ต้นทางล่วงหน้าและดูว่าค่าตัวประมวลผลล่วงหน้าประเมินว่าเป็นอย่างไร


2

คุณกำลังมองหา

#if BOOST_VERSION != "1.2"
#error "Bad version"
#endif

ไม่ดีนักหาก BOOST_VERSION เป็นสตริงอย่างที่ฉันสันนิษฐานไว้ แต่อาจมีจำนวนเต็มแต่ละตัวที่กำหนดไว้สำหรับตัวเลขหลักรองและตัวเลขแก้ไข


ฉันคิดว่าผู้ส่งไม่ต้องการ (เพียง) บังคับใช้ค่าใดค่าหนึ่งพวกเขาต้องการดูว่าค่าปัจจุบันคืออะไร
KeyserSoze

นี่เป็นสิ่งเดียวที่เหมาะกับฉัน ฉันสามารถเปลี่ยน#if VARIABLE == 123คำสั่งได้ทันทีและการเน้นไวยากรณ์บอกฉันว่ามันเป็นค่าที่ฉันคิดหรือไม่ ...
endolith

2

การมองไปที่เอาต์พุตของตัวประมวลผลก่อนเป็นสิ่งที่ใกล้เคียงที่สุดกับคำตอบที่คุณต้องการ

ฉันรู้ว่าคุณได้ยกเว้นสิ่งนั้น (และวิธีอื่น ๆ ) แต่ฉันไม่แน่ใจว่าทำไม คุณมีปัญหาที่เฉพาะเจาะจงเพียงพอที่จะแก้ไข แต่คุณยังไม่ได้อธิบายว่าเหตุใดวิธี "ปกติ" จึงไม่ได้ผลสำหรับคุณ


นี่อาจเป็นคำตอบที่ถูกต้องสำหรับปัญหาทั่วไป
jww

1

คุณสามารถเขียนโปรแกรมที่พิมพ์BOOST_VERSIONและคอมไพล์และเรียกใช้เป็นส่วนหนึ่งของระบบบิลด์ของคุณ มิฉะนั้นฉันคิดว่าคุณโชคไม่ดี


สำหรับกรณีของเวอร์ชันซอฟต์แวร์ที่กำหนดไว้ในส่วนหัวคุณอาจปลอดภัย (และเป็นคำตอบที่ดี) แต่ในทางแก้ปัญหาทั่วไปข้อเสียที่เป็นไปได้คือการทำให้แอปทดสอบและแอปจริงของคุณมีค่า #define เท่ากัน - ขึ้นอยู่กับเส้นทางที่รวมไว้ #defines อื่น ๆ ที่อาจใช้เพื่อตั้งค่าของแอปนั้น , CFLAGS ส่งไปยังคอมไพเลอร์ ฯลฯ
KeyserSoze

พิมพ์จากโปรแกรมจริงของคุณ หากเป็นแบบกราฟิกให้ใส่ไว้ในกล่องโต้ตอบ "เกี่ยวกับ" หากบรรทัดคำสั่งให้เป็นตัวเลือก (ส่วนหนึ่งของ --version อาจจะ) หากเป็น daemon ให้เขียนลงในล็อกไฟล์ ถ้าฝังให้หาวิธีอื่น
divegeek

@swillden - OP ต้องการในเวลาคอมไพล์ไม่ใช่ที่รันไทม์
Chris Lutz

นอกจากนี้ยังมีแนวโน้มที่จะทำลายบิลด์ที่ใช้คอมไพเลอร์ข้าม
Craig Ringer


1

ดูเอกสาร Boost เกี่ยวกับวิธีการใช้มาโคร:

อ้างอิงBOOST_VERSIONจากhttp://www.boost.org/doc/libs/1_37_0/libs/config/doc/html/boost_config/boost_macro_reference.html#boost_config.boost_macro_reference.boost_helper_macros :

อธิบายหมายเลขเวอร์ชันบูสต์ในรูปแบบ XXYYZZ เช่น: (BOOST_VERSION % 100)เป็นเวอร์ชันรองย่อยเป็นเวอร์ชันรองและ เป็นเวอร์ชันหลัก((BOOST_VERSION / 100) % 1000)(BOOST_VERSION / 100000)


0

แทนที่จะใช้ #error ให้ลองกำหนดมาโครใหม่ก่อนที่จะใช้งาน การคอมไพเลอร์จะล้มเหลวและคอมไพเลอร์จะให้ค่าปัจจุบันที่คิดว่าใช้กับมาโคร

#define BOOST_VERSION blah

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.