Добро пожаловать в форум, Guest  >>   Войти | Регистрация | Поиск | Правила | В избранное | Подписаться
Все форумы / C++ Новый топик    Ответить
Топик располагается на нескольких страницах: [1] 2 3 4 5 6   вперед  Ctrl      все
 Пятничная шабонная магия  [new]
CEMb
Member

Откуда: public T{};
Сообщений: 1958
Я надеваю свой шаблонный плащ и шаблонную шляпу...

Была у меня иерархия (классов) на основе вложенности, A содержит вектор объектов B, который в свою очередь содержит вектор объектов С. Всё ок.

Объекты класса C содержали ещё вектор объектов класса D, возможно пустой. Класс D содержал shared_ptr<C>. Чтобы было нагляднее, класс D, это, например, персонаж, а класс C - это клетка карты. Таким образом D всегда находится на какой-то клетке, держит с ней связь через shared_ptr<C>, а клетка содержит в себе список персонажей, которые на ней находятся. Всё ок, всё работало. Тут важный момент про конструкции языка: так как C и D ссылаются друг на друга, то в одном из их заголовков будет упоминание другого.

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

template <typename T> class A : public T {};

это породило цепную реакцию шаблонизации всей иерархии классов вплоть до класса A, и там есть ещё 3 обслуживающих класса (тоже шаблоны) которые используют A и друг друга в качестве параметра шаблона. Все эти классы (кто-то ещё раз) ошаблонились. Там тоже есть интересный вопрос по синтаксису, но это всё потом, главное - проблема теперь в C и D. По задумке слабой связанности, С теперь не имеет вектор с D, но есть C1<T>, у которого вектор<T> и функции работы с ним. D теперь без указателя shared_ptr<C>, но есть D1<T> у которого shared_ptr<T> и методы работы с ним.

Всё ок, до тех пор пока не начнёшь всё это создавать, потому что должно быть D1<C1> и C1<D1>, как это сделать? Выше я упоминал, что в случае обычной ссылки друг на друга, один из классов упоминается в заголовке другого и всё хорошо. А как тут быть? Попытался поразбивать C/C1 и D/D1 на иерархии и как-то использовать другие классы в качестве параметров шаблонов, но только всё запутал.

В результате шаблонной магии я вчера развалил себе весь проект напрочь(damage 281 hp per class), т.е. сейчас у меня ни одного работающего класса нет, так как все зависят от D и C.
23 дек 16, 06:34    [20039706]     Ответить | Цитировать Сообщить модератору
 Re: Пятничная шабонная магия  [new]
Anatoly Moskovsky
Member

Откуда: Odessa
Сообщений: 6386
CEMb
Но я захотел сделать лучше, захотел сделать слабые связи и масштабируемость. При этом решил воспользоваться шаблонной магией, заклинанием:

template <typename T> class A : public T {};


А с чего вы решили, что это хоть как-то устраняет зависимость? )))

CEMb
В результате шаблонной магии я вчера развалил себе весь проект напрочь(damage 281 hp per class), т.е. сейчас у меня ни одного работающего класса нет, так как все зависят от D и C.

Так всегда бывает, когда не к месту применяешь заклинания )))
23 дек 16, 10:43    [20040344]     Ответить | Цитировать Сообщить модератору
 Re: Пятничная шабонная магия  [new]
Пётр Седов
Member

Откуда: Санкт-Петербург
Сообщений: 674
CEMb, умные указатели -- это неуклюжая попытка превратить C++ в подобие managed-языка. При разработке игр на C++ это не особо нужно.
24 дек 16, 03:44    [20044004]     Ответить | Цитировать Сообщить модератору
 Re: Пятничная шабонная магия  [new]
MasterZiv
Member

Откуда: Питер
Сообщений: 34537
CEMb
[i]

Была у меня иерархия (классов) на основе вложенности, A содержит вектор объектов B, который в свою очередь содержит вектор объектов С. Всё ок.

.


что уж тут ок? иерархия (классов) на основе вложенности - это как-то странно, это не ок...
24 дек 16, 09:41    [20044128]     Ответить | Цитировать Сообщить модератору
 Re: Пятничная шабонная магия  [new]
MasterZiv
Member

