Генерация кода с помощью макросов: класс с членами и конструктором

Допустим, я хочу определить классы следующей структуры:

struct MyClass {
int x;
bool y;
float z;
MyClass(QVariantMap data) : x(data["x"]), y(data["y"]), z(data["z"]) {}
};

Как видите, у меня есть QVariantMap (что-то похожее на std::map<std::string, boost::variant<...>> для тех из вас, кто не знаком с Qt), из которых я хочу иметь возможность создавать такой тип без дополнительных знаний о его полях, отсюда и удобный конструктор, который должен «десериализовать» поля из карты.

Мне нужно несколько классов этого стиля, и я хочу, чтобы определения были максимально чистыми, чтобы обеспечить максимальную удобство сопровождения (в отношении опечаток в строковых ключах), удобочитаемость и автоматизацию.

Я думал о макроструктуре, подобной следующей:

DEF_CLASS(MyClass)(
DEF_FIELD(int, x)
DEF_FIELD(bool, y)
DEF_FIELD(float, z)
);

Я не вижу проблем, когда я хочу генерировать только поля, но не конструктор (возможен и другой способ, но я продемонстрирую только первый):

#define DEF_CLASS(CLASSNAME) \
struct CLASSNAME { \
_DEF_CLASS_TAIL/*..."curry the arguments"...*/
#define _DEF_CLASS_TAIL(FIELDS) \
FIELDS \
}
#define DEF_FIELD(TYPE, NAME) TYPE NAME;

Я определяю первый макрос, который запускает определение класса, и использую технику «карри» для пересылки вторых скобок во второй макрос, который помещает содержимое определения класса (FIELDS) и закрывает его потом. Таким образом, такова была моя идея, у меня есть доступ к FIELDS во втором макросе.

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

Я знаю, что если я определю макрос DEF_CLASS_FIELD двумя различными способами, а затем несколько «включить» поля из моего кода определения выше, по одному на каждое определение макроса, я мог бы правильно распечатать их одно за другим. Но так как список полей находится (и должен) быть в пределах определения класса, я не могу просто включить что-то дважды.

Есть ли другие варианты?

Я стараюсь избегать библиотеки препроцессора Boost, но если у вас есть хорошее решение, которое использует это, продолжайте. Однако я очень предпочитаю простое решение, если оно есть.

2

Решение

Вот пример, который на самом деле не создает структуру для кэширования значений из карты, а просто проверяет в конструкторе, что карта содержит поля, как обсуждалось в комментариях к открытому вопросу.

#define DEF_CLASS(CLASSNAME) \
struct CLASSNAME { \
CLASSNAME(QVariantMap& map) {\
_DEF_CLASS_TAIL/*..."curry the arguments"...*/
#define _DEF_CLASS_TAIL(FIELDS) \
FIELDS \
}};
#define CHK_FIELD(TYPE, NAME) \
if (typeid(TYPE)!=typeid(map[#NAME])) \
{ throw std::runtime_error(#NAME" missing or wrong type");}
DEF_CLASS(MyClass)(
CHK_FIELD(int, x)
CHK_FIELD(bool, y)
CHK_FIELD(float, z)
);

Это производит следующее из препроцессора (после запуска через astyle):

struct MyClass {
MyClass(QVariantMap& map) {
if (typeid(int) != typeid(map["x"])) {
throw std::runtime_error("x"" missing or wrong type");
}
if (typeid(bool) != typeid(map["y"])) {
throw std::runtime_error("y"" missing or wrong type");
}
if (typeid(float) != typeid(map["z"])) {
throw std::runtime_error("z"" missing or wrong type");
}
}
};

РЕДАКТИРОВАТЬ: Я не на 100% уверен, что сравнения typeid будет работать так, но это должно быть прямо заменить его проверкой, которая работает.

1

Другие решения

Других решений пока нет …

По вопросам рекламы ammmcru@yandex.ru
Adblock
detector