Как специализировать шаблон для иерархии?
От: Basil2 Россия https://starostin.msk.ru
Дата: 04.03.13 12:58
Оценка:
Есть абстрактный интерфейс
class MyIface;

Есть шаблонный класс
template<typename T>
class TypeHolder;

Я хочу специализировать этот шаблон для указателей на мой интерфейс:
template<>
class TypeHolder<MyIface*>;

template<>
class TypeHolder<MyIface**>;

Надо:
чтобы этот же шаблоны инстанциировались для всех наследников MyIface.
(я почему-то думал, что они будут работать для всей иерархии, примерно также как вызов функции или перехват у catch)

Пока я смог этого добиться только так: с помощью boost::type_traits сперва оторвал у типа указатель, потом проверил остаток на наследование MyIface и, если да, то заменял на MyIface*. Потом, с помощью static_cast, приводил тип обратно. Но как это все криво, имхо, да и кода много получается.

Есть ли более простой способ инстанцировать шаблон для всей иерархии?
Проект Ребенок8020 — пошаговый гайд как сделать, вырастить и воспитать ребенка.
Re: Как специализировать шаблон для иерархии?
От: Кодт Россия  
Дата: 04.03.13 13:50
Оценка: 6 (1)
Здравствуйте, Basil2, Вы писали:

B>Есть ли более простой способ инстанцировать шаблон для всей иерархии?


Эскиз:
// рабочий шаблон
template<class T, class Root = void> struct TypeHolderImpl { ..... }; // основная ветвь
template<class T> struct TypeHolderImpl<T, MyIface> { ..... }; // ветвь для семейства MyIface
template<class T> struct TypeHolderImpl<T, YourIface> { ..... }; // ветвь для семейства YourIface

// селектор (можно сделать на mpl, но на перегрузке функций - проще)
void*      typeholder_selector(void*);
MyIface*   typeholder_selector(MyIface*);
YourIface* typeholder_selector(YourIface*);
// если тип унаследован от нескольких корней, то возникнет неоднозначность
// если YourIface унаследован от MyIface, то будет выбран он, как более точный

// метафункция селектора
template<class T> struct GetRootType : remove_pointer<decltype(typeholder_selector((T*)0))> {};

// шаблон-диспетчер
template<class T> struct TypeHolder : TypeHolderImpl<T, typename GetRootType<T>::type> {};
Перекуём баги на фичи!
Re: Как специализировать шаблон для иерархии?
От: uzhas Ниоткуда  
Дата: 04.03.13 14:05
Оценка: 6 (1)
Здравствуйте, Basil2, Вы писали:

B>Есть ли более простой способ инстанцировать шаблон для всей иерархии?

пример: http://liveworkspace.org/code/wExAD$3
Re[2]: Как специализировать шаблон для иерархии?
От: Кодт Россия  
Дата: 04.03.13 15:45
Оценка:
К>Эскиз:

и полностью рабочий код на LWS: http://liveworkspace.org/code/Vo2XO$0
Перекуём баги на фичи!
Re[2]: Как специализировать шаблон для иерархии?
От: Basil2 Россия https://starostin.msk.ru
Дата: 04.03.13 16:20
Оценка:
Здравствуйте, uzhas, Вы писали:

U>Здравствуйте, Basil2, Вы писали:


B>>Есть ли более простой способ инстанцировать шаблон для всей иерархии?

U>пример: http://liveworkspace.org/code/wExAD$3
Спасибо!

А как можно различить MyIface* и MyIface**? Мне оба случая нужно перегрузить, причем по разному. То есть bool уже тоже не хватит...
Проект Ребенок8020 — пошаговый гайд как сделать, вырастить и воспитать ребенка.
Re[2]: Как специализировать шаблон для иерархии?
От: Basil2 Россия https://starostin.msk.ru
Дата: 04.03.13 16:26
Оценка:
Здравствуйте, Кодт, Вы писали:

B>>Есть ли более простой способ инстанцировать шаблон для всей иерархии?


К>Эскиз:

К>
К>// рабочий шаблон
К>template<class T, class Root = void> struct TypeHolderImpl { ..... }; // основная ветвь
К>template<class T> struct TypeHolderImpl<T, MyIface> { ..... }; // ветвь для семейства MyIface
К>template<class T> struct TypeHolderImpl<T, YourIface> { ..... }; // ветвь для семейства YourIface