Откуда: Питер
Сообщений: 34537
CEMb
[i. Тут важный момент про конструкции языка: так как C и D ссылаются друг на друга, то в одном из их заголовков будет упоминание другого.
.


ничего подобного, если они просто ссылаться друг нам друга, то в их реализации достаточно знать определение каждого из партнеров, но в объявления классов достаточно сделать лишь forward declaration другого класса, в виде

class D;

никакая шаблонная магия тут не нужна.
24 дек 16, 09:48    [20044133]     Ответить | Цитировать Сообщить модератору
 Re: Пятничная шабонная магия  [new]
CEMb
Member

Откуда: public T{};
Сообщений: 1958
Anatoly Moskovsky
А с чего вы решили, что это хоть как-то устраняет зависимость? )))
Изначальные классы друг о друге ничего не знают.
Пётр Седов
CEMb, умные указатели -- это неуклюжая попытка превратить C++ в подобие managed-языка. При разработке игр на C++ это не особо нужно.
Умные указатели тут ни при чём, я мог бы использовать что-то другое для связи, это ситуацию не меняет, вопрос же был не про это А игры тут при чём? Как игры или не игры влияют принципиально на разработку?
MasterZiv
что уж тут ок? иерархия (классов) на основе вложенности - это как-то странно, это не ок...
Ну не иерархия, а зависимость, всё равно всё ок. Иерархия - логическая, т.е. элементы одного класса являются частью другого (членами класса). Иерархия получается логическая.
MasterZiv
достаточно сделать лишь forward declaration другого класса, в виде
Ну я это и имел ввиду, когда говорил об упоминании.
MasterZiv
никакая шаблонная магия тут не нужна.
Почему никто не читает то, что я пишу шаблонная магия нужна была для построения двух как можно более независимых иерархий классов. Это необходимо для более удобной разработки, когда мне надо будет менять базовые элементы в обоих иерархиях. Сейчас зависимости между двумя иерархиями явно присутствуют в классах, это неудобно.
26 дек 16, 05:40    [20047346]     Ответить | Цитировать Сообщить модератору
 Re: Пятничная шабонная магия  [new]
mayton
Member

Откуда: loopback
Сообщений: 43003
А можно пример использования? Чтобы почувствовать всю "крутость" и "загадочность" этой магии.
26 дек 16, 10:00    [20047726]     Ответить | Цитировать Сообщить модератору
 Re: Пятничная шабонная магия  [new]
CEMb
Member

Откуда: public T{};
Сообщений: 1958
mayton
А можно пример использования? Чтобы почувствовать всю "крутость" и "загадочность" этой магии.
ага, если речь про
template <typename T> class A : public T {};
то тут всё просто. Данная конструкция делает надстройку над классом, к которому применяется. Применений тут много, я хочу использовать увеличение информации внутри класса. Есть класс А:
class A {
};
делаем шаблон:
template <typename T> class CUID : public T {
	unsigned int uid_ = 0;
public:
	unsigned int UIDGet() const {return uid_;}
	void UIDSet(unsigned int uid) {uid_ = uid;} 
};
после этого мы можем делать так:
CUID<A> auid; - Объект с функционалом класса А и с уидами. При этом ни А ничего не знает про CUID, ни CUID ничего не знает про A и эти два класса могут разрабатываться независимо.
Мы можем вешать CUID на любые классы: CUID<B> - это отличие от наследования, где если мы хотели добавить uid в дочерний класс и отнаследовались - чтобы навешать uid на другой класс, нам надо заново наследоваться (это грубое сравнение, в реальной жизни, само собой, мы бы по-другому сделали). Тут же достаточно просто в объявлении объекта указать CUID чтобы получить дополнительный нужный функционал. Ногами не пинайте, если вдруг рассказываю понятные вещи, я обычно всегда стараюсь подробно всё рассказывать.
"Магия" у меня там в проекте заключалась в том, что я хотел таким образом развести независимо 3 иерархии классов, которые у меня были изначально связаны друг с другом, чтобы потом можно было разрабатывать их независимо. Шаблонизацию я закончил, осталось сделать правильные объявления. Надеюсь.
26 дек 16, 13:59    [20048929]     Ответить | Цитировать Сообщить модератору
 Re: Пятничная шабонная магия  [new]
MasterZiv
Member

Откуда: Питер
Сообщений: 34537
CEMb
MasterZiv
никакая шаблонная магия тут не нужна.
Почему никто не читает то, что я пишу шаблонная магия нужна была для построения двух как можно более независимых иерархий классов. Это необходимо для более удобной разработки, когда мне надо будет менять базовые элементы в обоих иерархиях. Сейчас зависимости между двумя иерархиями явно присутствуют в классах, это неудобно.


