Làm cách nào để hiển thị giá trị của #define tại thời điểm biên dịch?


123

Tôi đang cố gắng tìm ra phiên bản Boost mà mã của tôi nghĩ rằng nó đang sử dụng. Tôi muốn làm một cái gì đó như thế này:

#error BOOST_VERSION

nhưng bộ xử lý trước không mở rộng BOOST_VERSION.

Tôi biết tôi có thể in nó ra trong thời gian chạy từ chương trình và tôi biết tôi có thể nhìn vào kết quả đầu ra của bộ tiền xử lý để tìm câu trả lời. Tôi cảm thấy có một cách làm điều này trong quá trình biên dịch có thể hữu ích.


7
Đối với những khách truy cập trong tương lai ... Chris Barry cung cấp giải pháp tổng quát ở cuối (không có công cụ cụ thể của Boost).
jww

Câu trả lời:


117

Tôi biết rằng đây là một thời gian dài sau truy vấn ban đầu, nhưng điều này có thể vẫn hữu ích.

Điều này có thể được thực hiện trong GCC bằng cách sử dụng toán tử stringify "#", nhưng nó yêu cầu hai giai đoạn.

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

Giá trị của macro sau đó có thể được hiển thị bằng:

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

Xem: 3.4 Cấu trúc chuỗi trong tài liệu trực tuyến gcc.

Làm thế nào nó hoạt động:

Bộ tiền xử lý hiểu các chuỗi được trích dẫn và xử lý chúng khác với văn bản bình thường. Nối chuỗi là một ví dụ về cách xử lý đặc biệt này. Thông báo pragma yêu cầu một đối số là một chuỗi được trích dẫn. Khi có nhiều hơn một thành phần trong đối số thì tất cả chúng phải là chuỗi để có thể áp dụng nối chuỗi. Bộ tiền xử lý không bao giờ có thể cho rằng một chuỗi chưa được trích dẫn phải được coi như thể nó đã được trích dẫn. Nếu nó đã làm thì:

#define ABC 123
int n = ABC;

sẽ không biên dịch.

Bây giờ hãy xem xét:

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

tương đương với

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

Điều này gây ra cảnh báo bộ xử lý trước vì abc (chưa được trích dẫn) không thể được nối với chuỗi trước đó.

Bây giờ hãy xem xét chuỗi tiền xử lý (Trước đây được gọi là chuỗi hóa, các liên kết trong tài liệu đã được thay đổi để phản ánh thuật ngữ sửa đổi. liên kết của bạn.)) toán tử. Điều này chỉ hoạt động trên các đối số của macro và thay thế đối số không được mở rộng bằng đối số được đặt trong dấu ngoặc kép. Như vậy:

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

sẽ gán các giá trị giống nhau cho s1 và s2. Nếu bạn chạy gcc -E, bạn có thể thấy điều này trong đầu ra. Có lẽ STR nên được đặt tên giống như ENQUOTE.

Điều này giải quyết vấn đề đặt dấu ngoặc kép xung quanh một mục không được trích dẫn, vấn đề bây giờ là, nếu đối số là một macro, thì macro sẽ không được mở rộng. Đây là lý do tại sao macro thứ hai là cần thiết. XSTR mở rộng đối số của nó, sau đó gọi STR để đặt giá trị được mở rộng vào dấu ngoặc kép.


3
Tôi tò mò là tại sao nó đòi hỏi hai giai đoạn
Vincent Fourmond

4
@VincentFourmond Nếu không có giai đoạn XSTR, macro sẽ không được mở rộng. Vì vậy, nếu bạn đã # xác định ABC 42 \ n STR (ABC), bạn sẽ nhận được "ABC". Xem gcc.gnu.org/onlineocs/cpp/Stringification.html
rob05c

Điều này cũng hoạt động tốt với Xcode 8, ví dụ như thay thế ABC bằng __IPHONE_9_3.
funroll

GCC ngữ dường như đã thay đổi, và cùng với nó URL, mà bây giờ https://gcc.gnu.org/onlinedocs/cpp/Stringizing.html#Stringizing
Chris Barry

119

BOOST_PP_STRINGIZE có vẻ là một giải pháp tuyệt vời cho C ++, nhưng không phải cho C thông thường.

Đây là giải pháp của tôi cho 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))

Các định nghĩa trên dẫn đến:

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"

Đối với các biến "được xác định là interger" , "được xác định là chuỗi""được xác định nhưng không có giá trị" , chúng hoạt động tốt. Chỉ đối với biến "không được xác định" , chúng hiển thị giống hệt như tên biến ban đầu. Bạn phải quen với nó - hoặc có thể ai đó có thể cung cấp giải pháp tốt hơn.


