Russian Qt Forum

Программирование => С/C++ => Тема начата: alexis031182 от Июль 01, 2012, 22:15



Название: Делегат на шаблонах
Отправлено: 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;
}
 

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


Название: Re: Делегат на шаблонах
Отправлено: DmitryM от Июль 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();
}
}
 
 


Название: Re: Делегат на шаблонах
Отправлено: alexis031182 от Июль 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();
Тогда получится вполне лаконично. Вот как именно этого добиться, сообразить не получается.


Название: Re: Делегат на шаблонах
Отправлено: Igors от Июль 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. Сколько ему времени потребуется чтобы разобраться? Ну конечно если кто-то другой оплатит это время - то хорошо :) Все-таки понятный код - большое преимущество


Название: Re: Делегат на шаблонах
Отправлено: alexis031182 от Июль 02, 2012, 12:07
По-моему вполне приемлемый вариант. Альтернативно можно вернуть указатель нужного типа из connect
Это в общем-то противоречит основному предназначению делегата. Ну или тому, как я собираюсь его использовать. Если собирать делегаты в список на отложенное выполнение, то я вынужден буду сохранять ещё и типы возвращаемых значений для каждого из них. Это не приемлемо.

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

Когда "несет" вот в такие конструкции - есть смысл прикинуть: а вот этот код попадет в руки человека который, может быть, и неплохой программист, но особо не силен в template. Сколько ему времени потребуется чтобы разобраться? Ну конечно если кто-то другой оплатит это время - то хорошо :) Все-таки понятный код - большое преимущество
Эм... Конкретно с этим кодом не больше часа при наличии Интернета, не больше получаса при наличии подробной документации. Ещё меньше, при наличии рядом того, кто в этом разбирается. Я ведь тоже недавно занимаюсь шаблонами. Кроме Интернета подсказок нет. Не вижу здесь ничего сложнее использования тех же виртуальных функций. Ну добавилась абстракция T, означающая некоторый класс, но больше ничего такого нового.


Название: Re: Делегат на шаблонах
Отправлено: DmitryM от Июль 02, 2012, 14:23
Необходима специализация, которая в данном случае лишает весь код гибкости (все варианты не предусмотришь же).
Пример был к размышлению, составлялся с утра перед уходом на работу :)
Всех вариантов предусматривать не надо, нужна спецификация лишь для void, когда ничего не возвращаем.


Название: Re: Делегат на шаблонах
Отправлено: Igors от Июль 02, 2012, 14:35
Мне кажется что выкладывая свое решение рассчитывать на плодотворное обсуждение трудно. Ход мыслей примерно такой; "оно мне надо вникать что он там накрутил?". "Ну сделал, ну и хорошо". Ну и конечно классическое "ишь какой умный" :). Кстати вникнуть не так уж легко как (возможно) Вам кажется. Опять-таки, учтите что разные люди используют разные стороны Qt, не все плотно работают с делегатами/депутатами.

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


Название: Re: Делегат на шаблонах
Отправлено: DmitryM от Июль 02, 2012, 14:43
Опять-таки, учтите что разные люди используют разные стороны Qt, не все плотно работают с делегатами/депутатами.
Знание владение Qt != Знание C++ ;)


Название: Re: Делегат на шаблонах
Отправлено: alexis031182 от Июль 02, 2012, 14:46
Пример был к размышлению, составлялся с утра перед уходом на работу :)
Нет, всё нормально и логично использовать указанный Вами вариант, но у меня всё встало из-за виртуальной функции. Ниже сейчас напишу суть проблемы.

Всех вариантов предусматривать не надо, нужна спецификация лишь для void, когда ничего не возвращаем.
С void'ом всё путём, вроде.


Название: Re: Делегат на шаблонах
Отправлено: alexis031182 от Июль 02, 2012, 15:01
Мне кажется что выкладывая свое решение рассчитывать на плодотворное обсуждение трудно. Ход мыслей примерно такой; "оно мне надо вникать что он там накрутил?". "Ну сделал, ну и хорошо". Ну и конечно классическое "ишь какой умный" :).
Да, конечно, код рассчитан на некоторый опыт работы с шаблонами. Если ранее они не изучались, то мотивации на обсуждение затронутой проблемы не будет. Согласен.

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

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

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


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