Для этого шаблонная магия тоже не нужна.
Для этого нужны классические паттерны, к которым шаблонная магия никак не относится.
Итого, у меня уже вырабатывается чёткое подозрение, что ты вообще что-то не то делаешь, что следовало бы.
Но что тебе нужно, я до конца не понял, так что думай сам.
26 дек 16, 14:23    [20049052]     Ответить | Цитировать Сообщить модератору
 Re: Пятничная шабонная магия  [new]
egorych
Member

Откуда: и зачем;
Сообщений: 4766
MasterZiv
Для этого шаблонная магия тоже не нужна.
Для этого нужны классические паттерны, к которым шаблонная магия никак не относится.
Итого, у меня уже вырабатывается чёткое подозрение, что ты вообще что-то не то делаешь, что следовало бы.
Но что тебе нужно, я до конца не понял, так что думай сам.
ну, ТС пусть читает Александреску, он как то же крестил ежа с ужом, собирал патерны на шаблонах.
Так то я вот тоже не понимаю, что человек задумал, но вдруг чем то ему поможет
26 дек 16, 14:55    [20049221]     Ответить | Цитировать Сообщить модератору
 Re: Пятничная шабонная магия  [new]
Пётр Седов
Member

Откуда: Санкт-Петербург
Сообщений: 674
CEMb
Пётр Седов
CEMb, умные указатели -- это неуклюжая попытка превратить C++ в подобие managed-языка. При разработке игр на C++ это не особо нужно.
Умные указатели тут ни при чём, я мог бы использовать что-то другое для связи, это ситуацию не меняет, вопрос же был не про это А игры тут при чём?
Прочитал вот это:
CEMb
Чтобы было нагляднее, класс D, это, например, персонаж, а класс C - это клетка карты. Таким образом D всегда находится на какой-то клетке, держит с ней связь через shared_ptr<C>, а клетка содержит в себе список персонажей, которые на ней находятся.
...
damage 281 hp per class
и подумал, что речь идёт о разработке игры. А то по именам типа «A», «B» сложно понять, что происходит.

CEMb
Как игры или не игры влияют принципиально на разработку?
Если игровую логику пишут на C++, то значит хотят выжать максимум из железа (по скорости и по памяти), а значит умные указатели лучше не использовать. std::shared_ptr выделяет блок памяти, в котором хранит счётчик ссылок и deleter.
27 дек 16, 01:10    [20051054]     Ответить | Цитировать Сообщить модератору
 Re: Пятничная шабонная магия  [new]
CEMb
Member

Откуда: public T{};
Сообщений: 1958
MasterZiv
Для этого шаблонная магия тоже не нужна.
Для этого нужны классические паттерны, к которым шаблонная магия никак не относится.
какие?
egorych
ну, ТС пусть читает Александреску, он как то же крестил ежа с ужом, собирал патерны на шаблонах.
ну вот я как раз его и читаю.
Пётр Седов
Прочитал вот это:
так я и говорю, чтобы было нагляднее :)
Пётр Седов
Если игровую логику пишут на C++, то значит хотят выжать максимум из железа (по скорости и по памяти), а значит умные указатели лучше не использовать.
наверно, получается так. Хотя не уверен, что работа в играх с каким-то внутреигровыми объектами на shared_ptr съест ресурсы железа соразмерно с той же графикой.
27 дек 16, 05:22    [20051165]     Ответить | Цитировать Сообщить модератору
 Re: Пятничная шабонная магия  [new]
CEMb
Member

Откуда: public T{};
Сообщений: 1958
В общем, получилось как-то так:

// base class A
class CA {
};

// AEx with gypotethic B
template <template <typename CA>, class BI> class CAWithB : public CA {
vector<BI<CAWithBI>> m_vb;
};

//base class B
class CB {
};

// BEx with gypothethic A
template <typename AI> class CBWithAI : public CB {
AI *m_pA;
}

// code
CAWithBI<CBWithAI> a;

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

Ну как, стал я шаблонным магом 2-го уровня? о_о
29 дек 16, 07:58    [20058755]     Ответить | Цитировать Сообщить модератору
 Re: Пятничная шабонная магия  [new]
CEMb
Member

