Russian Qt Forum
Апрель 30, 2024, 14:17 *
Добро пожаловать, Гость. Пожалуйста, войдите или зарегистрируйтесь.
Вам не пришло письмо с кодом активации?

Войти
 
  Начало   Форум  WIKI (Вики)FAQ Помощь Поиск Войти Регистрация  

Страниц: [1] 2 3 4   Вниз
  Печать  
Автор Тема: Специализация шаблона для базового класса  (Прочитано 22827 раз)
Racheengel
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 2679


Я работал с дискетам 5.25 :(


Просмотр профиля
« : Сентябрь 18, 2016, 13:02 »

Всем привет,

понадобилась довольно простая вещь.
Скажем, есть класс A, и есть шаблон типа

Код:
template<class A, class X>
class T
...

со временем выяснилось, что некоторые наследники от класса A имеют некоторые различные методы.
Например, что то типа этого:

Код:
class B: public A
{
...
void methodB();
};

class C: public A
{
...
void methodC1();
virtual void methodC2();
};

class D: public C
{
...
virtual void methodC2();
};

В шаблоне есть несколько мест, где нужно, в зависимости от типа A, вызывать тот или иной метод наследника.
Типа такого:

Код:
void T<A,X>::doStuff()
{
// if A is B:
this->methodB();
return;

// else if A is D:
this->methodC2();   // тут надо вызвать метод из класса D...
return;

// else if A is C:
this->methodC2();   // тут надо вызвать метод из класса C...
}

Проблему можно решить через runtime с помощью dynamic_cast, и это будет работать 100%.
Вопрос, можно ли каким-то образом это решить через специализацию шаблона?
Или современный C++ все еще далек от элементарных вещей?

Пробовал через

Код:
template<class X>
void T<D,X>::doStuff()
{
this->methodC2();
}

что выглядит логичным с точки зрения разработчика (специализируем первый параметр шаблона классом D), но ни одна подобная конструкция не компилиццо Грустный
« Последнее редактирование: Сентябрь 18, 2016, 13:06 от Racheengel » Записан

What is the 11 in the C++11? It’s the number of feet they glued to C++ trying to obtain a better octopus.

COVID не волк, в лес не уйдёт
m_ax
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 2094



Просмотр профиля
« Ответ #1 : Сентябрь 18, 2016, 13:52 »

Цитировать
Вопрос, можно ли каким-то образом это решить через специализацию шаблона?
Можно. И чтоб весь класс T не специализировать, достаточно завести хелпер, который будет возвращать нужный метод:

Код
C++ (Qt)
struct A
{
   void print() { cout << "A" << endl; }
};
 
struct B : public A
{
   void printB() { cout << "B" << endl; }
};
 
struct C : public A
{
   void printC() { cout << "C" << endl; }
};
 
 
template <class>
struct helper;
 
template<>
struct helper<A>
{
   static void method(A & obj) { obj.print(); }
};
 
template<>
struct helper<B>
{
   static void method(B & obj) { obj.printB(); }
};
 
template<>
struct helper<C>
{
   static void method(C & obj) { obj.printC(); }
};
 
 
template <class _A>
class T
{
public:
   void doStuff()
   {
       helper<_A>::method(m_obj);
   }
 
private:
   _A m_obj;
};
 
int main()
{
   T<A> objA;
   T<B> objB;
   T<C> objC;
 
   objA.doStuff();
   objB.doStuff();
   objC.doStuff();
 
   return 0;
}
 
Записан

Над водой луна двурога. Сяду выпью за Ван Гога. Хорошо, что кот не пьет, Он и так меня поймет..

Arch Linux Plasma 5
Racheengel
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 2679


Я работал с дискетам 5.25 :(


Просмотр профиля
« Ответ #2 : Сентябрь 18, 2016, 15:27 »

Идею понял, спасибо Улыбающийся
Получается, на КАЖДЫЙ метод КАЖДОГО класса, который будет параметром шаблона, создаем свой враппер-хелпер...

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

Код:
// if this is A:
if (this->methodA() == BlaBla1)
{
  ... handle BlaBla1 case
  m_flags = SomeFlags1;
  ...
}
else
{
  ... handle BlaBla2 case
  if (m_flags & SomeOtherFlags)
    doAnotherStuff();
  else
    m_flags = SomeFlags2;
  ...
}

// if this is C
...
ну и в этом духе
Записан

What is the 11 in the C++11? It’s the number of feet they glued to C++ trying to obtain a better octopus.

COVID не волк, в лес не уйдёт
m_ax
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 2094



Просмотр профиля
« Ответ #3 : Сентябрь 18, 2016, 17:28 »

Цитировать
Получается, на КАЖДЫЙ метод КАЖДОГО класса, который будет параметром шаблона, создаем свой враппер-хелпер...
Не совсем.. Хелпер может содержать ни один метод - их там может быть несколько, в зависимости от задачи. Он просто задаёт интерфейс, а реализация для конкретного класса специализируется. Идея в том, что проще написать легковесные специализации хелпера, чем специализации самого класса T.

Цитировать
Немножко усложним задачку...
Предположим, что вызовов методов типа method...() мало.
Надо в doStuff() обработать результаты их выполнения и в зависимости от этого сделать то или иное действие.

И в чём проблема?
« Последнее редактирование: Сентябрь 18, 2016, 17:33 от m_ax » Записан

Над водой луна двурога. Сяду выпью за Ван Гога. Хорошо, что кот не пьет, Он и так меня поймет..

Arch Linux Plasma 5
Igors
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 11445


Просмотр профиля
« Ответ #4 : Сентябрь 18, 2016, 17:37 »

Немножко усложним задачку...
Предположим, что вызовов методов типа method...() мало.
Надо в doStuff() обработать результаты их выполнения и в зависимости от этого сделать то или иное действие.
Т.е. так:
Если очевидна/налицо "динамика", то зачем делать ее "статикой"? Приведетесь dynamic_cast<>, не облезете. 

И в чём проблема?
Видимо в том что нужны все хелперы в одном тельце, а не только один (специализированный)
Записан
m_ax
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 2094



Просмотр профиля
« Ответ #5 : Сентябрь 18, 2016, 17:51 »

Цитировать
Видимо в том что нужны все хелперы в одном тельце, а не только один (специализированный)
Что значит все хелперы в одном тельце? Какие все?
Записан

Над водой луна двурога. Сяду выпью за Ван Гога. Хорошо, что кот не пьет, Он и так меня поймет..

Arch Linux Plasma 5
Racheengel
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 2679


Я работал с дискетам 5.25 :(


Просмотр профиля
« Ответ #6 : Сентябрь 18, 2016, 18:32 »

Проблема в том, что в doStuff() нужно не только дергать методы в зависимости от типа _A, но и реагировать на их результаты в зависимости от типа _A.
При этом возможен доступ к приватным членам T, что через хелпер не получится (ну, только если их зафрендить, или сделать спец. интерфейс для доступа).
А хотелось бы просто такое:

Код:
void T<A>::doStuff()
{

switch (type of A)
{
case A:
  methodA();
...

case D:
  methodC();
  methodD();
...
}

}

Т.е. такой условный свитч, который "умно" инстанциирует части кода doStuff() в зависимости от того, кто есть A.
В принципе, нечто вроде сишного #ifdef... #endif, но для шаблонов.
« Последнее редактирование: Сентябрь 18, 2016, 18:34 от Racheengel » Записан

What is the 11 in the C++11? It’s the number of feet they glued to C++ trying to obtain a better octopus.

COVID не волк, в лес не уйдёт
Racheengel
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 2679


Я работал с дискетам 5.25 :(


Просмотр профиля
« Ответ #7 : Сентябрь 18, 2016, 18:51 »

Если очевидна/налицо "динамика", то зачем делать ее "статикой"? Приведетесь dynamic_cast<>, не облезете. 

dynamic_cast<> прекрасно работает (пока на нем родимом все и держится).
Но это все же runtime.
А главный плюс шаблонов - это "когда компилятор все разрулит", вроде бы.. Непонимающий
Внутри каждого инстанциированного класса динамика не нужна, т.к все зависит тока от типа A.
Вот решил воспользоваться фичей языка... а похоже, что без гемороюшки тут никак Грустный
Записан

What is the 11 in the C++11? It’s the number of feet they glued to C++ trying to obtain a better octopus.

COVID не волк, в лес не уйдёт
m_ax
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 2094



Просмотр профиля
« Ответ #8 : Сентябрь 18, 2016, 19:35 »

Цитировать
Вот решил воспользоваться фичей языка... а похоже, что без гемороюшки тут никак
Да нет, такой меташаблонный case вполне реализуется) Напишу, выложу)
Записан

Над водой луна двурога. Сяду выпью за Ван Гога. Хорошо, что кот не пьет, Он и так меня поймет..

Arch Linux Plasma 5
m_ax
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 2094



Просмотр профиля
« Ответ #9 : Сентябрь 18, 2016, 20:20 »

Вобщем вот примерный меташаблонный вариант swithа:

Код
C++ (Qt)
#include <iostream>
 
using namespace std;
 
struct A
{
   void print() { cout << "A" << endl; }
};
 
struct B : public A
{
   void printB() { cout << "B" << endl; }
};
 
struct C : public A
{
   void printC() { cout << "C" << endl; }
};
 
 
template <class>
struct helper;
 
template<>
struct helper<A>
{
   static void method(A & obj) { obj.print(); }
};
 
template<>
struct helper<B>
{
   static void method(B & obj) { obj.printB(); }
};
 
template<>
struct helper<C>
{
   static void method(C & obj) { obj.printC(); }
};
 
 
 
template <bool>
struct case_of_helper;
 
 
template <>
struct case_of_helper<true>
{
   template <class F>
   static void doStuff(F f) { f(); }
};
 
template <>
struct case_of_helper<false>
{
   template <class F>
   static void doStuff(F) {}
};
 
 
template <class Base, class Derived, class F>
void meta_case_of(F f)
{
   case_of_helper<std::is_same<Base, Derived>::value>::doStuff(f);
}
 
 
template <class Obj>
class T
{
public:
   void doStuff()
   {
       meta_case_of<A, Obj>([&]()
       {
           helper<Obj>::method(m_obj);
       });
 
       meta_case_of<B, Obj>([&]()
       {
           helper<Obj>::method(m_obj);
       });
 
       meta_case_of<C, Obj>([&]()
       {
           helper<Obj>::method(m_obj);
       });
   }
 
private:
   Obj m_obj;
};
 
 
int main()
{
   T<A> objA;
   T<B> objB;
   T<C> objC;
 
   objA.doStuff();
   objB.doStuff();
   objC.doStuff();
 
   return 0;
}
 
 

Здесь лямбда будет видеть все приватные члены и прочие методы..

P.S. Но к этому варианту нужно относиться только лишь как к некой технической возможности, не более.. Просто как к альтернативе..
Но это, на мой взгляд, плохое решение (хотя я и не знаю, Ваших замыслов и т.д.. ). Плохое в том смысле, что если появится новый наследник от класса A, то помимо специализации хелпера, дополнительно придётся лезть в код метода T<Obj>::doStuff.. Мне это кажется весьма сомнительным..
« Последнее редактирование: Сентябрь 18, 2016, 20:45 от m_ax » Записан

Над водой луна двурога. Сяду выпью за Ван Гога. Хорошо, что кот не пьет, Он и так меня поймет..

Arch Linux Plasma 5
Racheengel
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 2679


Я работал с дискетам 5.25 :(


Просмотр профиля
« Ответ #10 : Сентябрь 18, 2016, 23:18 »

P.S. Но к этому варианту нужно относиться только лишь как к некой технической возможности, не более.. Просто как к альтернативе..
Но это, на мой взгляд, плохое решение (хотя я и не знаю, Ваших замыслов и т.д.. ). Плохое в том смысле, что если появится новый наследник от класса A, то помимо специализации хелпера, дополнительно придётся лезть в код метода T<Obj>::doStuff.. Мне это кажется весьма сомнительным..

Да, боюсь, на практике такая возможность будет несколько... скажем так... неадекватна по затратам по отношению к результатам Грустный
Гуглы и стековерфлоу я уже перекопал, там вообще все глухо.

Получается, что ни одна существующая реализация языка не поддерживает непосредственную инстанциацию фрагментов кода.
Хотя, опять же, как по мне - возможность была бы довольно мощной, а кроме того, не вижу проблем в имплементации для компилятора.
Статически выполнить сравнение типов параметров шаблона при инстанцировании и активировать нужные куски кода - это как то не особо сложно звучит, сишный препроцессор, не более.

В общем, "рантайм наше все", спасибо откликнувшимся Улыбающийся
Записан

What is the 11 in the C++11? It’s the number of feet they glued to C++ trying to obtain a better octopus.

COVID не волк, в лес не уйдёт
Igors
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 11445


Просмотр профиля
« Ответ #11 : Сентябрь 19, 2016, 06:58 »

Получается, что ни одна существующая реализация языка не поддерживает непосредственную инстанциацию фрагментов кода.
Хотя, опять же, как по мне - возможность была бы довольно мощной, а кроме того, не вижу проблем в имплементации для компилятора.
"Запретный плод" всегда кажется "довольно мощным" Улыбающийся Как я понимаю идея этих "хвостиков" (traits) в том что основной класс пишется один раз, а потом, отдельно дописываются хвосты для инстансов по мере их поступления. Так смысл есть (хотя конечно все равно мерзость). Но у Вас др ситуация - Вы хотите "куски кода", т.е. Вы уже согласны править основной класс всякий раз. Тогда чего затевать темплейты?

Кстати интересно, а напр при такой проверке
Код:
if (typeid(some_member) == typeid(some_class))
Не выкинет ли компилятор неиспользуемый код?

Да, боюсь, на практике такая возможность будет несколько... скажем так... неадекватна по затратам по отношению к результатам Грустный
Во-во-во, недавно в др теме (безуспешно) пытался объяснить то же самое
« Последнее редактирование: Сентябрь 19, 2016, 07:03 от Igors » Записан
Old
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 4349



Просмотр профиля
« Ответ #12 : Сентябрь 19, 2016, 08:50 »

понадобилась довольно простая вещь.
Racheengel, а у вас не возникает ощущения, что вы делаете что-то не то. Улыбающийся
Как-то это уж очень топорно получается. Может стоит пересмотреть интерфейс у дерева классов или сделать не один шаблон, а несколько...
Сейчас для абстракной задачи сложно что-то посоветовать, но я бы такое в боевой код не тащил, даже если бы C++ это позволял. Улыбающийся
Записан
__Heaven__
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 2130



Просмотр профиля
« Ответ #13 : Сентябрь 19, 2016, 11:04 »

Быть может есть возможность в классе A написать чистый виртуальный метод doStuff, который исполнит желаемое...
Записан
__Heaven__
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 2130



Просмотр профиля
« Ответ #14 : Сентябрь 19, 2016, 11:14 »

Быть может даже и виртуальность не нужна. Обычная перегрузка doStuff поможет?
Записан
Страниц: [1] 2 3 4   Вверх
  Печать  
 
Перейти в:  


Страница сгенерирована за 0.243 секунд. Запросов: 22.