3 votes

Macro variadique C++ pour les paires

Je suis conscient de la macro MAP qui peut être utilisé pour appliquer une fonction macro à une liste d'arguments variadique. Mais comment appliquer une fonction macro à des paires d'arguments variadiques ?

Ce que je veux créer est quelque chose comme ceci :

#define DECL_VAR(type,var)\
  type _##var;
#define DECL_GETSET(type,var)\
  type get_##var() const {return _##var;}\
  void set_##var(type val) {_##var = val;}

#define CREATE_CLASS(C, ...)\
  class C {\
    private:\
    MAP_PAIR(DECL_VAR, ODD_VA_ARG, EVEN_VA_ARG)\
    public:\
    MAP_PAIR(DECL_GETSET, ODD_VA_ARG, EVEN_VA_ARG)\
  };

CREATE_CLASS(Person, const char*, name, int, age, float, height)
// or maybe
CREATE_CLASS(Person, (const char*, name), (int, age), (float, height))

2voto

aschepler Points 23731
CREATE_CLASS(Person, (const char*, name), (int, age), (float, height))

C'est l'option la plus facile à utiliser, car la syntaxe du préprocesseur gère les parenthèses équilibrées, de telle sorte que, par exemple, les fichiers de type (const char*, name) est un argument unique pour la macro, bien que contenant une virgule.

Une solution simple serait donc de fournir des macros enveloppantes qui acceptent un argument de la forme (type, varname) et transmettez ses éléments à vos macros à deux arguments :

#define DECL_VAR(type,var)\
  type _##var;
#define DECL_VAR_PAIR(pair)\
  DECL_VAR pair
#define DECL_GETSET(type,var)\
  type get_##var() const {return _##var;}\
  void set_##var(type val) {_##var = val;}
#define DECL_GETSET_PAIR(pair)\
  DECL_GETSET pair

#define CREATE_CLASS(C, ...)\
  class C {\
    private:\
    MAP(DECL_VAR_PAIR, __VA_ARGS__)\
    public:\
    MAP(DECL_GETSET_PAIR, __VA_ARGS__)\
  };

CREATE_CLASS(Person, (const char*, name), (int, age), (float, height))

Ainsi, par exemple, lorsque l'expansion de MAP(DECL_VAR_PAIR, __VA_ARGS__) dans le dernier CREATE_CLASS passe l'argument unique (int, age) a DECL_VAR_PAIR Les étapes de l'expansion comprennent :

DECL_VAR_PAIR((int, age))
DECL_VAR(int, age)   // since DECL_VAR_PAIR(x) is just DECL_VAR then x
int _##age;
int _age;

Cependant, si vous avez un tas de choses à faire avec les arguments appariés, la création de toutes ces macros wrapper pourrait devenir encombrante. Au lieu de cela, nous pouvons ajouter un MAP -qui s'attend à ce que ses arguments soient des listes entre parenthèses. Tout d'abord, remarquez que dans <map.h> les étapes qui appliquent réellement une macro à l'un des arguments sont étroitement liées à l'étape principale de l'application de la macro. MAP macro :

#define MAP0(f, x, peek, ...) f(x) MAP_NEXT(peek, MAP1)(f, peek, __VA_ARGS__)
#define MAP1(f, x, peek, ...) f(x) MAP_NEXT(peek, MAP0)(f, peek, __VA_ARGS__)
#define MAP(f, ...) EVAL(MAP1(f, __VA_ARGS__, ()()(), ()()(), ()()(), 0))

Si l'argument x est déjà entre parenthèses autour d'une liste d'arguments à passer à la macro f nous voulons simplement que les versions parallèles évitent d'ajouter les parenthèses autour de x :

#define MAP_TUPLES0(f, x, peek, ...) f x MAP_NEXT(peek, MAP_TUPLES1)(f, peek, __VA_ARGS__)
#define MAP_TUPLES1(f, x, peek, ...) f x MAP_NEXT(peek, MAP_TUPLES0)(f, peek, __VA_ARGS__)
#define MAP_TUPLES(f, ...) EVAL(MAP_TUPLES1(f, __VA_ARGS__, ()()(), ()()(), ()()(), 0))

J'ai appelé ça MAP_TUPLES plutôt que MAP_PAIRS parce que ce n'est pas vraiment limité aux paires. Elle peut passer des listes d'arguments de n'importe quelle taille à n'importe quelle macro, tant que le nombre de paramètres de la macro correspond. Vous pouvez même utiliser une macro variadique avec des listes d'arguments de tailles différentes.

Une utilisation de cette MAP_TUPLES pour obtenir votre CREATE_CLASS en supposant que l'original DECL_VAR y DECL_GETSET on dirait :

#define CREATE_CLASS(C, ...)\
  class C {\
    private:\
    MAP_TUPLES(DECL_VAR, __VA_ARGS__)\
    public:\
    MAP_TUPLES(DECL_GETSET, __VA_ARGS__)\
  };

CREATE_CLASS(Person, (const char*, name), (int, age), (float, height))

Voir le exemple complet sur coliru .

1voto

Neil McGill Points 338

Cela vous convient-il ?

#include <iostream>
#include <string>

#define MACRO_DECL_TYPE(t, v) t
#define MACRO_DECL_NAME(t, v) v = {};
#define MACRO_GET_TYPE(t, v)
#define MACRO_GET_NAME(t, v) t get_##v(void) const { \
    std::cout << "called get() for " << #t << " " << #v << " as " << v << std::endl; \
    return v; \
}
#define MACRO_SET_TYPE(t, v)
#define MACRO_SET_NAME(t, v) void set_##v(t to) { \
    v = to; \
    std::cout << "called set() for " << #t << " " << #v << " as " << v << std::endl; \
}

#define CREATE_CLASS(CLASS_NAME, MACRO_VARS)             \
class CLASS_NAME {                                       \
private:                                                 \
    MACRO_VARS(MACRO_DECL_TYPE, MACRO_DECL_NAME)         \
public:                                                  \
    MACRO_VARS(MACRO_GET_TYPE, MACRO_GET_NAME)           \
    MACRO_VARS(MACRO_SET_TYPE, MACRO_SET_NAME)           \
};

#define LIST_MACRO_COMBO(list_macro1, list_macro2, type,name) \
    list_macro1(type,name) list_macro2(type,name)

//
// Example
// g++ test.cpp --std=c++11 ; ./a.out
//
#define Foo_args(list_macro1, list_macro2)       \
    LIST_MACRO_COMBO(list_macro1, list_macro2, int, a) \
    LIST_MACRO_COMBO(list_macro1, list_macro2, float, b) \
    LIST_MACRO_COMBO(list_macro1, list_macro2, std::string, c) \

CREATE_CLASS(Foo, Foo_args)

int main (void)
{
    Foo foo;
    foo.get_a();
    foo.get_b();
    foo.get_c();
    foo.set_a(1);
    foo.set_b(2);
    foo.set_c("hello");
    foo.get_a();
    foo.get_b();
    foo.get_c();
}

sortie

g++ test.cpp --std=c++11 ; ./a.out
called get() for int a as 0
called get() for float b as 0
called get() for std::string c as
called set() for int a as 1
called set() for float b as 2
called set() for std::string c as hello
called get() for int a as 1
called get() for float b as 2
called get() for std::string c as hello

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X