Откуда: public T{};
Сообщений: 1958
решил я зайти с другой стороны.
т.е. первая сторона: при объявлении переменной шаблонного класса мы передаём в шаблон все базовые типы и шаблоны используемых классов, и потом в этом шаблоне раздаём типы всем шаблонным-шаблонным параметрам. Это всё хорошо и правильно, потому что это гарантия использования одних типов во всех используемых классах.
другая сторона: шаблон, который должен знать только про один входящий обслуживаемый класс, при этом иногда должен возвращать или работать с типами, с которыми работает этот обслуживаемый класс-параметр. Если более детально, то в первом случае мы инициализируем всё в рамках одной переменной, а во втором, эту переменную(этот тип) используем для создания сервисного объекта. Вот тут тонкое место про типосовместимость: нам по-хорошему не надо заводить в шаблоне параметры для базовых типов, используемых в первом случае, просто потому что есть возможность там задать другие типы. У меня в результате так и получилось: пока я разрабатывал, я в шаблон добавил просто родительские классы, а потом при компиляции "оказалось", что типы этих классов не совпадают с типами классов, с которыми работает обслуживаемый класс.
Ок, поэтому во втором случае полезно передавать только основной класс в шаблон и из него как-то получать типы, с которыми работает этот класс. Если мы работаем как транслятор или посредник-обработчик, т.е. вызываем функции базовых классов или передаём данные базовых типов из обслуживаемого класса наружу, то auto подходит великолепно. Но вот если нам нужны какие-то переменные базовых типов внутри класса, то auto уже не подходит. Я решил использовать decltype. При этом, так как у меня из обслуживаемого класса доступны только функции, можно написать decltype(&f()) и получить тип. И тут у меня два вопроса
1. если функция возвращает shared_ptr<T>, то как из этого типа выдернуть T?
2. почему я не могу сказать decltype(&f()) если f - private или protected? sizeof вроде в таких случая работает.
20 янв 17, 07:08    [20125380]     Ответить | Цитировать Сообщить модератору
 Re: Пятничная шабонная магия  [new]
CEMb
Member

Откуда: public T{};
Сообщений: 1958
CEMb
1. если функция возвращает shared_ptr<T>, то как из этого типа выдернуть T?
decltype(foo.get()); же...
20 янв 17, 07:17    [20125388]     Ответить | Цитировать Сообщить модератору
 Re: Пятничная шабонная магия  [new]
ermak.nn
Member

Откуда: Нижний Новгород
Сообщений: 55
CEMb
CEMb
1. если функция возвращает shared_ptr<T>, то как из этого типа выдернуть T?
decltype(foo.get()); же...

Вернет T*, нужно хотя бы decltype(*foo)
Вам не поможет element_type? Т.е. foo.element_type
20 янв 17, 10:26    [20125879]     Ответить | Цитировать Сообщить модератору
 Re: Пятничная шабонная магия  [new]
CEMb
Member

Откуда: public T{};
Сообщений: 1958
ermak.nn, да, конечно, я просто навскидку написал общую идею. Спасибо.

вообще, если полностью, то это выглядит так:

template <typename T0> A0
{
protected:
	static T0& __typer () { ststic T0 t; return t;}
};

template <typename T>class A : public T
{
public:
	using TYPE = typename std::remove_reference<decltype(*&A0::__typer())>::type;
};

таким образом TYPE для внешнего наблюдателя это T0, если A<A0<T0>>::TYPE. Но:
1. криво как-то выглядит...
2. всё-таки есть сомнения в идеологической правильности такого подхода
20 янв 17, 11:31    [20126260]     Ответить | Цитировать Сообщить модератору
 Re: Пятничная шабонная магия  [new]
CEMb
Member

Откуда: public T{};
Сообщений: 1958
Доработал идею полностью на шаблонах сегодня утром, хочется немного высказаться Картинка с другого сайта.
Для простоты понимания, значит, иерархия была такая, по аналогии:
1. Библиотека - шкаф - книга - страница.
2. Закладка.
3. Поиск.
4. Консоль.
Это всё просто аналогии, на самом деле всё абстрактно. Это важно.

Изначально шаблонов не было, иерархия №1 была "классической": в библиотеке шкафы, в шкафах книги, в книгах страницы. Закладка могла указывать на страницу, на одной странице могло быть различное (0..N) число закладок. Само собой, когда я делал иерархию №1, я сделал интерфейс книги так, что можно получить страницу по номеру. Потом, когда я делал шкаф, я сделал интерфейс для получения книги по названию, ну и, для удобства по названию книги и номеру страницы - страницу из шкафа. В аналогии звучит весело, но когда на руках абстрактные классы, это вроде как нормально и удобно. Ок. С библиотекой в результате получилось тоже самое: по номеру шкафа, названию книги и номеру страницы из библиотеки можно сразу получить страницу.