Название: Re: Делегат на шаблонах
Отправлено: alexis031182 от Июль 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.


Название: Re: Делегат на шаблонах
Отправлено: alexis031182 от Июль 02, 2012, 15:40
На мой взгляд, получается "с точностью наоборот" :) Прочитав все Ваши посты в этом топике минимум дважды, я (мягко говоря) "не имею точного представления" о том какую же задачу Вы хотите решить. Вы сразу же сваливаетесь в подробности реализации - но непонятно "чего". Наверное мое абстрактное мЫшление слабовато :). Умолкаю
Эм... не понял. То Вы говорите, мол, абстрагируйся и в деталях, кусочками кода распиши подзадачи, то теперь указываете на отсутствие видения всей задачи. Будьте последовательны. Да и вроде как я описал, что мне нужен делегат и именно на шаблонах. Что такое делегат я также расписал.


Название: Re: Делегат на шаблонах
Отправлено: DmitryM от Июль 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;
}
 
Насколько похоже?


Название: Re: Делегат на шаблонах
Отправлено: alexis031182 от Июль 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;
};
 
Вызов дочернего "оператора" будет исключительно через базовый класс. Но как его объявить? Складывается впечатление, что в обозначенных условиях - никак. Может быть через какой-нибудь дополнительный класс... Пока не выходит.


Название: Re: Делегат на шаблонах
Отправлено: DmitryM от Июль 02, 2012, 17:39
Интересное решение, но проблема в том, что в базовом классе возвращаемый тип не известен.
Ох, тогда возвращай variant/any (http://insidecpp.ru/patterns/variant/)


Название: Re: Делегат на шаблонах
Отправлено: alexis031182 от Июль 02, 2012, 17:49
Ох, тогда возвращай variant/any (http://insidecpp.ru/patterns/variant/)
Спасибо большое, оно :)


Название: Re: Делегат на шаблонах
Отправлено: alexis031182 от Июль 02, 2012, 19:01
Всё работает. Осталась оптимизация. Шаблон варианта автоматом разрулил все остальные проблемы. Спасибо :)


Название: Re: [РЕШЕНО] Делегат на шаблонах
Отправлено: alexis031182 от Июль 05, 2012, 16:27
Всё же с радостью я несколько поторопился. Во-первых, решение с вариантом не дало возможности возвращать void, который в отличие от любых других типов не имеет значения. Пришлось выдумывать специализацию шаблонов, что в сущности свелось к реализации практически идентичных классов. И ладно бы одного, а то ведь по цепочке. Работает конечно, но не удобно. Во-вторых, вариант становится промежуточным контейнером для содержания возвращённого функтором результата. Это всё чревато, и, думаю, понятно чем.

С горя полез в Loki Александреску. Документации по его библиотеке практически никакой нет. Книги его в свободном доступе найти не удалось. Разбирался с исходниками, точнее с той их частью, что напрямую касалась интересующей меня темы.

Шаблоны сами по себе вещь не сложная. Зачастую программеры ограничиваются использованием их лишь в роли дополнительной абстракции, позволяющей указать в compile-time некие типы неких объектов. Вот класс делегата, опубликованный в начале темы, как раз из этого разряда. Единственное, что там может смутить начинающего изучать шаблоны - это их специализация. Впрочем, разобраться при желании с этим вопросом совсем не трудно.

В Loki всё значительно веселее. Метапрограммирование на шаблонах - истинно то, что позволяет вынести мозг буквально с первых минут изучения. Тем не менее, когда начинаешь въезжать, мощь производит впечатление. Следует также иметь ввиду один очень важный момент: этот код выполняется в compile-time, а значит какое бы другое run-time решение, пусть даже самое супер оптимизированное, не было бы представлено взамен, оно всё равно будет выполняться медленнее. И вполне вероятно, что значительно медленнее. Вот такая вот "магия".

Публикую второй вариант делегата на шаблонах. Здесь всё работает как положено, как собственно мне и хотелось. Проверка типов, само собой, осуществляется ещё на этапе компиляции, поэтому подсунуть что-то левое будет довольно сложно. Естественно код содран с Loki (лицензия позволяет). Впрочем можно было вообще ничего не "сдирать", а использовать "as is", но мне хотелось разобраться, вникнуть, так сказать, в самое оно. Результатом стала "лёгкая" переработка имеющихся в составе Loki шаблонов. Конечно же с сохранением концепции.

