Değişken bir makro nasıl oluşturulur (değişken sayıda bağımsız değişken)


196

C belirli bir sayı değil, herhangi bir sayıda parametre kabul bir makro yazmak istiyorum

misal:

#define macro( X )  something_complicated( whatever( X ) )

Xherhangi bir sayıda parametre nerede

Buna ihtiyacım var çünkü whateveraşırı yüklenmiş ve 2 veya 4 parametre ile çağrılabilir.

Makroyu iki kez tanımlamayı denedim, ancak ikinci tanım birincinin üzerine yazdı!

Birlikte çalıştığım derleyici g ++ (daha spesifik olarak mingw)


8
C veya C ++ ister misiniz? C kullanıyorsanız, neden bir C ++ derleyicisiyle derliyorsunuz? Uygun C99 varyasyon makrolarını kullanmak için, C ++ standart varyasyon makrolarına sahip olmadığından, C ++ derleyicisini değil, C99'u (gcc gibi) destekleyen bir C derleyicisiyle derlemelisiniz.
Chris Lutz

Peki, C ++ bu konuda C süper bir set olduğunu varsaydım ..
hasen

tigcc.ticalc.org/doc/cpp.html#SEC13 varyasyonlu makroların ayrıntılı bir açıklamasına sahiptir.
Gnubie


3
Gelecekteki okuyucular için: C, C ++ 'ın bir alt değeri değildir . Birçok şeyi paylaşıyorlar, ancak birbirlerinin alt kümesi ve üst kümesi olmalarını engelleyen kurallar var.
Pharap

Yanıtlar:


295

C99 yolu, VC ++ derleyicisi tarafından da desteklenir.

#define FOO(fmt, ...) printf(fmt, ##__VA_ARGS__)

8
C99 VA_ARGS önce ## gerektirir sanmıyorum . Bu sadece VC ++ olabilir.
Chris Lutz

98
VA_ARGS'den önce ##'nin nedeni , değişken argümanı listesinin boş olması durumunda önceki virgülün yutulmasıdır , örn. FOO ("a") printf ("a") olarak genişler. Bu, gcc'nin (ve vc ++, belki) bir uzantısıdır, C99, üç nokta yerine en az bir argüman olmasını gerektirir.
jpalecek

110
##gerekli değildir ve taşınabilir değildir. #define FOO(...) printf(__VA_ARGS__)işi taşınabilir şekilde yapar; fmtParametre tanımında ihmal edilebilir.
alecov

4
IIRC, ##
GCC'ye

10
## - sözdizimi llvm / clang ve Visual Studio derleyicisi ile de çalışır. Bu yüzden taşınabilir olmayabilir, ancak büyük derleyiciler tarafından desteklenir.
K. Biermann

37

__VA_ARGS__bunu yapmanın standart yoludur. Gerekmiyorsa derleyiciye özgü kesmek kullanmayın.

Orijinal gönderiye yorum yapamayacağım için gerçekten rahatsızım. Her durumda, C ++, C'nin bir üst kümesi değildir. C kodunuzu bir C ++ derleyicisiyle derlemek gerçekten saçmadır. Donny Don't ne yaparsa yapma.


8
"C kodunuzu bir C ++ derleyicisi ile derlemek gerçekten aptalca" => Herkes (ben dahil) tarafından dikkate alınmaz. Örneğin C ++ temel yönergelerine bakın: CPL.1: C ++ 'dan C , CPL.2'ye tercih edin: C kullanmanız gerekiyorsa, ortak C ve C ++ alt kümesini kullanın ve C kodunu C ++ olarak derleyin . Biri gerçekten uyumlu alt kümede programlama değmez yapmak için gereken "sadece-C-izm" ne düşünmek için zor ve C ve C ++ komiteleri uyumlu alt kümesini kullanılabilir yapmak için çok çalıştı.
HostileFork,

4
@HostileFork Fair , tabii ki C ++ millet C ++ kullanımını teşvik etmek istiyor. Diğerleri ise aynı fikirde değil; Linux Torvalds, örneğin, görünüşte reddetti birden tanımlayıcı değiştirme girişiminde Linux kernel yamaları önerdi classile klassbir C ++ derleyicisi ile derleme işlemlerine imkan verir. Ayrıca sizi uyaracak bazı farklılıklar olduğunu unutmayın; örneğin, üçlü operatör her iki dilde de aynı şekilde değerlendirilmez ve inlineanahtar kelime tamamen farklı bir şey ifade eder (farklı bir sorudan öğrendiğim gibi).
Kyle Strand

