Bagaimana cara menampilkan nilai #define pada waktu kompilasi?

123

Saya mencoba mencari tahu versi Boost apa yang menurut kode saya digunakan. Saya ingin melakukan sesuatu seperti ini:

#error BOOST_VERSION

tetapi preprocessor tidak memperluas BOOST_VERSION.

Saya tahu saya dapat mencetaknya pada saat run-time dari program, dan saya tahu saya dapat melihat output dari preprocessor untuk menemukan jawabannya. Saya merasa memiliki cara melakukan ini selama kompilasi dapat bermanfaat.

Jim Hunziker
sumber
7
Untuk pengunjung masa depan ... Chris Barry memberikan solusi umum di bagian akhir (tanpa Boost hal-hal tertentu).
jww

Jawaban:

117

Saya tahu bahwa ini masih lama setelah kueri asli, tetapi ini mungkin masih berguna.

Ini dapat dilakukan di GCC menggunakan operator stringify "#", tetapi memerlukan dua tahap.

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

Nilai makro kemudian dapat ditampilkan dengan:

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

Lihat: 3.4 Stringifikasi dalam dokumentasi online gcc.

Bagaimana itu bekerja:

Praprosesor memahami string yang dikutip dan menanganinya secara berbeda dari teks normal. Rangkaian string adalah contoh dari perlakuan khusus ini. Pragma pesan membutuhkan argumen yang berupa string yang dikutip. Jika ada lebih dari satu komponen ke argumen maka semuanya harus berupa string sehingga penggabungan string dapat diterapkan. Praprosesor tidak pernah dapat berasumsi bahwa string tanpa tanda kutip harus diperlakukan seolah-olah dikutip. Jika ya maka:

#define ABC 123
int n = ABC;

tidak akan dikompilasi.

Sekarang pertimbangkan:

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

yang setara dengan

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

Ini menyebabkan peringatan preprocessor karena abc (unquoted) tidak dapat digabungkan dengan string sebelumnya.

Sekarang perhatikan preprocessor stringize (Yang pernah disebut stringifikasi, link dalam dokumentasi telah diubah untuk mencerminkan terminologi yang direvisi. (Kedua istilah tersebut, kebetulan, sama-sama menjijikkan. Istilah yang benar, tentu saja, stringifaction. Bersiaplah untuk memperbarui tautan Anda.)). Ini hanya bertindak pada argumen makro dan menggantikan argumen yang tidak diperluas dengan argumen yang diapit tanda kutip ganda. Jadi:

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

akan memberikan nilai yang identik ke s1 dan s2. Jika Anda menjalankan gcc -E, Anda dapat melihat ini di keluaran. Mungkin STR akan lebih baik diberi nama seperti ENQUOTE.

Ini memecahkan masalah menempatkan kutipan di sekitar item yang tidak dikutip, masalahnya sekarang adalah, jika argumennya adalah makro, makro tidak akan diperluas. Inilah mengapa makro kedua dibutuhkan. XSTR memperluas argumennya, lalu memanggil STR untuk memasukkan nilai yang diperluas ke dalam tanda kutip.

Chris Barry
sumber
3
Saya penasaran mengapa ini membutuhkan dua tahap
Vincent Fourmond
4
@VincentFourmond Tanpa tahap XSTR, makro tidak diperluas. Jadi jika Anda melakukan #define ABC 42 \ n STR (ABC) Anda akan mendapatkan "ABC". Lihat gcc.gnu.org/onlinedocs/cpp/Stringification.html
rob05c
Ini juga berfungsi baik dengan Xcode 8, misalnya mengganti ABC dengan __IPHONE_9_3.
funroll
Terminologi GCC tampaknya telah berubah, dan dengan itu URL-nya, yang sekarang menjadi https://gcc.gnu.org/onlinedocs/cpp/Stringizing.html#Stringizing
Chris Barry
119

BOOST_PP_STRINGIZE tampaknya solusi yang sangat baik untuk C ++, tetapi tidak untuk C biasa.

Inilah solusi saya untuk 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))

Definisi di atas menghasilkan:

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"

Untuk variabel "didefinisikan sebagai interger" , "didefinisikan sebagai string" , dan "ditentukan tetapi tidak ada nilai" , keduanya berfungsi dengan baik. Hanya untuk variabel "tidak ditentukan" , mereka ditampilkan persis sama dengan nama variabel asli. Anda harus terbiasa - atau mungkin seseorang dapat memberikan solusi yang lebih baik.