thông minh! Bất kỳ kinh nghiệm trong ARM RVCT? nó dường như không có "Stringification" tính năng như GCC infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.dui0491c/...
xdan

2
Giải pháp tuyệt vời. Tuy nhiên, nếu tôi muốn hiển thị kích thước của một giá trị được tính toán theo thời gian biên dịch, chẳng hạn như kích thước của một cấu trúc phức tạp, điều này có thể thực hiện được không? Phương thức được đề xuất trong câu trả lời này dường như tạo ra DEFINED_INT=(sizeof(MY_STRUCT)), mà không sizeofđánh giá toán tử.
Carl

(Chú thích thêm: không bất ngờ, vì nó là trình biên dịch chứ không phải xử lý trước đó sẽ đánh giá sizeof. Tuy nhiên, vẫn tò mò nếu có một cách khéo léo để đạt được điều này)
Carl

@xdan giải pháp tốt, thật không may là không phục vụ cho những thứ như#define masks {0xff, 0xaf, 0x0f}
Simon Bagley

59

Nếu bạn đang sử dụng Visual C ++, bạn có thể sử dụng #pragma message:

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

Biên tập: Cảm ơn LB cho liên kết

Rõ ràng, GCC tương đương là (không được thử nghiệm):

#pragma message "BOOST_VERSION=" BOOST_PP_STRINGIZE(BOOST_VERSION)

5
Đó được gọi là pragmas chẩn đoán, gcc.gnu.org/onlineocs/gcc/…
LB40

4
Sẽ rất tuyệt nếu bạn bao gồm định nghĩa vềBOOST_PP_STRINGIZE cái nào đẹp và ngắn gọn và sao chép / dán được.
Timmmm

Hoạt động tốt dưới gcc :)
Thomas LEGRIS

14

Theo như tôi biết '#error' chỉ in các chuỗi, trên thực tế bạn thậm chí không cần sử dụng dấu ngoặc kép .

Bạn đã thử viết nhiều mã có mục đích không chính xác bằng "BOOST_VERSION" chưa? Có lẽ cái gì đó giống như "blah [BOOST_VERSION] = foo;" sẽ cho bạn biết một cái gì đó như "chuỗi ký tự 1.2.1 không thể được sử dụng làm địa chỉ mảng". Nó sẽ không phải là một thông báo lỗi, nhưng ít nhất nó sẽ cho bạn thấy giá trị liên quan. Bạn có thể thử cho đến khi tìm thấy lỗi biên dịch cho bạn biết giá trị.


Điều đó không hiệu quả, vì BOOST_VERSION là một số nguyên, nhưng tôi đã thấy nó với câu lệnh sau: std::vector<BOOST_VERSION>;trong gcc 4.4.1. Cảm ơn!
Jim Hunziker

Lưu ý rằng với Visual C ++, bạn sẽ phải sử dụng câu trả lời của Bojan Resnik.
Raphaël Saint-Pierre

Tôi đã cố gắng làm cho điều này hoạt động, nhưng đáng buồn là thông báo lỗi mà GCC đưa ra cho tôi là không thể mô tả được. Nhưng +1 vì đã đề cập đến nó.
Chris Lutz

14

Không tăng:

  1. xác định lại cùng một macro và trình biên dịch HIMSELF sẽ đưa ra cảnh báo.

  2. Từ cảnh báo, bạn có thể thấy vị trí của định nghĩa trước đó.

  3. tập tin vi của định nghĩa trước đó.

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 ()
{

}

Điều này là dễ dàng hơn và đơn giản.
Tmx

1
chính nó : trình biên dịch không có giới tính
Sky

Điều này không hoạt động với các macro được xác định trước, chẳng hạn như __cplusplus.
ManuelAtWork

10

Trong Microsoft C / C ++, bạn có thể sử dụng tích hợp sẵn _CRT_STRINGIZE()để in các hằng số. Nhiều stdafx.htệp của tôi chứa một số kết hợp sau:

#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)) 

và kết quả như thế này:

_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 : lỗi nghiêm trọng C1083: Không thể mở tệp bao gồm: ':: 106200': Không có tệp hoặc thư mục như vậy

Hoạt động ngay cả khi preprocess to fileđược bật, ngay cả khi có mã thông báo không hợp lệ:

#define a <::'*/`#>
#include a
MSVC2015 : lỗi nghiêm trọng C1083: Không thể mở tệp bao gồm: '::' * / `# ': Không có tệp hoặc thư mục nào như vậy
GCC4.x : cảnh báo: thiếu ký tự chấm dứt' [-Winvalid-pp-token]
#define a <:: '* / `#>

