Derleme zamanında bir #define değerini nasıl gösterebilirim?


125

Kodumun hangi Boost sürümünü kullandığını düşündüğünü anlamaya çalışıyorum. Bunun gibi bir şey yapmak istiyorum:

#error BOOST_VERSION

ancak önişlemci BOOST_VERSION öğesini genişletmez.

Bunu programdan çalışma zamanında yazdırabileceğimi biliyorum ve yanıtı bulmak için önişlemcinin çıktısına bakabileceğimi biliyorum. Derleme sırasında bunu yapmanın bir yolunun faydalı olabileceğini düşünüyorum.


7
Gelecekteki ziyaretçiler için ... Chris Barry sonunda genel bir çözüm sunuyor (Boost'a özgü şeylerden yoksun).
jww

Yanıtlar:


119

Bunun orijinal sorgudan çok sonra olduğunu biliyorum, ancak bu yine de yararlı olabilir.

Bu, "#" stringify operatörü kullanılarak GCC'de yapılabilir, ancak iki aşama gerektirir.

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

Bir makronun değeri daha sonra şu şekilde görüntülenebilir:

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

Bakınız: 3.4 gcc çevrimiçi belgelerinde Dizgileştirme.

Nasıl çalışır:

Ön işlemci, alıntılanan dizeleri anlar ve bunları normal metinden farklı şekilde işler. Dize birleştirme, bu özel muamelenin bir örneğidir. Mesaj pragması, tırnak içine alınmış bir dizge olan bir argüman gerektirir. Bağımsız değişkenin birden fazla bileşeni olduğunda, dize birleştirme uygulanabilmesi için bunların tümü dize olmalıdır. Önişlemci, alıntılanmamış bir dizenin, alıntılanmış gibi ele alınması gerektiğini asla varsayamaz. Öyleyse:

#define ABC 123
int n = ABC;

derlenmez.

Şimdi düşünün:

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

eşdeğer olan

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

Bu bir önişlemci uyarısına neden olur çünkü abc (tırnaksız) önceki dizeyle birleştirilemez.

Şimdi önişlemci dizesini düşünün (Bir zamanlar dizgeleştirme olarak adlandırılan, dokümantasyondaki bağlantılar, revize edilmiş terminolojiyi yansıtacak şekilde değiştirildi. (Tesadüfen, her iki terim de aynı derecede kötüdür. Doğru terim, elbette, sertleştirmedir. Güncellemeye hazır olun bağlantılarınız.)) operatörü. Bu, yalnızca bir makronun argümanlarına etki eder ve genişletilmemiş argümanı çift tırnak içine alınmış argümanla değiştirir. Böylece:

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

s1 ve s2'ye aynı değerleri atayacaktır. Gcc -E komutunu çalıştırırsanız, bunu çıktıda görebilirsiniz. Belki STR, ENQUOTE gibi bir isimle adlandırılsa daha iyi olur.

Bu, alıntı yapılmamış bir öğenin etrafına tırnak işareti koyma sorununu çözer, şimdi sorun, eğer argüman bir makro ise, makronun genişletilmemesidir. Bu nedenle ikinci makroya ihtiyaç vardır. XSTR bağımsız değişkenini genişletir, ardından genişletilmiş değeri tırnak içine almak için STR'yi çağırır.


3
Neden iki aşama gerektirdiğini merak ediyorum
Vincent Fourmond

4
@VincentFourmond XSTR aşaması olmadan makro genişletilmez. Dolayısıyla, # ABC 42'yi tanımla \ n STR (ABC) yaparsanız "ABC" alırsınız. Bkz. Gcc.gnu.org/onlinedocs/cpp/Stringification.html
rob05c

Bu aynı zamanda Xcode 8 ile de harika çalışıyor, örneğin ABC'yi __IPHONE_9_3.
2016

GCC terminolojisi ve bununla birlikte şu anda https://gcc.gnu.org/onlinedocs/cpp/Stringizing.html#Stringizing
Chris Barry

119

BOOST_PP_STRINGIZE C ++ için mükemmel bir çözüm gibi görünüyor, ancak normal C için değil.

İşte GNU CPP için çözümüm:

/* 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))

Yukarıdaki tanımlar şunlarla sonuçlanır:

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"

For "interger olarak tanımlanan" , "dizesi olarak tanımlanan" ve "tanımlanmış ancak hiçbir değer" değişkenleri, gayet iyi çalışır. Yalnızca "tanımlanmamış" değişken için, orijinal değişken adıyla tamamen aynı şekilde görüntülenirler. Alışmak zorundasın - veya belki birisi daha iyi bir çözüm sağlayabilir.


mükemmel! ARM RVCT'de herhangi bir deneyim var mı? GCC infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.dui0491c/…
xdan

2
Harika çözüm. Bununla birlikte, derleme zamanında hesaplanan bir değerin boyutunu, örneğin karmaşık bir yapının boyutunu görüntülemek istersem, bu yapılabilir mi? Bu cevapta önerilen yöntem DEFINED_INT=(sizeof(MY_STRUCT)), sizeofoperatör değerlendirilmeden üretiliyor gibi görünüyor .
Carl

(Yorum eklenmesi: beklenmedik değil, çünkü değerlendirecek olan ön işlemciden ziyade derleyici olduğundan sizeof, ancak bunu başarmanın akıllıca bir yolu olup olmadığını merak ediyor.)
Carl

@xdan İyi çözüm, maalesef şu gibi şeylere hitap etmiyor#define masks {0xff, 0xaf, 0x0f}
Simon Bagley

59

Visual C ++ kullanıyorsanız, şunları kullanabilirsiniz #pragma message:

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

Düzenle: Bağlantı için LB'ye teşekkürler

Görünüşe göre, GCC eşdeğeri (test edilmemiştir):

#pragma message "BOOST_VERSION=" BOOST_PP_STRINGIZE(BOOST_VERSION)

5
Buna tanısal pragmalar denir, gcc.gnu.org/onlinedocs/gcc/…
LB40

4
Eğer dahil olsaydı iyi olurdu tanımınıBOOST_PP_STRINGIZE / pasteable güzel ve kısa ve kopyalama hangi.
Timmmm

Gcc altında iyi çalışıyor :)
Thomas Legris

14

Bildiğim kadarıyla '#error' yalnızca dizeleri yazdıracak, aslında tırnak kullanmanıza bile gerek yok .

"BOOST_VERSION" kullanarak kasıtlı olarak çeşitli yanlış kodlar yazmayı denediniz mi? Belki "blah [BOOST_VERSION] = foo;" gibi bir şey "1.2.1 dizesi bir dizi adresi olarak kullanılamaz" gibi bir şey söyleyecektir. Güzel bir hata mesajı olmayacak, ancak en azından size ilgili değeri gösterecektir. Size değeri söyleyen bir derleme hatası bulana kadar oynayabilirsiniz.


Bu işe yaramadı, çünkü BOOST_VERSION bir tamsayıdır, ancak şu ifadeyle görmeliyim: std::vector<BOOST_VERSION>;gcc 4.4.1'de. Teşekkürler!
Jim Hunziker

Visual C ++ ile Bojan Resnik'in cevabını kullanmanız gerektiğini unutmayın.
Raphaël Saint-Pierre

Bunu çalıştırmaya çalıştım, ancak GCC'nin bana verdiği hata mesajı ne yazık ki açıklayıcıydı. Ancak bundan bahsettiği için +1.
Chris Lutz

14

Güçlendirme olmadan:

  1. aynı makroyu tekrar tanımlarsanız derleyici HIMSELF uyarı verecektir.

  2. Uyarıdan önceki tanımın yerini görebilirsiniz.

  3. vi dosyası önceki tanımın.

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

}

Bu daha kolay ve anlaşılır.
Tmx

1
kendisi : derleyicilerin cinsiyeti yoktur
Sky

Bu, gibi önceden tanımlanmış makrolarla çalışmaz __cplusplus.
ManuelAtWork

11

Microsoft C / C ++ 'da, _CRT_STRINGIZE()sabitleri yazdırmak için yerleşik olanı kullanabilirsiniz. stdafx.hDosyalarımın çoğu şunların bazı kombinasyonlarını içeriyor:

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

ve şöyle bir çıktı verir:

_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 : önemli hata C1083: Dosya dahil açılamıyor: ':: 106200': Böyle bir dosya veya dizin yok

preprocess to fileGeçersiz belirteçler mevcut olsa bile etkinleştirilse bile çalışır :

#define a <::'*/`#>
#include a
MSVC2015 : önemli hata C1083: dosya içerme açılamıyor: '::' * / "# ': Böyle bir dosya veya dizin yok
GCC4.x : uyarı: sonlandırma' karakteri eksik [-Winvalid-pp-token]
# define a <:: '* / `#>

Benimki sadece diyor Build error: #include expects "FILENAME" or <FILENAME>. İç çekmek.
endolith

@endolith hangi derleyici ve sürümü?
Andry

DP8051 Keil 9.51 :)
Endolit