Jackie Yeh
sumber
luar biasa! Ada pengalaman di ARM RVCT? tampaknya tidak memiliki fitur "Stringification" sebagai GCC infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.dui0491c/…
xdan
2
Solusi yang bagus. Namun, jika saya ingin menampilkan ukuran nilai yang dihitung waktu kompilasi, misalnya ukuran struct yang kompleks, dapatkah ini dilakukan? Metode yang disarankan dalam jawaban ini tampaknya menghasilkan DEFINED_INT=(sizeof(MY_STRUCT)), tanpa sizeofoperator dievaluasi.
Carl
(Komentar tambahan: tidak terduga, karena itu adalah kompiler daripada pra-prosesor yang akan mengevaluasi sizeof, namun, masih penasaran apakah ada cara cerdas untuk mencapai ini.)
Carl
@xdan Solusi bagus, sayangnya tidak melayani hal-hal seperti#define masks {0xff, 0xaf, 0x0f}
Simon Bagley
59

Jika Anda menggunakan Visual C ++, Anda dapat menggunakan #pragma message:

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

Edit: Terima kasih kepada LB untuk tautannya

Rupanya, padanan GCC (tidak diuji):

#pragma message "BOOST_VERSION=" BOOST_PP_STRINGIZE(BOOST_VERSION)
Bojan Resnik
sumber
5
Itu disebut pragmas diagnostik, gcc.gnu.org/onlinedocs/gcc/…
LB40
4
Alangkah baiknya jika Anda mencantumkan definisiBOOST_PP_STRINGIZE yang bagus dan pendek serta dapat di-copy / paste.
Timmmm
Bekerja dengan baik di bawah gcc :)
Thomas Legris
14

Sejauh yang saya tahu '#error' hanya akan mencetak string, sebenarnya Anda bahkan tidak perlu menggunakan tanda kutip .

Sudahkah Anda mencoba menulis berbagai kode yang salah secara sengaja menggunakan "BOOST_VERSION"? Mungkin sesuatu seperti "blah [BOOST_VERSION] = foo;" akan memberi tahu Anda sesuatu seperti "string literal 1.2.1 tidak dapat digunakan sebagai alamat array". Ini tidak akan menjadi pesan kesalahan yang bagus, tapi setidaknya itu akan menunjukkan kepada Anda nilai yang relevan. Anda dapat bermain-main sampai Anda menemukan kesalahan kompilasi yang memberi tahu Anda nilainya.

KeyserSoze
sumber
Itu tidak berhasil, karena BOOST_VERSION adalah bilangan bulat, tetapi saya harus melihatnya dengan pernyataan ini: std::vector<BOOST_VERSION>;di gcc 4.4.1. Terima kasih!
Jim Hunziker
Perhatikan bahwa dengan Visual C ++, Anda harus menggunakan jawaban Bojan Resnik.
Raphaël Saint-Pierre
Saya mencoba untuk membuat ini bekerja, tetapi pesan kesalahan yang diberikan GCC kepada saya sayangnya tidak deskriptif. Tapi +1 untuk menyebutkannya.
Chris Lutz
14

Tanpa dorongan:

  1. definisikan makro yang sama lagi dan compiler DIRINYA akan memberikan peringatan.

  2. Dari peringatan Anda dapat melihat lokasi definisi sebelumnya.

  3. file vi dari definisi sebelumnya.

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

}
Ambarish Kumar Shivam
sumber
Yang ini lebih mudah dan lugas.
Tmx
1
sendiri : penyusun tidak memiliki jenis kelamin
Sky
Ini tidak berfungsi dengan makro yang sudah ditentukan sebelumnya, seperti __cplusplus.
ManuelAtWork
10

Di Microsoft C / C ++, Anda dapat menggunakan bawaan _CRT_STRINGIZE()untuk mencetak konstanta. Banyak stdafx.hfile saya berisi beberapa kombinasi dari ini:

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

dan menghasilkan sesuatu seperti ini:

_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
UweBaemayr
sumber
5
#define a <::BOOST_VERSION>
#include a
MSVC2015 : kesalahan fatal C1083: Tidak dapat membuka file yang disertakan: ':: 106200': Tidak ada file atau direktori seperti itu

Berfungsi bahkan jika preprocess to filediaktifkan, meskipun ada token yang tidak valid:

