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

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

Страниц: [1] 2 3   Вниз
  Печать  
Автор Тема: Делегат на шаблонах  (Прочитано 17130 раз)
alexis031182
Гость
« : Июль 01, 2012, 22:15 »

Доброго дня.

Имеется делегат на шаблонах. Своим функционалом напоминает концепцию сигнал/слота.

Файл awebdelegatearguments.h
Код
C++ (Qt)
class AWebNullArgument {};
 
class AWebAbstractArguments
{
public:
   //! Деструктор.
   virtual ~AWebAbstractArguments() {}
 
};
 
template<class Arg1 = AWebNullArgument, class Arg2 = AWebNullArgument>
class AWebDelegateArguments : public AWebAbstractArguments
{
public:
   Arg1 _arg1; Arg2 _arg2;
 
   //! Конструктор.
   AWebDelegateArguments() {}
 
   //! Конструктор.
   AWebDelegateArguments(Arg1 arg1) : _arg1(arg1) {}
 
   //! Конструктор.
   AWebDelegateArguments(Arg1 arg1, Arg2 arg2) : _arg1(arg1), _arg2(arg2) {}
 
   //! Деструктор.
   virtual ~AWebDelegateArguments() {}
 
};
 

Файл awebdelegatecontainer.h
Код
C++ (Qt)
#include "awebdelegatearguments.h"
 
class AWebAbstractContainer
{
public:
   //! Деструктор.
   virtual ~AWebAbstractContainer() {}
 
   //! Функция вызова метода объекта.
   virtual void run(AWebAbstractArguments*) = 0;
 
};
 
template<class Class, class Method>
class AWebDelegateContainer : public AWebAbstractContainer
{
public:
   //! Деструктор.
   virtual ~AWebDelegateContainer() {}
 
};
 
template<class Class>
class AWebDelegateContainer<Class, void (Class::*)(void)>
   : public AWebAbstractContainer
{
typedef void (Class::*Method)(void);
 
public:
   //! Конструктор.
   AWebDelegateContainer(Class *c, Method m) : _class(c), _method(m) {}
 
   //! Деструктор.
   virtual ~AWebDelegateContainer() {}
 
   //! Функция вызова метода объекта.
   void run(AWebAbstractArguments*) {(_class->*_method)();}
 
private:
   Class *_class;
   Method _method;
 
};
 
template<class Class, class Arg1>
class AWebDelegateContainer<Class, void (Class::*)(Arg1)>
   : public AWebAbstractContainer
{
typedef void (Class::*Method)(Arg1);
typedef AWebDelegateArguments<Arg1> Args;
 
public:
   //! Конструктор.
   AWebDelegateContainer(Class *c, Method m) : _class(c), _method(m) {}
 
   //! Деструктор.
   virtual ~AWebDelegateContainer() {}
 
   //! Функция вызова метода объекта.
   void run(AWebAbstractArguments *args_i) {
       Args *args = dynamic_cast<Args*>(args_i);
       if(args) (_class->*_method)(args->_arg1);
   }
 
private:
   Class *_class;
   Method _method;
 
};
 
template<class Class, class Arg1, class Arg2>
class AWebDelegateContainer<Class, void (Class::*)(Arg1, Arg2)>
   : public AWebAbstractContainer
{
typedef void (Class::*Method)(Arg1, Arg2);
typedef AWebDelegateArguments<Arg1, Arg2> Args;
 
public:
   //! Конструктор.
   AWebDelegateContainer(Class *c, Method m) : _class(c), _method(m) {}
 
   //! Деструктор.
   virtual ~AWebDelegateContainer() {}
 
   //! Функция вызова метода объекта.
   void run(AWebAbstractArguments *args_i) {
       Args *args = dynamic_cast<Args*>(args_i);
       if(args) (_class->*_method)(args->_arg1, args->_arg2);
   }
 
private:
   Class *_class;
   Method _method;
 
};
 