Код нового делегата по размеру получился значительно большим. Но лично меня это нисколько не смущает, поскольку всё это обилие классов и структур шаблонов, как я уже указывал выше, озаботит лишь компилятор на соответствующей стадии, не более.

Использовать новый делегат также просто как и предыдущий. Впрочем, можно подумать о специализации некоторых отдельных параметров, дабы лишний раз не указывать void при отсутствии возвращаемого значения и/или обозначать отсутствие аргументов функции. Но всё это уже не столь существенно и может быть легко настроено самостоятельно.
Код
C++ (Qt)
#include "adelegate.h"
 
class A {
public:
   void echo() {qDebug() << "A::echo()";}
};
 
class Base {
public:
   void foo() {qDebug() << "foo()";}
   void bar(int a) {qDebug() << QString("bar(%1)").arg(a);}
 
   A *test(QString a, int b) {qDebug() << QString("test(%1)").arg(a) << b; return new A;}
 
};
 
int main(int argc, char *argv[])
{
Base b;
 
typedef void(Base::*foo)();
ADelegate<Base*, foo> delegate(&b, &Base::foo);
delegate();
 
typedef void(Base::*bar)(int);
ADelegate<Base*, bar, void, ALoki::TypeSequence<int> > delegate1(&b, &Base::bar);
delegate1(2);
 
typedef A*(Base::*test)(QString, int);
ADelegate<Base*, test, A*, ALoki::TypeSequence<QString, int> > delegate2(&b, &Base::test);
A *r = delegate2(QString("tratata"), 10);
r->echo();
delete r;
 
return 0;
}
 


Название: Re: [РЕШЕНО] Делегат на шаблонах
Отправлено: DmitryM от Июль 05, 2012, 16:44
Цитировать
что в базовом классе возвращаемый тип не известен.
И как ты решил свою проблему?  :)


Название: Re: [РЕШЕНО] Делегат на шаблонах
Отправлено: alexis031182 от Июль 05, 2012, 17:17
И как ты решил свою проблему?  :)
Обыграно на более сложной иерархии, нежели чем в предыдущем варианте. Возвращаемый тип передаётся в AbstractFunctorObject (файл alokiabstractfunctor.h). Это по сути базовый класс-шаблон-интерфейс для всех функторов. От этого класса наследуется публичный шаблон-интерфейс функтора, который в свою очередь имеет собственные специализации. Вся эта катавасия даёт возможность не использовать один единственный класс-интерфейс с предопределённой виртуальной функцией, как это было сделано в предыдущем варианте.


Название: Re: [РЕШЕНО] Делегат на шаблонах
Отправлено: m_ax от Июль 06, 2012, 08:59
Это всё можно проще сделать.. Накатаю пример, как приду домой..


Название: Re: [РЕШЕНО] Делегат на шаблонах
Отправлено: alexis031182 от Июль 06, 2012, 12:14
Для полного счастья ещё не хватает возможности вызова функции, не являющейся методом какого-либо класса.


Название: Re: Делегат на шаблонах
Отправлено: alexis031182 от Июль 06, 2012, 15:00
Ну и наконец третий вариант. Наверное последний, если не будет предложено решения лучше. Тогда к фабрике можно приступать на основе этого делегата.
Код
C++ (Qt)
#include "alokifunctor.h"
 
void a() {
   qDebug() << "a()";
}
 
class Base {
public:
   const QString test(const QString &txt) {return txt;}
};
 
int main(int, char**)
{
ALoki::Functor<> functor(&a);
functor();
 
Base obj;
typedef const QString(Base::*test)(const QString&);
ALoki::Functor<const QString, ALoki::TypeSequence<const QString&> > functor1(&obj, &Base::test);
qDebug() << functor1("tratata");
 
return 0;
}
 
Третий вариант отличается от второго прежде всего структурой. Помимо прочего, добавлено два класса для "разруливания" указателя на функцию и указателя на метод класса.


