C / C ++ makrosunda virgül


104

Böyle bir makromuz olduğunu varsayalım

#define FOO(type,name) type name

Hangi gibi kullanabiliriz

FOO(int, int_var);

Ancak her zaman bu kadar basit değil:

FOO(std::map<int, int>, map_var); // error: macro "FOO" passed 3 arguments, but takes just 2

Tabii ki yapabiliriz:

 typedef std::map<int, int> map_int_int_t;
 FOO(map_int_int_t, map_var); // OK

ki bu çok ergonomik değil. Artı tipi uyumsuzluklarla ilgilenilmelidir. Bunu makro ile nasıl çözeceğiniz hakkında bir fikriniz var mı?


Tahmin ediyorum ki onları değişmez hale getirmek için anlam taşıyan karakterlerden kaçmanız gerekiyor.
Jite

En azından C ++ 'da, herhangi bir yere typedef koyabilirsiniz, bu yüzden neden "önceden" olması gerektiğini söylediğinizden emin değilim.
Vaughn Cato

Yanıtlar:


111

Açılı ayraçlar da ifade etmektedir (ya da meydana) çünkü karşılaştırma operatörleri <, >, <=ve >=parantez içinde olduğu gibi, makro genişleme köşeli parantez içinde virgül göz ardı edilemez. (Bu aynı zamanda köşeli parantezler ve kaşlı ayraçlar için de bir sorundur, bunlar genellikle dengeli çiftler olarak görülse de.) Makro bağımsız değişkenini parantez içine alabilirsiniz:

FOO((std::map<int, int>), map_var);

Bu durumda sorun, parametrenin makro genişletme içinde parantez içinde kalması ve bu da çoğu bağlamda bir tür olarak okunmasını engellemesidir.

Bunu geçici olarak çözmenin güzel bir yolu, C ++ 'da, bir işlev türü kullanarak parantezli bir tür adından bir tür adını çıkarabilmenizdir:

template<typename T> struct argument_type;
template<typename T, typename U> struct argument_type<T(U)> { typedef U type; };
#define FOO(t,name) argument_type<void(t)>::type name
FOO((std::map<int, int>), map_var);

İşlev türleri oluşturmak fazladan parantezleri yok saydığından, bu makroyu, tür adının virgül içermediği durumlarda parantezli veya parantezsiz kullanabilirsiniz:

FOO((int), int_var);
FOO(int, int_var2);

C de, elbette, bu gerekli değildir çünkü tür adları parantez dışında virgül içeremez. Yani, diller arası bir makro için şunları yazabilirsiniz:

#ifdef __cplusplus__
template<typename T> struct argument_type;
template<typename T, typename U> struct argument_type<T(U)> { typedef U type; };
#define FOO(t,name) argument_type<void(t)>::type name
#else
#define FOO(t,name) t name
#endif

Bu harika. Ama bunu nasıl öğrendin? Tonlarca numara deniyorum ve bir işlev türünün sorunu çözeceğini hiç düşünmemiştim.
Will Custode

@WilliamCustode hatırladığım kadarıyla, en can sıkıcı çözümleme problemine referansla fonksiyon türlerinin ve fonksiyon bildirimlerinin gramerini inceliyordum, bu yüzden bu bağlamda gereksiz parantezlerin bir türe uygulanabileceğinin farkında olmam tesadüftü.
ecatmur

Şablonlarla çalışırken bu yöntemle ilgili bir sorun buldum. Diyelim ki istediğim kod şuydu: template<class KeyType, class ValueType> void SomeFunc(FOO(std::map<KeyType, ValueType>) element) {}Bu çözümü buraya uygularsam, makronun arkasındaki yapılar bağımlı tipler olur ve artık tipte tip adı öneki gereklidir. Ekleyebilirsiniz, ancak tür kesintisi bozulmuştur, bu nedenle artık işlevi çağırmak için tür bağımsız değişkenlerini manuel olarak listelemeniz gerekir. Temple'ın virgül için bir makro tanımlama yöntemini kullandım. O kadar güzel görünmeyebilir ama mükemmel çalıştı.
Roger Sanders

Cevapla ilgili küçük bir sorun: Virgüllerin içeride göz ardı edildiğini [] ve yok {}olmadıklarını, sadece ()ne yazık ki işe yaradığını belirtir . Bakın: Ancak, dengelemek için köşeli parantez veya parantezlere gerek yoktur ...
VinGarcia

Ne yazık ki bu MSVC'de çalışmıyor: godbolt.org/z/WPjYW8 . Görünüşe göre MSVC birden fazla parantez eklemeye izin vermiyor ve ayrıştırmayı başaramıyor. Şık ama daha hızlı (daha az şablon örneklemesi) gibi olmadığı bir çözüm sarıcı makro içine virgülle ed argüman sarılmasıdır: #define PROTECT(...) argument_type<void(__VA_ARGS__)>::type. Bağımsız değişkenleri aktarmak artık birden fazla makro aracılığıyla bile kolayca mümkündür ve basit türler için PROTECT'i atlayabilirsiniz. Ancak bu şekilde değerlendirildiğinde işlev türleri işlev işaretçileri haline gelir
Flamefire

119

Parantez kullanamıyorsanız ve Mike'ın SINGLE_ARG çözümünü beğenmiyorsanız, bir COMMA tanımlamanız yeterlidir:

#define COMMA ,

FOO(std::map<int COMMA int>, map_var);

Bu, aynı zamanda, aşağıdaki gibi bazı makro argümanlarını dizginlemek istiyorsanız da yardımcı olur.

#include <cstdio>
#include <map>
#include <typeinfo>

#define STRV(...) #__VA_ARGS__
#define COMMA ,
#define FOO(type, bar) bar(STRV(type) \
    " has typeid name \"%s\"", typeid(type).name())