Файл awebdelegate.h
Код
C++ (Qt)
#include "awebdelegatecontainer.h"
 
class AWebDelegate
{
public:
   //! Конструктор.
   AWebDelegate() : _container(NULL) {}
 
   //! Деструктор.
   virtual ~AWebDelegate() {if(_container) delete _container;}
 
   //! Функция подключения метода объекта.
   template<class Class, class Method>
   void connect(Class *c, Method m) {
       if(_container) delete _container;
       _container = new AWebDelegateContainer<Class, Method>(c, m);
   }
 
   //! Оператор вызова.
   void operator()() {
       AWebDelegateArguments<> args;
       _container->run(&args);
   }
 
   //! Оператор вызова.
   template<class Arg1>
   void operator()(Arg1 arg1) {
       AWebDelegateArguments<Arg1> args(arg1);
       _container->run(&args);
   }
 
   //! Оператор вызова.
   template<class Arg1, class Arg2>
   void operator()(Arg1 arg1, Arg2 arg2) {
       AWebDelegateArguments<Arg1, Arg2> args(arg1, arg2);
       _container->run(&args);
   }
 
private:
   AWebAbstractContainer *_container;
 
};
 

Использование сводится к следующему:
Код
C++ (Qt)
#include "awebdelegate.h"
 
class Base {
public:
   void test1() {qDebug() << "test1()";}
   void test2(int a) {qDebug() << QString("test2(%1)").arg(a);}
   void test3(const QString &b) {qDebug() << QString("test3(%1)").arg(b);}
 
   int problemFunc() {qDebug() << "We are the champions!!!";}
 
};
 
int main(int argc, char *argv[])
{
Base b;
 
AWebDelegate delegate;
 
//OK
delegate.connect(&b, &Base::test1);
delegate();
 
//OK
delegate.connect(&b, &Base::test2);
delegate(10);
 
//OK
delegate.connect(&b, &Base::test3);
delegate("tra-ta-ta");
 
//Fail
delegate.connect(&b, &Base::problemFunc);
delegate();
 
return 0;
}
 

Всё очень удобно. Единственная проблема, которую пока никак не могу победить, это тип возвращаемого значения функции. Необходима возможность указывать любой тип для любого метода класса. Пробовал разные манёвры, но к сожалению пока не слишком успешно. Во вложении исходный код. Прошу помочь. Спасибо.
« Последнее редактирование: Июль 06, 2012, 12:15 от alexis031182 » Записан
DmitryM
Гость
« Ответ #1 : Июль 02, 2012, 07:43 »

для размышления
Код
C++ (Qt)
template<typename RetType>
class Foo
{
public:
 
RetType run()
{
std::cout<<"some type\n";
return RetType();
}
};
 
template<>
int Foo<int>::run()
{
std::cout<<"ret int\n";
return 1;
}
template<>
void Foo<void>::run()
{
std::cout<<"void\n";
}
 
int main()
{
{
Foo<int> foo;
foo.run();
}
 
{
Foo<double> foo;
foo.run();
}
 
{
Foo<void> foo;
foo.run();
}
}
 
 
Записан
alexis031182
Гость
« Ответ #2 : Июль 02, 2012, 11:25 »

Да, я рассматривал этот вариант, но разве что без специализации шаблона возвращаемого значения. Есть ли возможность решить эту задачу без такой специализации? Впрочем, и со специализацией тоже не всё гладко.

