C ++ вариационный макрос для пар - PullRequest
3 голосов
/ 08 октября 2019

Мне известен макрос MAP , который можно использовать для применения функции макроса к списку аргументов с переменным числом аргументов. Но как бы вы применили функцию макроса к парам переменных аргументов?

Я хочу создать что-то вроде этого:

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

Ответы [ 2 ]

2 голосов
/ 09 октября 2019
CREATE_CLASS(Person, (const char*, name), (int, age), (float, height))

Это будет более простой вариант для работы, поскольку синтаксис препроцессора обрабатывает сбалансированные скобки таким образом, что, например, (const char*, name) является единственным аргументом макроса, несмотря на то, что содержит запятую.

Таким образом, одним простым решением было бы предоставить макросы-обертки, которые принимают аргумент в форме (type, varname) и передают его элементы вашим действительным макросам с двумя аргументами:

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

Так, например, когдарасширение MAP(DECL_VAR_PAIR, __VA_ARGS__) в последней строке CREATE_CLASS передает один аргумент (int, age) в DECL_VAR_PAIR, шаги расширения включают в себя:

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

Хотя, если у вас есть куча вещей, которые вы хотитеиз-за парных аргументов создание всех этих макросов-оберток может стать громоздким. Вместо этого мы можем добавить MAP -подобный макрос, который ожидает, что его аргументы будут списками, заключенными в скобки. Во-первых, обратите внимание, что в <map.h> шаги, которые фактически применяют макрос к одному из аргументов, тесно связаны с основным макросом MAP:

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

Если аргумент x уже является скобкамивокруг одного списка аргументов для передачи в макрос f, мы просто хотим, чтобы параллельные версии пропускали добавление скобок вокруг 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))

Я назвал это MAP_TUPLES вместо MAP_PAIRS, потому чтоэто на самом деле не ограничивается парами. Он может передавать списки аргументов любого размера любому макросу, если совпадает количество параметров макроса. Вы даже можете использовать макрос с переменным списком аргументов различных размеров.

Использование этого MAP_TUPLES для получения CREATE_CLASS, при условии, что ваши исходные DECL_VAR и DECL_GETSET, выглядят так:

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

См. Полный пример в coliru .

1 голос
/ 09 октября 2019

Эта работа для вас?

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

вывод

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
...