@endolith Bu derleyicinin ön işleme konusunda çok sınırlı olduğu görülüyor : keil.com/support/man/docs/c51/c51_pp_directives.htm Ancak benim tarafımda neredeyse beklendiği gibi çalışıyor, bazı geçersiz karakterleri kaldırdım, örneğin ':*** WARNING C318 IN LINE 2 OF test.c: can't open file '::*/`'
Andry

Teşekkürler, bu beni kurtardı çünkü pragma mesajı kullandığım derleyicide uygulanmadı.
CodeMonkey

3

Ayrıca kaynak dosyayı önceden işleyebilir ve önişlemci değerinin ne olarak değerlendirildiğini görebilirsiniz.


2

Mı arıyorsun

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

BOOST_VERSION varsaydığım gibi bir dizeyse harika değil, ancak büyük, küçük ve revizyon numaraları için tanımlanmış tek tek tamsayılar da olabilir.


Bence gönderenin belirli bir değeri (sadece) zorlamak istemediğini, mevcut değerin ne olduğunu görmek istediğini düşünüyorum.
KeyserSoze

Benim için işe yarayan tek şey bu. #if VARIABLE == 123İfadeyi anında değiştirebilirim ve sözdizimi vurgusu bana düşündüğüm değer olup olmadığını söyler ...
endolith