Если глянуть в листинге файла awebdelegatecontainer.h на любой из вариантов специализации шаблона AWebDelegateContainer, то можно увидеть, что те наследуются от класса AWebAbstractContainer, имеющего чисто виртуальную функцию run():
Код
C++ (Qt)
virtual void run(AWebAbstractArguments*) = 0;
Поскольку в С нет возможности перегрузки функций по возвращаемому значению, то данное значение должно быть задано в любом случае. Конечно сразу же приходит на ум воспользоваться шаблоном для возвращаемого значения. И это получается, но не далее, нежели выполнение кода возвратится к классу AWebDelegate. А в нём присутствует перегруженный оператор вызова, который как раз и требует специализации для возвращаемого значения. Другими словами, написать вот так нельзя:
Код
C++ (Qt)
template<class Result>
Result operator()() {
  AWebDelegateArguments<> args;
  return _container->run(&args);
}
Необходима специализация, которая в данном случае лишает весь код гибкости (все варианты не предусмотришь же). Безусловно, можно оператор вызова заменить обычной функцией, в которой специализацию указывать не придётся, но мне не нравится, что придётся тогда писать тип возвращаемого значения, например:
Код
C++ (Qt)
delegate.connect(&b, &Base::problemFunc);
int a = delegate.run<int>();
Тип возвращаемого значения известен уже при вызове функции connect(), и именно туда, по идее, нужно его вставлять, например:
Код
C++ (Qt)
delegate.connect<int>(&b, &Base::problemFunc);
int a = delegate();
Тогда получится вполне лаконично. Вот как именно этого добиться, сообразить не получается.
Записан
Igors
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 11445


Просмотр профиля
« Ответ #3 : Июль 02, 2012, 11:47 »

но мне не нравится, что придётся тогда писать тип возвращаемого значения, например:
Код
C++ (Qt)
delegate.connect(&b, &Base::problemFunc);
int a = delegate.run<int>();
По-моему вполне приемлемый вариант. Альтернативно можно вернуть указатель нужного типа из connect

Поскольку в С нет возможности перегрузки функций по возвращаемому значению,
Не совсем, напр такое возможно
Код
C++ (Qt)
// ClassA
virtual ClassA & run( void );
 
// ClassB
virtual ClassB & run( void );
 
Это свинство проходит (конечно A и B должны иметь общую базу)

[/offtop]Когда "несет" вот в такие конструкции - есть смысл прикинуть: а вот этот код попадет в руки человека который, может быть, и неплохой программист, но особо не силен в template. Сколько ему времени потребуется чтобы разобраться? Ну конечно если кто-то другой оплатит это время - то хорошо Улыбающийся Все-таки понятный код - большое преимущество
Записан
alexis031182
Гость
« Ответ #4 : Июль 02, 2012, 12:07 »

По-моему вполне приемлемый вариант. Альтернативно можно вернуть указатель нужного типа из connect
Это в общем-то противоречит основному предназначению делегата. Ну или тому, как я собираюсь его использовать. Если собирать делегаты в список на отложенное выполнение, то я вынужден буду сохранять ещё и типы возвращаемых значений для каждого из них. Это не приемлемо.

Поскольку в С нет возможности перегрузки функций по возвращаемому значению,
Не совсем, напр такое возможно
Код
C++ (Qt)
// ClassA
virtual ClassA & run( void );
 
// ClassB
virtual ClassB & run( void );
 
Это свинство проходит (конечно A и B должны иметь общую базу)
Не знал, спасибо.

Когда "несет" вот в такие конструкции - есть смысл прикинуть: а вот этот код попадет в руки человека который, может быть, и неплохой программист, но особо не силен в template. Сколько ему времени потребуется чтобы разобраться? Ну конечно если кто-то другой оплатит это время - то хорошо Улыбающийся Все-таки понятный код - большое преимущество
Эм... Конкретно с этим кодом не больше часа при наличии Интернета, не больше получаса при наличии подробной документации. Ещё меньше, при наличии рядом того, кто в этом разбирается. Я ведь тоже недавно занимаюсь шаблонами. Кроме Интернета подсказок нет. Не вижу здесь ничего сложнее использования тех же виртуальных функций. Ну добавилась абстракция T, означающая некоторый класс, но больше ничего такого нового.
Записан
DmitryM
Гость
« Ответ #5 : Июль 02, 2012, 14:23 »