С закладками всё немного не так, как в жизни, закладке пришлось-таки помнить своё место от шкафа до страницы, иначе смысл такой закладки терялся. Но вернёмся к первой иерархии. Когда я стал её переводить на шаблоны, названия классов выросли до 3 экранов. Почему так. Библиотека должна была знать классы шкафа, книги и страницы, чтобы уметь возвращать объект страницы. При этом явно указывать ни один из этих классов нельзя, они все идут как параметры шаблона, итого 3 (на самом деле 4, ещё и закладки, но это потом). Далее, класс шкафа должен так же уметь работать с книгами и страницами, которые тоже должны быть параметры его шаблона. Шаблона. Вот теперь делаем шаг назад и в шаблоне библиотеки заменяем первый шаблонный параметр (шкаф) этим самым шаблоном шкафа. Тоже самое проделываем с книгой, но ещё и в шаблоне шкафа заменяем первый параметр, книгу, на шаблон книги. В результате у нас получается прикольная иерархия шаблонов, которые внутри себя передают свои параметры друг другу. Название класса библиотеки почти влазит в один экран. Ура.

И вот теперь надо добавить закладки. Закладка по сложности описания почти доходит до библиотеки, потому что у неё в параметрах тоже шкаф, книга и страница. Но при этом посмотрим на первую иерархию, там тоже должна появится закладка, как минимум в классе библиотеки и в классе страницы. А так как страница фигурирует в книге и шкафу, то и там тоже. Страница превращается в шаблон с параметром "закладка" и этот шаблон в качестве параметра заменяет класс в шаблонах библиотеки, шкафа и книги.

Если вы ещё со мной, то вернёмся к закладке: у неё в качестве параметров должны добавляться шкаф, книга и страница. При этом они должны быть идентичны по (шаблонной)сигнатуре параметрам в первой иерархии. Но шкаф, книга и страница сами принимают на вход шаблон закладки. Рекурсия :) Но задача решается применением магии 20058755, описанной выше, но более сложной. Там 2 класса, тут класс + иерархия 3 классов, все друг на друга ссылаются. Нагрузку по прокидыванию себя в шаблонные параметры я оставил в закладке, так как иерархия №1 и так была перегружена магией.

Если до этого всё было более-менее понятно, до дальше шли пункт 3 и 4. Это тоже должны были стать 2 шаблона, первый принимал в качестве параметра библиотеку, второй принимал на вход библиотеку и поиск. Т.е. из консоли должно было возможно делать поиск в библиотеке, грубо говоря. И вот тут я стал запутываться, так как шаблонный параметр библиотеки в обоих этих классах тоже должен был быть шаблоном, причём ровно таким многоэтажным, каким он был в иерархии №1. Почему так: поиск и консоль должны были уметь возвращать, например, страницу по закладке. Т.е. класс страницы там должен был как-то появиться, чтобы поиск и консоль могли с ним работать.

На самом деле я всё писал не в такой последовательности, просто потому что изначально всё было уже написано без шаблонов, а потом я пытался всё и сразу перевести на шаблоны. И проблемы начались где-то по середине, когда я пытался свести все шаблоны к нужному виду, чтобы они нормально принимались в качестве параметров другими шаблонами, опять рекурсия. А надо ещё учесть, что у всех шаблонов были родительские классы, с информацией. Шаблоны, по моей задумке, нужны были для связи. Поэтому для простоты я ввёл порядок в описание, чтобы было нагляднее.

И вот когда в понедельник с утра я понял, что всё слегка запуталось, я сел и посмотрел на иерархию №1. И вот тогда я решил выкинуть все транзитные взаимосвязи. В библиотеке только шкафы, в шкафах только книги, а в книгах страницы. И сразу куча "кода" из названий классов пропала, во-первых. Во-вторых, я разбил классы библиотеки на отдельные шаблоны-прослойки, которые просто работали с транзитными данными, принимая в качестве шаблонных параметров только родителя и только целевой тип. Например библиотека_с_книгами<библиотека, книга>который, например, просто по названию возвращает просто книгу(из auto шкафа, см. далее). Код упростился и распался на несколько никак не зависящих частей. Они все наследовались один раз, при декларации класса(шаблона), и потом про них можно забыть. Точно так же выкинул упоминания класса страницы из шаблона шкафа, и часть кода сразу можно было выкинуть(она "волшебным образом" оказалась уже в прослойках для класса библиотеки).

