Powiedzmy, że mamy takie makro
#define FOO(type,name) type name
Którego moglibyśmy użyć
FOO(int, int_var);
Ale nie zawsze tak prosto:
FOO(std::map<int, int>, map_var); // error: macro "FOO" passed 3 arguments, but takes just 2
Oczywiście mogliśmy zrobić:
typedef std::map<int, int> map_int_int_t;
FOO(map_int_int_t, map_var); // OK
co nie jest zbyt ergonomiczne. Niezgodności typu Plus muszą zostać rozwiązane. Masz jakiś pomysł, jak rozwiązać ten problem za pomocą makra?
c++
c
macros
c-preprocessor
Muzyka pop
źródło
źródło
Odpowiedzi:
Ponieważ wsporniki kątowe może także oznaczać (lub występują w) operatorów porównania
<
,>
,<=
i>=
, makro rozszerzeń nie ignorować przecinki wewnątrz wsporników kątowych, jak to czyni w nawiasach. (Jest to również problem w przypadku nawiasów kwadratowych i nawiasów klamrowych, mimo że zwykle występują one jako zrównoważone pary). Możesz umieścić argument makro w nawiasach:Problem polega na tym, że parametr pozostaje w nawiasach wewnątrz rozwinięcia makra, co uniemożliwia odczytanie go jako typu w większości kontekstów.
Fajną sztuczką do obejścia tego jest to, że w C ++ możesz wyodrębnić nazwę typu z nazwy typu w nawiasach za pomocą typu funkcji:
Ponieważ tworzenie typów funkcji ignoruje dodatkowe nawiasy, możesz użyć tego makra z nawiasami lub bez, gdzie nazwa typu nie zawiera przecinka:
W C oczywiście nie jest to konieczne, ponieważ nazwy typów nie mogą zawierać przecinków poza nawiasami. Tak więc dla makra międzyjęzykowego możesz napisać:
źródło
template<class KeyType, class ValueType> void SomeFunc(FOO(std::map<KeyType, ValueType>) element) {}
Jeśli zastosuję tutaj to rozwiązanie, struktury za makrem staną się typami zależnymi, a przedrostek nazwy typu jest teraz wymagany dla typu. Możesz go dodać, ale dedukcja typów została zerwana, więc musisz teraz ręcznie wyświetlić argumenty typu, aby wywołać funkcję. Skończyło się na użyciu metody świątyni do definiowania makra dla przecinka. Może nie wyglądać tak ładnie, ale działało idealnie.[]
i{}
, nie są, działa tylko ze()
smutkiem. Zobacz: Jednak nie ma wymogu stosowania nawiasów kwadratowych ani nawiasów klamrowych do zrównoważenia ...#define PROTECT(...) argument_type<void(__VA_ARGS__)>::type
. Przekazywanie argumentów jest teraz łatwo możliwe nawet przy użyciu wielu makr, a dla prostych typów można pominąć OCHRONA. Jednak typy funkcji stają się wskaźnikami funkcji, gdy są oceniane w ten sposóbJeśli nie możesz użyć nawiasów i nie podoba ci się rozwiązanie SINGLE_ARG autorstwa Mike'a, po prostu zdefiniuj PRZECINEK:
Jest to również pomocne, jeśli chcesz zdefiniować niektóre argumenty makr, jak w
który drukuje
std::map<int , int> has typeid name "St3mapIiiSt4lessIiESaISt4pairIKiiEEE"
.źródło
#define STRVX(...) STRV(__VA_ARGS__)
i#define STRV(...) # __VA_ARGS__
, tostd::cout << STRV(type<A COMMA B>) << std::endl;
wydrukujetype<A COMMA B>
istd::cout << STRVX(type<A COMMA B>) << std::endl;
wydrukujetype<A , B>
. (STRV
oznacza „variadic stringify” iSTRVX
„expand variadic stringify”.)COMMA
makra w pierwszej kolejności. Na tym skończyłem.Jeśli Twój preprocesor obsługuje makra wariadyczne:
W przeciwnym razie jest to trochę bardziej uciążliwe:
źródło
Po prostu zdefiniuj
FOO
jakoNastępnie wywołaj go zawsze z nawiasami wokół argumentu typu, np
Oczywiście dobrym pomysłem może być zilustrowanie wywołań w komentarzu do definicji makra.
źródło
UNPACK
przy takim użyciu) UNPACK type name
? Dlaczegotype
poprawnie pobiera typ, gdy jest używany) UNPACK type name
? Tylko co tu się do cholery dzieje?Można to zrobić na co najmniej dwa sposoby. Najpierw możesz zdefiniować makro, które przyjmuje wiele argumentów:
jeśli to zrobisz, może się okazać, że w końcu zdefiniujesz więcej makr do obsługi większej liczby argumentów.
Po drugie, możesz umieścić argument w nawiasach:
jeśli to zrobisz, może się okazać, że dodatkowe nawiasy zepsują składnię wyniku.
źródło
Jest to możliwe z P99 :
Powyższy kod skutecznie usuwa tylko ostatni przecinek na liście argumentów. Sprawdź za pomocą
clang -E
(P99 wymaga kompilatora C99).źródło
Prosta odpowiedź brzmi: nie możesz. Jest to efekt uboczny wyboru
<...>
argumentów dla szablonu;<
a>
także pojawiają się w kontekstach niesymetrycznych więc makro mechanizm nie mógł być przedłużony do obsługi ich jak obsługuje nawiasów. (Niektórzy członkowie komitetu opowiadali się za innym tokenem, powiedzmy(^...^)
, ale nie byli w stanie przekonać większości problemów za pomocą<...>
.)źródło
(^...^)
to jedna szczęśliwa buźka :)