Необходима специализация, которая в данном случае лишает весь код гибкости (все варианты не предусмотришь же).
Пример был к размышлению, составлялся с утра перед уходом на работу Улыбающийся
Всех вариантов предусматривать не надо, нужна спецификация лишь для void, когда ничего не возвращаем.
« Последнее редактирование: Июль 02, 2012, 14:29 от DmitryM » Записан
Igors
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 11445


Просмотр профиля
« Ответ #6 : Июль 02, 2012, 14:35 »

Мне кажется что выкладывая свое решение рассчитывать на плодотворное обсуждение трудно. Ход мыслей примерно такой; "оно мне надо вникать что он там накрутил?". "Ну сделал, ну и хорошо". Ну и конечно классическое "ишь какой умный" Улыбающийся. Кстати вникнуть не так уж легко как (возможно) Вам кажется. Опять-таки, учтите что разные люди используют разные стороны Qt, не все плотно работают с делегатами/депутатами.

Нехорошо лишать людей возможности "блеснуть", "показать себя".  В следующий раз обязательно зашлангуйтесь, покажите только простейшие кусочки кода и изобразите искреннее удивление что их приходится писать снова и снова - только потому что др. класс! И не высовывайтесь со своей реализацией, она никуда не убежит, посмотрите что другие предложат.
Записан
DmitryM
Гость
« Ответ #7 : Июль 02, 2012, 14:43 »

Опять-таки, учтите что разные люди используют разные стороны Qt, не все плотно работают с делегатами/депутатами.
Знание владение Qt != Знание C++ Подмигивающий
Записан
alexis031182
Гость
« Ответ #8 : Июль 02, 2012, 14:46 »

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

Всех вариантов предусматривать не надо, нужна спецификация лишь для void, когда ничего не возвращаем.
С void'ом всё путём, вроде.
Записан
alexis031182
Гость
« Ответ #9 : Июль 02, 2012, 15:01 »

Мне кажется что выкладывая свое решение рассчитывать на плодотворное обсуждение трудно. Ход мыслей примерно такой; "оно мне надо вникать что он там накрутил?". "Ну сделал, ну и хорошо". Ну и конечно классическое "ишь какой умный" Улыбающийся.
Да, конечно, код рассчитан на некоторый опыт работы с шаблонами. Если ранее они не изучались, то мотивации на обсуждение затронутой проблемы не будет. Согласен.

Кстати вникнуть не так уж легко как (возможно) Вам кажется. Опять-таки, учтите что разные люди используют разные стороны Qt, не все плотно работают с делегатами/депутатами.
Делегат по своей сути - тот же абстрактный класс (интерфейс). Единственное отличие в том, что интерфейс подразумевает наследование и, соответственно, обязательную перегрузку в наследнике методов, объявленных в интерфейсе. Делегат лишён этого э-э-э... недостатка (оно конечно и достоинство, но не в моём случае).

Нехорошо лишать людей возможности "блеснуть", "показать себя". В следующий раз обязательно зашлангуйтесь, покажите только простейшие кусочки кода и изобразите искреннее удивление что их приходится писать снова и снова - только потому что др. класс! И не высовывайтесь со своей реализацией, она никуда не убежит, посмотрите что другие предложат.
Здесь я даже не знаю, что и сказать. Мне нужно решить вполне конкретную задачу, со вполне определёнными условиями. Если бы я ставил себе цель поиграться, то наверное так бы и поступил.

Этот код я даже не знаю как можно привести маленькими кусочками. Он имеет смысл, когда целостный. Это в общем-то чужие идеи, просто я их скомпоновал вот в такую реализацию. В частности шаблонизацию аргументов функций взял из loki (это библиотека Андрея Александреску, вроде; книги его пока не читал). По функторам нашёл описания в Интернете. Описание специализаций шаблонов - там же.
Записан
Igors
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 11445


Просмотр профиля
« Ответ #10 : Июль 02, 2012, 15:18 »