Вот тут вылезла польза auto. Если раньше я считал его просто удобным иногда, а часто вредным из-за нечитаемости кода, тот тут оно просто стало необходимым. Изначально библиотека ведает только о шкафах. Абстрактная прослойка, работающая с книгами, ничего про класс шкафов не знает, но имеет доступ к родительским методам для работы с ними. Поэтому auto для определения типа, возвращаемого методом родительского класса - самое оно! Пришлось местами переписать функции, чтобы они именно возвращали объекты. Не знаю, хорошо это или плохо. Раньше они возвращали bool, теперь кидают исключение.

Но смотрите, я мог и не делать эти прослойки тут. Основной бонус, к которому меня подтолкнули шаблоны - разрыв связей, и это мне удалось по максимуму. Вот эти код-прослойки я могу делать прямо внутри классов поиска и консоли: я могу написать там тот же код на auto для получения нужных объектов нужных классов и потом получения их дочерних объектов и так далее. Или могу отнаследовать входящий шаблонный параметр библиотеки от готового кода прослойки. Неважно. Главное у меня теперь есть дважды независимый код, который можно использовать на конкретных объектах, о которых он ничего не знает (кроме названий функций). Главным уроком было то, что когда я переписал "нормальный" код на шаблоны, сразу стало видно, что он совсем не нормальный, и сразу стало видно, как надо сделать правильно.

В результате у меня семь файлов(4+1+1+1) с описанием независимых друг от друга классов, без forward declaration, их можно разрабатывать отдельно, что и было целью моего приключения: я хотел иметь возможность фиксировать эти классы на некоторых моментах разработки, а потом мочь заменять их другими с более широким или просто иным функционалом, при этом чтобы другие работали как прежде, а так же мочь возвращаться к прежним классам, если нужно. И теперь я это могу, просто заменив название одного класса на другой в строке декларации объекта :)

И дайте мне уже мой чёртов второй уровень шаблонной магии
24 янв 17, 08:45    [20138085]     Ответить | Цитировать Сообщить модератору
 Re: Пятничная шабонная магия  [new]
Anatoly Moskovsky
Member

Откуда: Odessa
Сообщений: 6386
CEMb,

Ваша ошибка в том, что вы вместо описания конкретной предметной области пытались в первом посте объяснить все какими-то абстрактными сущностями A B C.
Если бы вы сразу написали про книги, то вам бы сразу и сказали что в сущности Страница не должно быть никакой ссылки на сущность Закладка. И никаких бы циклических ссылок не было бы ))
24 янв 17, 15:26    [20140439]     Ответить | Цитировать Сообщить модератору
 Re: Пятничная шабонная магия  [new]
CEMb
Member

Откуда: public T{};
Сообщений: 1958
Anatoly Moskovsky
Ваша ошибка в том, что вы вместо описания конкретной предметной области пытались в первом посте объяснить все какими-то абстрактными сущностями A B C.
Если бы вы сразу написали про книги, то вам бы сразу и сказали что в сущности Страница не должно быть никакой ссылки на сущность Закладка. И никаких бы циклических ссылок не было бы ))

Нет никакой ошибки. Предметную область я добавил в описание для того, чтобы было легче понять логическую связь между классами. Выше я приводил пример с картами. Если писать про А, В, С, никто читать не будет, потому что потеряется на втором абзаце :) читать и одновременно строить и держать в голове абстрактную модель в общем случае тяжело для человека.
Сделать ссылающиеся друг на друга и при этом независимые шаблоны - это была цель. Как альтернатива избавлению от связей в коде. По-моему, получилось отлично: никаких #include, forward declaration, интерфейсов, собирается на момент компиляции. Лучше не бывает!
Где мой диплом шаблонного мага? :) я уже придумал ТЗ на новое заклинание для получения 3-го уровня.
25 янв 17, 05:23    [20142570]     Ответить | Цитировать Сообщить модератору
 Re: Пятничная шабонная магия  [new]
CEMb
Member

Откуда: public T{};
Сообщений: 1958
Я надеваю свой шаблонный плащ и шаблонную шляпу...

Статическая альтернатива polymorphic hell

Вчера вечером натолкнулся на такую проблему:
- Есть некое множество классов, назовём их Shape, у которых есть метод Compare, который на вход принимает ссылку на другой объект Shape.
- Есть некий класс А, который должен содержать вектор этих самых Shape. У А есть тоже метод Compare, который на вход принимает ссылку на А.
- В А::Compare нужно все родные Shape "сравнить" через Compare входящего объека А. При этом если классы родного Shape и входящего Shape не совпадают - делать ничего не надо. Строгая типизация, типа.