Название: Re: Делегат на шаблонах
Отправлено: m_ax от Июль 06, 2012, 21:20
Можно лучше. Например, используя библиотеку libssc (я использовал версию libssc-cpp11-1.0.3 http://www.prog.org.ru/topic_16829_90.html (http://www.prog.org.ru/topic_16829_90.html))

Тогда это будет выглядеть так:
Код
C++ (Qt)
#include <iostream>
#include <string>
#include "signal_slot.h"
 
void a() {
   std::cout << "a()" << std::endl;
}
 
class Base : public ssc::trackable {
public:
   void test1() {std::cout << "test1()";}
   void test2(int a) {std::cout << a << std::endl;}
   void test3(const std::string &b) {std::cout << b << std::endl;}
 
   int problemFunc() {
       std::cout << "We are the champions!!!" << std::endl;
       return 0;
   }
 
};
 
int main()
{
   ssc::signal<void> sig0;
   sig0.connect(a);
   sig0();
 
   Base base;
 
   ssc::signal<int> sig_int;
   sig_int.connect(&base, &Base::test2);
   sig_int(10);
 
   ssc::signal<void> sig_problemFunc;
   sig_problemFunc.connect(&base, &Base::problemFunc);
   sig_problemFunc();
 
 
   return 0;
}
 

Проект с библиотекой приаттачен.

А если подумать ещё, то можно и вовсе сделать так, как задумывалось в первоначальном варианте.
Немного почистить исходники libssc и этот делегат готов)



Название: Re: Делегат на шаблонах
Отправлено: alexis031182 от Июль 06, 2012, 22:17
Можно лучше. Например, используя библиотеку libssc (я использовал версию libssc-cpp11-1.0.3 http://www.prog.org.ru/topic_16829_90.html (http://www.prog.org.ru/topic_16829_90.html))
...
Спасибо за приведённый код, но не могли бы Вы растолковать в деталях, чем это решение лучше. В коде я вижу необходимость в наследовании класса, к которому создаётся делегат (может быть это какой-то служебный класс, тогда напишите об этом). Это далеко не всегда хорошо. И не очень понятна ситуация с возвращаемым значением.

...
А если подумать ещё, то можно и вовсе сделать так, как задумывалось в первоначальном варианте.
Немного почистить исходники libssc и этот делегат готов)
Мой первоначальный вариант меня не устраивал. В итоге темы получился третий, который делает всё что необходимо:
- независимый от какого-либо наследования функтор;
- выполнение любых функций/методов с любым набором аргументов;
- возврат любого значения без необходимости явного указания типа этого значения на этапе вызова делегированной функции;
- ну и конечно код должен формироваться не в run-time.

Если указанная Вами библиотека всё это предоставляет, то укажите, пожалуйста, дополнительные её возможности, не рассмотренные в этом списке. Спасибо.

Update: и Вы, похоже, в аттач только мой вариант делегата запостили


Название: Re: Делегат на шаблонах
Отправлено: DmitryM от Июль 07, 2012, 00:02
Мой первоначальный вариант меня не устраивал. В итоге темы получился третий, который делает всё что необходимо:
- независимый от какого-либо наследования функтор;
- выполнение любых функций/методов с любым набором аргументов;
- возврат любого значения без необходимости явного указания типа этого значения на этапе вызова делегированной функции;
- ну и конечно код должен формироваться не в run-time.
Я бы взял функторы или сигналы из boost и не парился.


Название: Re: Делегат на шаблонах
Отправлено: alexis031182 от Июль 07, 2012, 00:25
Я бы взял функторы или сигналы из boost и не парился.
Нужно было узнать, как это работает.


Название: Re: Делегат на шаблонах
Отправлено: m_ax от Июль 07, 2012, 09:37
Цитировать
Спасибо за приведённый код, но не могли бы Вы растолковать в деталях, чем это решение лучше. В коде я вижу необходимость в наследовании класса, к которому создаётся делегат (может быть это какой-то служебный класс, тогда напишите об этом). Это далеко не всегда хорошо. И не очень понятна ситуация с возвращаемым значением.
Оно, во-первых безопаснее. Т.е. если объект (receiver) будет разрушен, соединение автоматически разорвётся. Во-вторых - это обобщение. Можно соединять как сигналы с сигналами, так и сигнал с любыми слотами: будь то простые функции или члены класса. Причём аргументы у сигнала и у слота могут и не совпадать.

