Я пытаюсь создать макрос, который генерирует указатель на экземпляр другого класса для обозначения направленного отношения.
//#define BIND(A,B) ?
//can be modified a little, top header
BIND(CAT,DOG)
BIND(CAT,TREE)
BIND(CAT,RAT)
BIND(DOG,TREE)
//#define MACRO_CAT (need?) ?
//#define MACRO_DOG (need?) ?
Выше приведена соответствующая диаграмма. (В реальном случае, есть более 100 классов.)
Стрелка головы (красная) Right<>
, Стрелка хвоста (зеленая) Left<>
, (ниже фрагмента)
Возможно ли, что приведенный выше код создаст макрос MACRO_CAT
/MACRO_DOG
автоматически как это? : —
//v should not be modified
class Cat{
MACRO_CAT
/* expand to :-
Right<Dog> dogs;
Right<Tree> trees;
Right<Rat> rats;
*/
};
class Dog{
MACRO_DOG
/* expand to :-
Right<Tree> trees;
Left<Cat> cats;
*/
};
это повозка, запряженная волами Макро-магия была бы действительно полезна для поддержания отношений между объектами с максимальной производительностью.
Похоже X-макро это возможное решение, но у меня относительно низкий опыт по этому поводу.
Я также прочитал:
Просто грубое руководство / идея ценится. (полный код не нужен, но я не против)
Редактировать: В реальном случае BIND
разбрасывает во многих заголовках.
Это пример #include
поток (ниже #include
верхний): —
В этом решении я склонялся к TMP, а не к макросам.
Первый шаг — собрать все заявленные привязки. Возможно, можно было бы разрешить свободно объявлять привязки и разбрасывать их по другому коду. Однако потребуется некоторый способ сохранить и обновить состояние списка, что в значительной степени самая загадочная вещь, которую вы хотели бы сделать в C ++. Так что давайте не будем этого делать.
Файлы привязки будут иметь содержать только инструкции препроцессора и обращения к макросу BIND
, следующее:
BIND(Cat, Dog)
BIND(Cat, Tree)
BIND(Cat, Rat)
BIND(Dog, Tree)
Другими словами, предварительно обработанные файлы должны содержать только замещенный вывод из BIND
, Тогда мы можем сэндвич эти между bindTop.h
а также bindBottom.h
:
template <class...>
struct pack;
// Stuff in bindTop.h
#define BIND(T, U) \
pack<struct T, struct U>,
using AllBindings = pack<
// End of bindTop.h, beginning of the binding file(s)
BIND(Cat, Dog)
BIND(Cat, Tree)
BIND(Cat, Rat)
BIND(Dog, Tree)
// End of the binding file(s), beginning of bindBottom.h
void // Pairs up with the last comma,
// will be silently ignored in further processing
>;
#undef BIND
// Stuff in bindBottom.h
Теперь у нас есть наш список привязок внутри AllBindings
,
Следующий шаг: как мы вводим член в класс? Я отказался от макроса и вместо этого использовал наследование членов. Таким образом, определение класса, как:
struct Cat : WithBindings<Cat, AllBindings> { };
… в конечном итоге наследует от нескольких структур, которые будут определять членов Right<Dog> dogs
, Right<Tree> trees
, а также Right<Rat> rats
и, таким образом, сможет получить к ним доступ почти так, как если бы они были его.
Но как объявить, что член типа Right<Dog>
должен быть назван dogs
? Макросы, конечно! Давайте сделаем пустые шаблоны для левых и правых базовых классов:
template <class T, class Binding>
struct MakeLeftMember { };
template <class T, class Binding>
struct MakeRightMember { };
И затем мы будем использовать макрос, чтобы специализировать их для каждого из наших классов, с именем класса и именем соответствующих членов:
#define BINDING_MEMBER_NAME(type_, memberName_) \
template <class T> struct MakeLeftMember<T, pack<type_, T>> { \
Left<type_> memberName_; \
}; \
template <class T> struct MakeRightMember<T, pack<T, type_>> { \
Right<type_> memberName_; \
}
Binding
как ожидается, будет одним из pack<L, R>
что мы определили с BIND
, Теперь создаем экземпляр, например MakeLeftMember<T, pack<L, R>>
отправит к специализации только если T
является R
то есть привязка действительно является левой привязкой для T. Тогда специализация сгенерирует подходящее имя Left<L>
член для наследования T
, В остальных случаях базовый шаблон выбирается, и ничего не происходит.
Последнее недостающее звено конечно WithBindings<T, AllBindings>
, который просто отправляет все привязки в создатели элементов и наследует полученные сгенерированные элементы:
template <class T, class... Bindings>
struct WithBindings<T, pack<Bindings...>>
: MakeLeftMember <T, Bindings>...
, MakeRightMember<T, Bindings>... { };
И там мы идем. Смотрите это в прямом эфире на Coliru!
Других решений пока нет …