Przecinek w makro C/C++

Powiedzmy, że mamy takie makro

#define FOO(type,name) type name

Które moglibyśmy wykorzystać jak

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, że możemy:

 typedef std::map<int, int> map_int_int_t;
 FOO(map_int_int_t, map_var); // OK
Co nie jest zbyt ergonomiczne. Należy rozwiązać problem niezgodności Typu Plus. Jakiś pomysł, jak rozwiązać ten problem z makro ?
Author: PoP, 2012-12-12

7 answers

Ponieważ nawiasy kątowe mogą również reprezentować (lub występować) operatory porównania <, >, <= i >=, rozszerzenie makr nie może ignorować przecinków w nawiasach kątowych, tak jak robi to w nawiasach. (Jest to również problem dla nawiasów kwadratowych i szelek, chociaż zwykle występują one jako pary zrównoważone.) Możesz dołączyć argument makra w nawiasach:

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

Problem polega na tym, że parametr pozostaje w nawiasie wewnątrz rozszerzenia makra, co uniemożliwia odczytywane jako typ w większości kontekstów.

Ładny trik do obejścia jest to, że w C++, można wyodrębnić nazwę typu z nazwy typu w nawiasie za pomocą typu funkcji:

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

Ponieważ tworzenie typów funkcji ignoruje dodatkowe nawiasy, można użyć tego makra z nawiasami lub bez nich, gdzie nazwa typu nie zawiera przecinka:

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

W C nie jest to oczywiście konieczne, ponieważ nazwy typów nie mogą zawierać przecinków poza nawiasami. Więc, dla języka krzyżowego makro można napisać:

#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
 117
Author: ecatmur,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-09-23 14:57:33

Jeśli nie możesz użyć nawiasów i nie podoba Ci się rozwiązanie Single_arg Mike ' a, po prostu zdefiniuj przecinek:

#define COMMA ,

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

To również pomaga, jeśli chcesz stringify niektóre z argumentów makra, jak w

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

Które drukuje std::map<int , int> has typeid name "St3mapIiiSt4lessIiESaISt4pairIKiiEEE".

 124
Author: not-a-user,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-03-16 10:54:26

Jeśli twój preprocesor obsługuje różne makra:

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

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

W Przeciwnym Razie jest to trochę bardziej nudne:

#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);
 59
Author: Mike Seymour,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-12-12 15:08:00

Po prostu zdefiniuj FOO jako

#define UNPACK( ... ) __VA_ARGS__

#define FOO( type, name ) UNPACK type name

Następnie wywołaj go zawsze z nawiasem wokół argumentu type, np.

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

Dobrym pomysłem może być oczywiście zilustrowanie wywołań w komentarzu do definicji makro.

 34
Author: Cheers and hth. - Alf,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-03-14 23:06:52

Istnieją co najmniej dwa sposoby, aby to zrobić. Najpierw można zdefiniować makro, które pobiera wiele argumentów:

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

Jeśli to zrobisz, może się okazać, że definiujesz więcej makr, aby obsłużyć więcej argumentów.

Po drugie, możesz umieścić nawiasy wokół argumentu:

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

Jeśli to zrobisz, może się okazać, że dodatkowe nawiasy psują składnię wyniku.

 4
Author: Pete Becker,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-12-12 15:08:18

Jest to możliwe z P99 :

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

Powyższy kod skutecznie usuwa tylko ostatni przecinek z listy argumentów. Sprawdź za pomocą clang -E (P99 wymaga kompilatora C99).

 4
Author: xiaq,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-12-12 15:25:30

Prosta odpowiedź jest taka, że nie możesz. Jest to efekt uboczny wyboru <...> dla argumentów szablonu; < i > również pojawiają się w niezrównoważonych kontekstach, więc mechanizm makr nie mógł zostać rozszerzony tak, aby obsługiwał je tak, jak obsługuje nawiasy. (Niektórzy członkowie Komitetu argumentowali za innym tokenem, powiedzmy (^...^), ale nie byli w stanie przekonać większości problemów za pomocą <...>.)

 3
Author: James Kanze,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-12-12 15:11:33