Цитировать
Мой первоначальный вариант меня не устраивал. В итоге темы получился третий, который делает всё что необходимо:
- независимый от какого-либо наследования функтор;
- выполнение любых функций/методов с любым набором аргументов;
- возврат любого значения без необходимости явного указания типа этого значения на этапе вызова делегированной функции;
- ну и конечно код должен формироваться не в run-time.

Вот это всё можно более изяшно реализовать, подсмотрев как устроена libssc.

Цитировать
Update: и Вы, похоже, в аттач только мой вариант делегата запостили
Да, сорри, не то приаттачил)
Переаттачиваю)


Название: Re: Делегат на шаблонах
Отправлено: alexis031182 от Июль 07, 2012, 10:18
Оно, во-первых безопаснее. Т.е. если объект (receiver) будет разрушен, соединение автоматически разорвётся.
Хорошее замечание. Добавлю "умный" указатель. Можно даже посмотреть в сторону запрещения удаления объекта, если хотя бы один из методов объекта контролируется делегатом.

Во-вторых - это обобщение. Можно соединять как сигналы с сигналами, так и сигнал с любыми слотами: будь то простые функции или члены класса. Причём аргументы у сигнала и у слота могут и не совпадать.
Это в Loki есть. Очередь из делегатов. Мне как таковые сигнал/слоты не нужны, нужен именно делегат.

Вот это всё можно более изяшно реализовать, подсмотрев как устроена libssc.
Нет, ну так не очень подходит. Loki я стал рассматривать, потому что там это уже всё было. А тут получается нужно придумывать что-то, чтобы добиться необходимого результата. Как я тогда могу сравнить два подхода? Быстрый взгляд на исходники не дал положительного ответа на вопрос о наличии поддержки любых возвращаемых типов, помимо void.

Сигнал/слот, как я себе их представляю и то, что вижу в Вашем примере, подразумевают наследование от какого-либо базового объекта. Мне же нужен максимально независимый делегат. Наследование будет мешать в том проекте, которым я занят. Возможно позже сигнал/слоты понадобятся, но есть Qt. Тем не менее, для изучения принципа их подключения Ваша библиотека конечно подходит.


Название: Re: Делегат на шаблонах
Отправлено: alexis031182 от Июль 07, 2012, 10:29
Да, смотрю безопасность тоже через "умный" указатель достигается. Значит, похоже, я правильно задумал. Этот момент важен. Спасибо за наводку, отчего-то упустил это из виду.


Название: Re: Делегат на шаблонах
Отправлено: alexis031182 от Июль 07, 2012, 10:49
Да, смотрю безопасность тоже через "умный" указатель достигается. Значит, похоже, я правильно задумал. Этот момент важен. Спасибо за наводку, отчего-то упустил это из виду.
Упс. Забыл про auto_ptr. Безопасность уже есть. Объект не будет удалён, если он занят делегатом. Это в моей ситуации лучше, нежели бы делегат не выполнил функцию.
Код
C++ (Qt)
#include "alokifunctor.h"
 
void a() {
   qDebug() << "a()";
}
 
class Base {
public:
   const QString test(const QString &txt) {return txt;}
};
 
int main(int, char**)
{
ALoki::Functor<> functor(&a);
functor();
 
Base *obj = new Base;
typedef const QString(Base::*test)(const QString&);
ALoki::Functor<const QString, ALoki::TypeSequence<const QString&> > functor1(obj, &Base::test);
 
delete obj;
 
qDebug() << functor1("tratata");
 
qDebug() << obj->test("is not deleted!!!");
 
return 0;
}
 


Название: Re: Делегат на шаблонах
Отправлено: m_ax от Июль 07, 2012, 10:54
Безопасность там как раз достигается (как и в boost::signal, и libsigc++) за счёт наследования от класса trackable. Всё, что наследуется от него автоматически становится отслеживаемым. И если объект будет удалён, то об этом тут же все узнают.

А умные указатели там больше для удобства..


Название: Re: Делегат на шаблонах
Отправлено: m_ax от Июль 07, 2012, 10:56
Чёт я не понял про void..
Какие проблемы с возвращающим типом?


Название: Re: Делегат на шаблонах
Отправлено: alexis031182 от Июль 07, 2012, 11:00
Безопасность там как раз достигается (как и в boost::signal, и libsigc++) за счёт наследования от класса trackable. Всё, что наследуется от него автоматически становится отслеживаемым. И если объект будет удалён, то об этом тут же все узнают.