int main()
{
    FOO(std::map<int COMMA int>, std::printf);
}

hangi baskılar std::map<int , int> has typeid name "St3mapIiiSt4lessIiESaISt4pairIKiiEEE".


16
#define COMMA vay, bana HOURS işten kurtardın ... neden bunu yıllar önce düşünmedim. Bu fikri paylaştığınız için teşekkürler. Bu, farklı argüman sayılarına sahip fonksiyonları tamamen kuran makrolar oluşturmama bile izin veriyor.
moliad

28
Korku için Artı 1
namezero

1
@kiw Eğer siz #define STRVX(...) STRV(__VA_ARGS__)ve #define STRV(...) # __VA_ARGS__, sonra std::cout << STRV(type<A COMMA B>) << std::endl;yazdıracak type<A COMMA B>ve std::cout << STRVX(type<A COMMA B>) << std::endl;yazdıracaktır type<A , B>. ( STRV"variadic stringify" içindir ve STRVX"genişletilmiş variadic stringify" içindir.)
kullanıcı değil

1
@ kullanıcı değil evet, ancak değişken makrolarla COMMAilk etapta makroya ihtiyacınız yok . Ben de böyle bitirdim.
kiw

Bunu asla kullanmam ama komik olduğu için +1.
Rafael Baptista

58

Ön işlemciniz değişken makroları destekliyorsa:

#define SINGLE_ARG(...) __VA_ARGS__
#define FOO(type,name) type name

FOO(SINGLE_ARG(std::map<int, int>), map_var);

Aksi takdirde, biraz daha sıkıcı olur:

#define SINGLE_ARG2(A,B) A,B
#define SINGLE_ARG3(A,B,C) A,B,C
// as many as you'll need

FOO(SINGLE_ARG2(std::map<int, int>), map_var);

Tanrım ... Neden? Neden parantez içine almayalım?

15
@VladLazarenko: Çünkü rastgele kod parçalarını her zaman parantez içine koyamazsınız. Özellikle, bir bildiricide tür adının etrafına parantez koyamazsınız, bu da tam olarak bu argümanın olduğu şeydir.
Mike Seymour

2
... ve ayrıca makro tanımını değiştirebileceğiniz ve onu çağıran tüm yerleri değiştiremeyeceğiniz için (ki bu sizin kontrolünüz altında olmayabilir veya 1000 dosyaya yayılmış olabilir, vb.). Bu, örneğin, benzer adlı bir işlevden görevleri devralmak için bir makro eklerken meydana gelir.
BeeOnRope

32

FOOOlarak tanımlayın

#define UNPACK( ... ) __VA_ARGS__

#define FOO( type, name ) UNPACK type name

Sonra onu her zaman tür bağımsız değişkeni etrafında parantezle çağırın, örn.

FOO( (std::map<int, int>), map_var );

Makro tanıma ilişkin bir yorumda çağrıları örneklemek elbette iyi bir fikir olabilir.


Bunun neden bu kadar aşağıda olduğundan emin değilim, Mike Seymours'dan çok daha güzel bir çözüm. Hızlı ve basittir ve kullanıcıdan tamamen gizlidir.
iFreilicht

3
@iFreilicht: Bir yıldan biraz daha uzun bir süre sonra yayınlandı. ;-)
Şerefe ve hth. - Alf

5
Ve nasıl ve neden çalıştığını anlamak da zor olduğu için
VinGarcia

@VinGarcia, neden / nasıl çalıştığını açıklayabilir misin? Parantez çağırırken neden gerekli? UNPACKBöyle kullanıldığında ne yapmalı ) UNPACK type name? typeKullanıldığında türü neden doğru alıyor ) UNPACK type name? Burada ne oluyor?
kullanıcı

@User yok, belki Cheers ve
hth

4

Bunu yapmanın en az iki yolu var. İlk olarak, birden çok argüman alan bir makro tanımlayabilirsiniz:

#define FOO2(type1, type2, name) type1, type2, name

Bunu yaparsanız, daha fazla argümanı işlemek için daha fazla makro tanımladığınızı fark edebilirsiniz.

İkinci olarak, bağımsız değişkenin etrafına parantez koyabilirsiniz:

#define FOO(type, name) type name
F00((std::map<int, int>) map_var;

bunu yaparsanız, fazladan parantezlerin sonucun sözdizimini bozduğunu görebilirsiniz.


İlk çözüm için, makrolar aşırı yüklenmediğinden her makronun farklı bir adı olması gerekecektir. İkincisi, bir tür adını iletiyorsanız, bunun bir değişken (veya bir typedef) bildirmek için kullanılması çok yüksek bir ihtimaldir, bu nedenle parantezler sorunlara neden olur.
James Kanze

4

Bu P99 ile mümkündür :

#include "p99/p99.h"
#define FOO(...) P99_ALLBUTLAST(__VA_ARGS__) P99_LAST(__VA_ARGS__)
FOO()

Yukarıdaki kod, bağımsız değişken listesindeki yalnızca son virgülü etkili bir şekilde çıkarır. clang -E(P99, bir C99 derleyicisi gerektirir) ile kontrol edin .


3

Basit cevap, yapamayacağınızdır. Bu, <...>şablon argümanlarının seçiminin bir yan etkisidir ; <ve >makro mekanizması o parantez kolları gibi onları işlemek için uzatılamaz böylece de dengesiz bağlamlarda görünür. (Komite üyelerinden bazıları farklı bir jeton için tartıştılar (^...^), ancak sorunların çoğunu kullanarak ikna edemediler <...>.)


2
(^...^)bu mutlu bir yüz :)
CygnusX1
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.