Здесь я даже не знаю, что и сказать. Мне нужно решить вполне конкретную задачу, со вполне определёнными условиями. Если бы я ставил себе цель поиграться, то наверное так бы и поступил.
На мой взгляд, получается "с точностью наоборот" Улыбающийся Прочитав все Ваши посты в этом топике минимум дважды, я (мягко говоря) "не имею точного представления" о том какую же задачу Вы хотите решить. Вы сразу же сваливаетесь в подробности реализации - но непонятно "чего". Наверное мое абстрактное мЫшление слабовато Улыбающийся. Умолкаю
Записан
alexis031182
Гость
« Ответ #11 : Июль 02, 2012, 15:31 »

Всё же наверное можно выделить одну из проблем. Приведу пример:
Код
C++ (Qt)
class Base
{
public:
  virtual operator int() {return 1;}
};
 
class Child : public Base
{
public:    
  virtual operator int() {return 2;}
};
 
Child child;
Base &base = child;
int i = base; //переменная i будет содержать значение "2"
 
Похожее мне надо повторить на шаблонах.
Код
C++ (Qt)
class AWebAbstractResult
{
public:
  virtual ~AWebAbstractResult() {}
 
  //Как объявить operator, исходя из данных нижеследующего класса.
  //virtual operator ???() = 0;
};
 
template<class Result>
class AWebDelegateResult : public AWebAbstractResult
{
public:
  AWebDelegateResult(Result data) : _data(data) {}
  virtual ~AWebDelegateResult() {}
 
  operator Result() {return _data;}
 
private:
   Result _data;
};
 
Замечание: класс AWebAbstractResult шаблонным делать нельзя, только методы; в коде приложения будет создаваться AWebDelegateResult<Result>, но использоваться его базовый класс - AWebAbstractResult.
Записан
alexis031182
Гость
« Ответ #12 : Июль 02, 2012, 15:40 »

На мой взгляд, получается "с точностью наоборот" Улыбающийся Прочитав все Ваши посты в этом топике минимум дважды, я (мягко говоря) "не имею точного представления" о том какую же задачу Вы хотите решить. Вы сразу же сваливаетесь в подробности реализации - но непонятно "чего". Наверное мое абстрактное мЫшление слабовато Улыбающийся. Умолкаю
Эм... не понял. То Вы говорите, мол, абстрагируйся и в деталях, кусочками кода распиши подзадачи, то теперь указываете на отсутствие видения всей задачи. Будьте последовательны. Да и вроде как я описал, что мне нужен делегат и именно на шаблонах. Что такое делегат я также расписал.
Записан
DmitryM
Гость
« Ответ #13 : Июль 02, 2012, 16:09 »

Похожее мне надо повторить на шаблонах.
Код
C++ (Qt)
#include <iostream>
#include <functional>
 
using namespace std;
 
class Base
{
public:
  virtual int operator()() {return 1;}
};
 
class Child : public Base
{
public:    
  virtual int operator()() {return 2;}
};
 
int main()
{
   function<int(Base&)> f_int = &Base::operator();
 
   Child ch;
   cout<<f_int(ch)<<std::endl;
 
   Base bs;
   cout<<f_int(bs)<<std::endl;
   return 0;
}
 
Насколько похоже?
Записан
alexis031182
Гость
« Ответ #14 : Июль 02, 2012, 16:38 »

Интересное решение, но проблема в том, что в базовом классе возвращаемый тип не известен.
Код
C++ (Qt)
class Base
{
public:
  //Как объявить operator, исходя из данных нижеследующего класса.
  //virtual operator ???() = 0;
};
 
template<class Result>
class Child : public Base
{
public:
  Child(Result data) : _data(data) {}
  operator Result() {return _data;}
 
private:
   Result _data;
};
 
Вызов дочернего "оператора" будет исключительно через базовый класс. Но как его объявить? Складывается впечатление, что в обозначенных условиях - никак. Может быть через какой-нибудь дополнительный класс... Пока не выходит.
Записан
Страниц: [1] 2 3   Вверх
  Печать  
 
Перейти в:  


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