3
Bir işletim sistemi gibi gerçekten çapraz platform sistemleri projeleri için, sıkı C'ye bağlı kalmak istersiniz, çünkü C derleyicileri çok daha yaygındır. Gömülü sistemlerde, hala C ++ derleyicileri olmayan platformlar vardır. (Sadece geçilebilir C derleyicileri olan platformlar var!) C ++ derleyicileri beni özellikle siber-fiziksel sistemler için sinirlendiriyor ve sanırım bu duyguya sahip tek gömülü yazılım / C programcısı değilim.
kötümser

2
@downbeat İster üretim için C ++ kullansanız da kullanmasanız da endişe duyuyorsanız, C ++ ile derleyebilmeniz statik analiz için sihirli güçler verir. Bir C kod tabanından yapmak istediğiniz bir sorunuz varsa ... belirli türlerin belirli yollarda kullanılıp kullanılmadığını merak ediyorsanız, type_traits'in nasıl kullanılacağını öğrenmek bunun için hedeflenmiş araçlar oluşturabilir. C statik bir analiz aracı yapmak için büyük paralar ne ödeyecek C ++ biraz biliyorum ve zaten sahip derleyici ile yapılabilir ...
HostileFork SE

1
Linux sorunuyla konuşuyorum. (Sadece "Linux Torvalds" ha diyor fark!)
kötümser

28

Bunun mümkün olduğunu düşünmüyorum, çift parens ile taklit edebilirsin ... tek tek argümanlara ihtiyacın olmadığı sürece.

#define macro(ARGS) some_complicated (whatever ARGS)
// ...
macro((a,b,c))
macro((d,e))

21
Variadik bir makroya sahip olmak mümkün olsa da, çift parantez kullanmak iyi bir tavsiye.
David Rodríguez - dribeas

2
Microchip'in XC derleyicisi varyasyonlu makroları desteklemez ve bu nedenle bu çift parantez hile yapabileceğiniz en iyisidir.
gbmhunter

10
#define DEBUG

#ifdef DEBUG
  #define PRINT print
#else
  #define PRINT(...) ((void)0) //strip out PRINT instructions from code
#endif 

void print(const char *fmt, ...) {

    va_list args;
    va_start(args, fmt);
    vsprintf(str, fmt, args);
        va_end(args);

        printf("%s\n", str);

}

int main() {
   PRINT("[%s %d, %d] Hello World", "March", 26, 2009);
   return 0;
}

Derleyici varyasyonlu makroları anlamazsa, PRINT'i aşağıdakilerden biriyle de çıkarabilirsiniz:

#define PRINT //

veya

#define PRINT if(0)print

Birincisi YAZDIR komutlarını yorumlar, ikincisi ise NULL durumunda koşul nedeniyle YAZDIR komutunu engeller. Optimizasyon ayarlanmışsa, derleyici hiçbir zaman yürütülmeyen talimatları çıkarmalıdır: if (0) print ("merhaba dünya"); veya ((boşluk) 0);


8
#define PRINT //, PRINT yerine //
bitc

8
#define (0) yazdırma iyi bir fikir değilse, ya çağırma kodunun PRINT çağrısı için kendine ait bir if olması durumunda PRINT. Daha
iyisi

3
Standart "hiçbir şey yapma, zarifçe" yapmak {} ise (0)
vonbrand

ifKod yapısını dikkate alan "bunu yapma" nın uygun sürümü: if (0) { your_code } elsemakro genişletmeniz sona erdikten sonraki noktalı virgül else. whileVersiyon görünüyor gibi: while(0) { your_code } ile sorun do..whilesürümü kodu içinde olmasıdır do { your_code } while (0)kez yapılır, garanti. Her üç durumda da your_code, boşsa, bu uygun bir durumdur do nothing gracefully.
Jesse Chisholm

4

Burada g ++ için açıkladı, ancak C99'un bir parçası olmasına rağmen herkes için çalışmalı

http://www.delorie.com/gnu/docs/gcc/gcc_44.html

hızlı örnek:

#define debug(format, args...) fprintf (stderr, format, args)

3
GCC'nin varyasyon makroları, C99 varyasyon makroları değildir. GCC vardır C99 variadic makro ama C99 C ++ bir parçası değildir, çünkü G ++, bunları desteklemez.
Chris Lutz

1
Aslında g ++, C ++ dosyalarında C99 makrolarını derleyecektir. Bununla birlikte, '-pedantik' ile derlendiğinde bir uyarı verecektir.
Alex B

2
C99 değil. C99, VA_ARGS makrosunu kullanır ).
qrdl

1
C ++ 11 __VA_ARGS__, önceki sürümlerdeki derleyiciler tarafından da desteklense de, bir uzantı olarak da desteklenir .
Ethouris

1
Bu printf ("hi") için çalışmaz; burada değişken argümanı yoktur. Bunu düzeltmek için genel bir yol var mı?
BTR Naidu
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.