К>// селектор (можно сделать на mpl, но на перегрузке функций - проще)
К>void*      typeholder_selector(void*);
К>MyIface*   typeholder_selector(MyIface*);
К>YourIface* typeholder_selector(YourIface*);
К>// если тип унаследован от нескольких корней, то возникнет неоднозначность
К>// если YourIface унаследован от MyIface, то будет выбран он, как более точный

К>// метафункция селектора
К>template<class T> struct GetRootType : remove_pointer<decltype(typeholder_selector((T*)0))> {};

К>// шаблон-диспетчер
К>template<class T> struct TypeHolder : TypeHolderImpl<T, typename GetRootType<T>::type> {};
К>

Спасибо. Как быть в случае, если мне надо отдельно инстанцировать MyIface**? Достаточно ли просто добавить
MyIface**   typeholder_selector(MyIface**);



P.S. В принцип работы вкурил. С++ жжет
Проект Ребенок8020 — пошаговый гайд как сделать, вырастить и воспитать ребенка.
Re[3]: Как специализировать шаблон для иерархии?
От: Кодт Россия  
Дата: 04.03.13 20:25
Оценка:
Здравствуйте, Basil2, Вы писали:

B>Спасибо. Как быть в случае, если мне надо отдельно инстанцировать MyIface**? Достаточно ли просто добавить

B>
B>MyIface**   typeholder_selector(MyIface**);
B>


Если задача стоит — свести все MyIface, MyIface*, MyIface** к одной общей специализации, то достаточно определить remove_pointer так, как тебе это хочется:
template<class T> struct remove_all_stars { typedef T type; };
template<class T> struct remove_all_stars<T*> : remove_all_stars<T> {};
// кстати, это тоже не повредит - чтобы MyIface*const**const*const правильно обрабатывались
template<class T> struct remove_all_stars<const T> : remove_all_stars<T> {};
template<class T> struct remove_all_stars<volatile T> : remove_all_stars<T> {};

Собственно, я в коде на LWS это и показал. (без снятия константности, правда).
Тогда TypeHolder<Derived> и TypeHolder<Derived***> будут относиться к TypeHolderImpl<Derived,Base> и TypeHolderImpl<Derived***,Base> соответственно.

Если нужно, чтобы разные количества звёздочек относились к разным специализациям, то нужно отметить вот какую штуку.
Derived* естественно приводится к Base*. Но Derived** не приводится к Base** (и наоборот тоже), потому что это ломает принцип подстановки Лисков.
Конечно, чисто формально мы это можем сделать — на уровне работы с параметрами шаблона, просто как символьная информация: "какой-то тип и сколько-то звёздочек", но прагматического смысла я в этом не вижу.
Как именно это технически реализовать — чтобы Holder<Derived> относился к HolderImpl<Derived,Base>, а Holder<Derived*> — к Holder<Derived*,Base*> — надо немного подумать.
Воспользоваться способностями компилятора к выбору наилучшей сигнатуры, по крайней мере, в лоб, мы не можем.
Можем написать список типов — вроде mpl::vector<MyIface,YourIface,MyIface*,MyIface***> — и пробегать по нему последовательно, делая с типами вообще всё, что захотим, — это нам под силу. Но
1) громоздко (впрочем, на вкус и цвет)
2) централизованно (хотя, у централизации есть свои достоинства)
3) вместо естественного порядка (наиболее конкретные классы вперёд) мы получаем произвольный порядок, т.е. mpl::vector<Base,Derived> поймает всех наследников в специализацию для Base

Кстати, что мне не нравится в моём решении — это то, что пришлось дублировать информацию: у нас есть специализации TypeHolderImpl и рядом с ними — функции-селекторы.
Нужно подумать, как бы от этого избавиться. Скажем, рожать френд-функции... причём, делать это автоматизированно, через шаблоны.
template<class T, class S> struct HolderImpl { ..... };
template<class T> struct HolderImpl<T, Introduce<MyIface> > { ..... }; // где Introduce<MyIface> определит селектор, и так далее...

но это даже не эскиз, а вообще мысль вслух.
Либо простой способ — автоматическая генерация С++ного текста, то есть, макропроцессор.
Перекуём баги на фичи!
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.