Tôi chỉ nói Build error: #include expects "FILENAME" or <FILENAME>. Thở dài.
endolith

@endolith trình biên dịch và phiên bản nào?
Andry

DP8051 Keil 9.51 :)
endolith

@endolith Có vẻ như trình biên dịch này rất hạn chế về tiền xử lý: keil.com/support/man/docs/c51/c51_pp_directives.htm Nhưng, về phía tôi, nó gần như hoạt động như mong đợi, tôi vừa xóa một số ký tự không hợp lệ như ':*** WARNING C318 IN LINE 2 OF test.c: can't open file '::*/`'
Andry

Cảm ơn bạn, điều này đã cứu tôi vì nội dung thông báo pragma không được triển khai trong trình biên dịch mà tôi đang sử dụng.
CodeMonkey

3

Bạn cũng có thể xử lý trước tệp nguồn và xem giá trị của bộ tiền xử lý được đánh giá là gì.


2

Có phải bạn đang tìm kiếm

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

Không tuyệt nếu BOOST_VERSION là một chuỗi, như tôi đã giả định, nhưng cũng có thể có các số nguyên riêng lẻ được xác định cho các số chính, phụ và số sửa đổi.


Tôi nghĩ rằng người gửi không muốn (chỉ) thực thi một giá trị cụ thể, họ muốn xem giá trị hiện tại là bao nhiêu.
KeyserSoze

Đây là điều duy nhất phù hợp với tôi. Tôi có thể thay đổi #if VARIABLE == 123tuyên bố một cách nhanh chóng và làm nổi bật cú pháp nói với tôi cho dù đó là giá trị tôi nghĩ rằng đó là hay không ...
endolith

2

Nhìn vào đầu ra của bộ tiền xử lý là điều gần nhất với câu trả lời bạn yêu cầu.

Tôi biết bạn đã loại trừ điều đó (và những cách khác), nhưng tôi không chắc tại sao. Bạn có một vấn đề đủ cụ thể để giải quyết, nhưng bạn chưa giải thích được lý do tại sao bất kỳ phương pháp "bình thường" nào không hoạt động tốt cho bạn.


Đây có lẽ là câu trả lời chính xác cho vấn đề chung.
jww

1

Bạn có thể viết một chương trình in ra BOOST_VERSION, biên dịch và chạy nó như một phần của hệ thống xây dựng của bạn. Nếu không, tôi nghĩ bạn đã gặp may.


Đối với trường hợp phiên bản phần mềm được xác định trong tiêu đề, bạn có thể an toàn (và đó là một câu trả lời tốt). Nhưng như một giải pháp chung, một nhược điểm có thể xảy ra là khiến ứng dụng thử nghiệm và ứng dụng thực của bạn có cùng giá trị của #define - tùy thuộc vào đường dẫn bao gồm của chúng, các #define khác có thể được sử dụng để đặt giá trị của #define đó , CFLAGS được chuyển đến trình biên dịch, v.v.
KeyserSoze 13/10/09

In nó ra từ chương trình thực của bạn. Nếu đồ họa, hãy đặt nó trong hộp thoại "giới thiệu". Nếu dòng lệnh, hãy biến nó thành một tùy chọn (một phần của --version, có thể). Nếu một daemon, hãy ghi nó vào một tệp nhật ký. Nếu được nhúng, hãy tìm một số cách khác.
lặn biển

@swillden - OP muốn nó lúc biên dịch, không phải lúc chạy.
Chris Lutz,

Điều này cũng có xu hướng phá vỡ cross-trình biên dịch dựa xây dựng
Craig Ringer

1

BOOST_VERSION được xác định trong tệp tiêu đề tăng phiên bản.hpp.


1

Hãy xem cả tài liệu về Boost, về cách bạn đang sử dụng macro:

Tham khảo BOOST_VERSION, từ 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 :

Mô tả số phiên bản tăng ở định dạng XXYYZZ như: (BOOST_VERSION % 100)là phiên bản phụ, là phiên bản phụ và là phiên bản chính.((BOOST_VERSION / 100) % 1000)(BOOST_VERSION / 100000)


0

Thay vì #error, hãy thử xác định lại macro, ngay trước khi nó được sử dụng. Quá trình biên dịch sẽ không thành công và trình biên dịch sẽ cung cấp giá trị hiện tại mà nó cho là áp dụng cho macro.

#define BOOST_VERSION blah

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.