Сначала я сделал так: все классы Shape отнаследовал от одного базового, скажем ShapeBase, метод Compare сделал виртуальным, в классе А завёл вектор ShapeBase и в методе А::Compare устроил двойной цикл сравнения "каждый с каждым".

Но тут обнаружилась проблема: строгая типизация. Нужно было на ходу извлекать реальные типы из итераторов ShapeBase. При условии, что А::Compare должен отрабатывать так быстро, как только можно, методы динамического определения типов оказались плохим вариантом. Так как долго.

В результате я опять решил всё сделать на шаблонах. Проблема была в том, что вектор в А должен содержать объекты разных классов. Я решил сделать шаблон, с двумя параметрами, один - базовый класс A, второй - класс Shape, вектор которого я объявил внутри шаблона.
template <typename P, typename S> class Shaper : public P
{
         std::vector<S> m_vS;
public:
         bool Compare(const Shaper&);
         void AddShape(S&& s);
};

Ok, добавляя этот шаблон к А(через параметр P), я получал для одного класса Shape то, что требовалось.

Теперь надо чтобы можно было добавить в структуру второй шаблон. Параметр Р тут как верёвочка, на которую нанизываются шаблоны. Если теперь просто навесить сверху второй Shape, он так же удачно заработает, но отвалится первый, его методы Compare и AddShape перестанут работать, так как они перекрыты вторым шаблоном, и их не будет видно.
Проблема решается через добавление
using S::Compare;
using S::AddShape;

Теперь при добавлении объектов Shape1, Shape2 и так далее, все они складываются в свои вектора. Ура.
Осталась одна проблема: базовый класс А. Он должен поддержать то, что написано в using. Туда достаточно добавить минимальные заглушки (шаблону в большей степени всё равно), и вся иерархия становится завершённой(в решении я вынес это в отдельный шаблон, чтобы не мусорить в А). Compare потребуется указать параметр, так как он будет зваться рекурсивно, а вот AddShape всё равно на параметры, он зовётся только снаружи.

Минусы:
- заведение закрывающего класса Shaper. Хотя в обычном варианте так же присутствует "лишний" класс - основа иерархии.
- если в иерархии А были явные конструкторы с параметрами - придётся их пробросить в Shaper и P.
- обработку (обход) по вектору придётся вынести в сами методы шаблона Shaper, для рекурсии. А это даже уже плюс.
- "вектор" объектов нельзя сортировать/перестраивать как угодно, сложно (но реализуемо) обращение по индексу.
- объявление типа конечного класса занимает много места :)

Плюсы:
+ скорость обхода "вектора" на порядок меньше, чем в классическом варианте: количество итераций пропорционально N против N2 в случае с полным обходом.
+ статическая типизация. И нет virtual.
+ "вектор" "отсортирован" автоматически - как бы мы ни добавляли элементы в "вектор", при обработке они пойдут порядке объявленных в шаблоне классов. Плюс сомнительный, скорее просто фича.
+ все классы Shape и класс А - независимы друг от друга, все связываются через Shaper
+ изначальный код класса А ничего не знает про новый функционал, связанный с классами Shape. Если заглушки вынести в отдельный класс, то код класса А вообще не меняется. Так ж класс Shaper ничего не знает про класс А.

В общем случае такой подход не применим, но в моём варианте отлично подходит.

+ Ну и вот простенький примерчик, для наглядности
class Shape1
{
         int m_iData;
public:
         void Draw() { printf("S1> \n"); }
};

class Shape2
{
         int m_iData;
public:
         void Draw() { printf("S2> \n"); }
};

class ShapeStubs
{
public:
         void DrawAll(){};
         void AddShape(){};
};
 
template <typename P, typename S> class CStaticPoly : public P
{
         std::vector<S> m_vS;
public:
         using P::DrawAll;
         void DrawAll();
         using P::AddShape;
         void AddShape(S&& s);
};

template <typename P, typename S> void CStaticPoly<P, S>::DrawAll()
{
         for (auto s : m_vS)
                 s.Draw();
         P::DrawAll();
}

template <typename P, typename S> void CStaticPoly<P, S>::AddShape(S&& s)
{
         m_vS.emplace_back(s);
}