А умные указатели там больше для удобства..
А-а, понял. Спасибо. Мне так нельзя. Точнее можно конечно, но мультинаследования я стараюсь избегать.

Я смотрю, в libssc используется новый стандарт языка. Вы не могли бы подсказать, в каких файлах применяются новые конструкции, которые "не потянет" C++?


Название: Re: Делегат на шаблонах
Отправлено: alexis031182 от Июль 07, 2012, 11:03
Чёт я не понял про void..
Какие проблемы с возвращающим типом?
Возможно и нет проблем, я написал, что поверхностно взглянул. Каким образом тогда можно добиться возврата любого значения, помимо void? Аналогичным Loki способом? Или как-то иначе?


Название: Re: Делегат на шаблонах
Отправлено: m_ax от Июль 07, 2012, 11:12
Безопасность там как раз достигается (как и в boost::signal, и libsigc++) за счёт наследования от класса trackable. Всё, что наследуется от него автоматически становится отслеживаемым. И если объект будет удалён, то об этом тут же все узнают.

А умные указатели там больше для удобства..
А-а, понял. Спасибо. Мне так нельзя. Точнее можно конечно, но мультинаследования я стараюсь избегать.

Я смотрю, в libssc используется новый стандарт языка. Вы не могли бы подсказать, в каких файлах применяются новые конструкции, которые "не потянет" C++?

Из нового стандарта там пока только decltype используется. А std::shared_ptr можно и самому своять.
У меня где то был рукописный)

Цитировать
Возможно и нет проблем, я написал, что поверхностно взглянул. Каким образом тогда можно добиться возврата любого значения, помимо void? Аналогичным Loki способом? Или как-то иначе?
А понял, чего вы хотите. Нет, в libssc такого нет. Поскольку для системы сигнал-слот это как то не свойственно.. Но сделать это можно. В boost::signal это реализовано.


Название: Re: Делегат на шаблонах
Отправлено: alexis031182 от Июль 07, 2012, 11:20
Из нового стандарта там пока только decltype используется. А std::shared_ptr можно и самому своять.
У меня где то был рукописный)
Поделитесь, если есть возможность. Эта тема мне интересна.

А понял, чего вы хотите. Нет, в libssc такого нет. Поскольку для системы сигнал-слот это как то не свойственно..
Да, разные идеологии. Делегат - "жёсткая" обёртка, если можно так выразиться.

Но сделать это можно. В boost::signal это реализовано.
В Loki возвращаемое значение передаётся как аргумент шаблона по всей иерархии классов. Вы примерно не можете подсказать, насколько отличается этот подход от boost'а?


Название: Re: Делегат на шаблонах
Отправлено: m_ax от Июль 07, 2012, 11:41
Цитировать
Поделитесь, если есть возможность. Эта тема мне интересна.
Ну про decltype можно почитать, например здесь: http://www.quizful.net/post/cpp0x-auto-decltype (http://www.quizful.net/post/cpp0x-auto-decltype)
Доморощенный counted_ptr, аналог std::shared_ptr приаттачил. Но лучше юзать std::shared_ptr.

Цитировать
В Loki возвращаемое значение передаётся как аргумент шаблона по всей иерархии классов. Вы примерно не можете подсказать, насколько отличается этот подход от boost'а?

Не знаю как это в бусте сделано. Надо исходники курить) Я особо этим не интересовался, поскольку не было необходимости в этом. Знаю только, что там это возможно.

 


Название: Re: Делегат на шаблонах
Отправлено: alexis031182 от Июль 07, 2012, 11:56
Ну про decltype можно почитать, например здесь: http://www.quizful.net/post/cpp0x-auto-decltype (http://www.quizful.net/post/cpp0x-auto-decltype)
Доморощенный counted_ptr, аналог std::shared_ptr приаттачил. Но лучше юзать std::shared_ptr.
Спасибо.

Не знаю как это в бусте сделано. Надо исходники курить) Я особо этим не интересовался, поскольку не было необходимости в этом. Знаю только, что там это возможно.
Да, тоже где-то видел ссылку на то, что буст имеет свою реализацию делегата. Но пока тогда остановлюсь на варианте Loki (вполне возможно, что в бусте та же логика).