2

Önişlemcinin çıktısına bakmak, istediğiniz cevaba en yakın şeydir.

Bunu (ve diğer yolları) dışladığınızı biliyorum, ama neden olduğundan emin değilim. Çözmeniz gereken yeterince spesifik bir probleminiz var, ancak "normal" yöntemlerden herhangi birinin sizin için neden işe yaramadığını açıklamadınız.


Bu muhtemelen genel soruna doğru cevaptır.
jww

1

BOOST_VERSIONOluşturma sisteminizin bir parçası olarak yazdıran ve derleyen ve çalıştıran bir program yazabilirsiniz . Aksi takdirde, şansın kalmaz.


Bir başlıkta tanımlanan bir yazılım sürümü söz konusu olduğunda muhtemelen güvendesiniz (ve bu iyi bir cevap). Ancak genel bir çözüm olarak, olası bir dezavantaj, test uygulamanızın ve gerçek uygulamanızın aynı #define değerine sahip olmasını sağlamaktır - içerme yollarına bağlı olarak, diğer # tanımların değerini ayarlamak için kullanılabilir. , CFLAGS derleyiciye geçti, vb.
KeyserSoze

Gerçek programınızdan yazdırın. Grafiksel ise, "hakkında" iletişim kutusuna koyun. Komut satırı ise, bunu bir seçenek yapın (--version'ın bir parçası, belki). Bir arka plan programı ise, bir günlük dosyasına yazın. Gömülü ise başka bir yol bulun.
divegeek

@swillden - OP bunu çalışma zamanında değil derleme zamanında istedi.
Chris Lutz

Bu aynı zamanda çapraz derleyici tabanlı yapıları bozma eğilimindedir
Craig Ringer

1

BOOST_VERSION, yükseltme başlık dosyasında version.hpp tanımlanır.



0

#Error yerine, kullanılmadan hemen önce makroyu yeniden tanımlamayı deneyin. Derleme başarısız olacak ve derleyici makro için geçerli olduğunu düşündüğü mevcut değeri sağlayacaktır.

#define BOOST_VERSION blah

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.