int _tmain(int argc, _TCHAR* argv[])
{
         CStaticPoly<CStaticPoly<ShapeStubs, Shape1>, Shape2> c;
         c.AddShape(Shape1());
         c.AddShape(Shape2());
         c.AddShape(Shape1());
         c.AddShape(Shape2());
         c.DrawAll();

         return 0;
}


Мне было бы так же интересно, как можно было бы быстро решить эту задачу классическим методом. Но только не добавляя в классы Shape информацию о типе! :)
2 фев 17, 11:54    [20172805]     Ответить | Цитировать Сообщить модератору
 Re: Пятничная шабонная магия  [new]
CEMb
Member

Откуда: public T{};
Сообщений: 1958
...
- а я написал игру, используя шаблоны
- ну и как игра?
- победил на этапе компиляции...


---
Возникла такая ситауация: у меня есть шаблон, который принимает на вход шаблонные-шаблонные параметры. В процессе реализации мне захотелось абстрагировать один фундаментальный тип (это был float) на что-то общее, потому что тип иногда даёт ошибки вычисления, да и вообще есть задачи, которые должны решаться снаружи этого типа (функции, константы). Поэтому решено было заменить тип классом, шаблонным, само собой, но это не важно. Важно то, что тип (float) плотно использовался в одном из шаблонных-шаблонных параметров А. А это значит, что в шаблон этого шаблонного-шаблонного параметра А нужно будет добавить ещё один typename и то же самое учесть во всех зависимых от него шаблонах, а это большая переделка.

для начала я вынес всю работу с float в отдельный шаблон:
template <typename B> class A {};
template <typename B, typename M> class A1 : public A<B> {};

в таком виде A1 непригоден для использования, так как по сигнатуре не подходит под А

поэтому непосредственно перед использованием-объявлением я написал строчку:
template <typename B> class A2 : public A1<B, Metric<float>>
using BarType = Bar <... ,A2, ...>;

так вот вопрос: можно ли как-то обойтись без А2? Как-то в идеале примерно так:
using BarType = Bar <... ,A1<Metric<float>>, ...>;

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

Сообщение было отредактировано: 7 фев 17, 13:07
7 фев 17, 12:55    [20188416]     Ответить | Цитировать Сообщить модератору
 Re: Пятничная шабонная магия  [new]
CEMb
Member

Откуда: public T{};
Сообщений: 1958
CEMb
Статическая альтернатива polymorphic hell
Всё можно сделать в разы проще:
template <typename... Ts> class CStaticPoly;
template <> class CStaticPoly<>
{
public:
         void Add(...) {}
};

template <typename T1, typename... Ts> class CStaticPoly<T1, Ts...> : public CStaticPoly<Ts...>
{
         std::vector<T1> m_v;
public:
         using CStaticPoly<Ts...>::Add;
         void Add(T1&& t1) { m_v.emplace_back(t1); }
         void Draw() {}
};
//... in far-far galaxy:
CStaticPoly <Shape1, Shape2> c;
c.Add(Shape1());
c.Add(Shape2());
почему никто не сказал?
2 мар 17, 08:32    [20256178]     Ответить | Цитировать Сообщить модератору
 Re: Пятничная шабонная магия  [new]
Anatoly Moskovsky
Member

Откуда: Odessa
Сообщений: 6386
CEMb
почему никто не сказал?

Потому что никто не читает и тем более не улучшает абстрактный код делающий непонятно что
2 мар 17, 13:54    [20257497]     Ответить | Цитировать Сообщить модератору
 Re: Пятничная шабонная магия  [new]
CEMb
Member

Откуда: public T{};
Сообщений: 1958
Anatoly Moskovsky
Потому что никто не читает и тем более не улучшает абстрактный код делающий непонятно что
Тема ветки - про изучение шаблонов, я хочу сильно преуспеть на этом поприще. Далее, я два раза придумывал тематические объяснения для структур данных, чтобы было удобнее понимать. И я как раз упомянул, что описание - чтобы было удобно читать не про абстракции.
Последние два поста про формирование блока объектов внутри класса. Никто же не жалуется, что vector<T> - ни о чём? Или паттерны программирования. Тут та же ситуация, я пытаюсь на шаблонах сделать иерархию классов, цель указана три поста назад: 20172805, но и то, у меня там в качестве примера приведена ситуация собирания и рисования двух классов объектов.
2 мар 17, 20:10    [20258722]     Ответить | Цитировать Сообщить модератору
Топик располагается на нескольких страницах: [1] 2 3 4 5 6   вперед  Ctrl      все
Все форумы / C++ Ответить