#define a <::'*/`#>
#include a
MSVC2015 : kesalahan fatal C1083: Tidak dapat membuka file yang disertakan: '::' * / `# ': Tidak ada file atau direktori seperti itu
GCC4.x : peringatan: karakter' terminating 'hilang [-Winvalid-pp-token]
#define a <:: '* / `#>
Andry
sumber
Punyaku baru saja mengatakan Build error: #include expects "FILENAME" or <FILENAME>. Mendesah.
endolit
@ endolith apa kompiler dan versinya?
Andry
DP8051 Keil 9.51 :)
endolit
@endolith Sepertinya kompiler ini sangat terbatas pada preprocessing: keil.com/support/man/docs/c51/c51_pp_directives.htm Tapi, di sisi saya hampir berfungsi seperti yang diharapkan, saya baru saja menghapus beberapa karakter yang tidak valid seperti ':*** WARNING C318 IN LINE 2 OF test.c: can't open file '::*/`'
Andry
Terima kasih, ini menyelamatkan saya karena pesan pragma tidak diimplementasikan di compiler yang saya gunakan.
CodeMonkey
3

Anda juga dapat memproses file sumber dan melihat untuk apa nilai preprocessor mengevaluasi.

fbrereto
sumber
2

Apakah Anda sedang mencari

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

Tidak bagus jika BOOST_VERSION adalah string, seperti yang saya asumsikan, tetapi mungkin juga ada bilangan bulat individual yang ditentukan untuk bilangan mayor, minor, dan revisi.

pengguna47559
sumber
Saya pikir pengirim tidak ingin (hanya) memaksakan nilai tertentu, mereka ingin melihat berapa nilai saat ini.
KeyserSoze
Ini adalah satu-satunya hal yang berhasil untuk saya. Saya dapat mengubah #if VARIABLE == 123pernyataan dengan cepat dan penyorotan sintaks memberi tahu saya apakah itu nilai yang saya pikirkan atau tidak ...
endolith
2

Melihat keluaran dari preprocessor adalah hal yang paling mendekati jawaban yang Anda minta.

Saya tahu Anda telah mengecualikannya (dan cara lain), tetapi saya tidak yakin mengapa. Anda memiliki masalah yang cukup spesifik untuk dipecahkan, tetapi Anda belum menjelaskan mengapa salah satu metode "normal" tidak bekerja dengan baik untuk Anda.

dwc
sumber
Ini mungkin jawaban yang benar untuk masalah umum.
jww
1

Anda dapat menulis program yang mencetak BOOST_VERSIONdan mengkompilasi serta menjalankannya sebagai bagian dari sistem build Anda. Kalau tidak, saya pikir Anda kurang beruntung.

Chris Lutz
sumber
Untuk kasus versi perangkat lunak yang ditentukan dalam tajuk, Anda mungkin aman (dan itu jawaban yang bagus). Namun sebagai solusi umum, kemungkinan kerugiannya adalah membuat aplikasi pengujian dan aplikasi Anda yang sebenarnya memiliki nilai yang sama dari #define - bergantung pada jalur penyertaannya, #defines lain yang dapat digunakan untuk menyetel nilai dari yang satu itu , CFLAGS diteruskan ke kompiler, dll.
KeyserSoze
Cetak dari program Anda yang sebenarnya. Jika grafis, letakkan di dialog "tentang". Jika command-line, jadikan itu opsi (bagian dari --version, mungkin). Jika sebuah daemon, tulislah ke file log. Jika disematkan, cari cara lain.
divegeek
@swillden - OP menginginkannya pada waktu kompilasi, bukan pada waktu proses.
Chris Lutz
Ini juga cenderung merusak build berbasis cross-compiler
Craig Ringer
1

BOOST_VERSION didefinisikan dalam file header boost version.hpp.

David Harris
sumber
1

Lihat juga dokumentasi Boost, mengenai bagaimana Anda menggunakan makro:

Mengacu ke BOOST_VERSION, dari 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 :

Menjelaskan nomor versi peningkatan dalam format XXYYZZ sedemikian rupa sehingga: (BOOST_VERSION % 100)merupakan versi sub-minor, merupakan versi minor, dan merupakan versi mayor.((BOOST_VERSION / 100) % 1000)(BOOST_VERSION / 100000)

bn.
sumber
0

Alih-alih #error, coba definisikan ulang makro, tepat sebelum digunakan. Kompilasi akan gagal dan kompilator akan memberikan nilai saat ini yang menurutnya berlaku untuk makro.

#define BOOST_VERSION bla

tecMav
sumber