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

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

Страниц: 1 [2] 3   Вниз
  Печать  
Автор Тема: Делегат на шаблонах  (Прочитано 17092 раз)
DmitryM
Гость
« Ответ #15 : Июль 02, 2012, 17:39 »

Интересное решение, но проблема в том, что в базовом классе возвращаемый тип не известен.
Ох, тогда возвращай variant/any
Записан
alexis031182
Гость
« Ответ #16 : Июль 02, 2012, 17:49 »

Ох, тогда возвращай variant/any
Спасибо большое, оно Улыбающийся
Записан
alexis031182
Гость
« Ответ #17 : Июль 02, 2012, 19:01 »

Всё работает. Осталась оптимизация. Шаблон варианта автоматом разрулил все остальные проблемы. Спасибо Улыбающийся
Записан
alexis031182
Гость
« Ответ #18 : Июль 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;
}
 
Записан
DmitryM
Гость
« Ответ #19 : Июль 05, 2012, 16:44 »

Цитировать
что в базовом классе возвращаемый тип не известен.
И как ты решил свою проблему?  Улыбающийся
Записан
alexis031182
Гость
« Ответ #20 : Июль 05, 2012, 17:17 »

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

Сообщений: 2095



Просмотр профиля
« Ответ #21 : Июль 06, 2012, 08:59 »

Это всё можно проще сделать.. Накатаю пример, как приду домой..
Записан

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

Arch Linux Plasma 5
alexis031182
Гость
« Ответ #22 : Июль 06, 2012, 12:14 »

Для полного счастья ещё не хватает возможности вызова функции, не являющейся методом какого-либо класса.
Записан
alexis031182
Гость
« Ответ #23 : Июль 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;
}
 
Третий вариант отличается от второго прежде всего структурой. Помимо прочего, добавлено два класса для "разруливания" указателя на функцию и указателя на метод класса.
Записан
m_ax
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 2095



Просмотр профиля
« Ответ #24 : Июль 06, 2012, 21:20 »

Можно лучше. Например, используя библиотеку libssc (я использовал версию libssc-cpp11-1.0.3 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 и этот делегат готов)

« Последнее редактирование: Июль 07, 2012, 09:42 от m_ax » Записан

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

Arch Linux Plasma 5
alexis031182
Гость
« Ответ #25 : Июль 06, 2012, 22:17 »

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

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

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

Update: и Вы, похоже, в аттач только мой вариант делегата запостили
« Последнее редактирование: Июль 06, 2012, 23:37 от alexis031182 » Записан
DmitryM
Гость
« Ответ #26 : Июль 07, 2012, 00:02 »

Мой первоначальный вариант меня не устраивал. В итоге темы получился третий, который делает всё что необходимо:
- независимый от какого-либо наследования функтор;
- выполнение любых функций/методов с любым набором аргументов;
- возврат любого значения без необходимости явного указания типа этого значения на этапе вызова делегированной функции;
- ну и конечно код должен формироваться не в run-time.
Я бы взял функторы или сигналы из boost и не парился.
Записан
alexis031182
Гость
« Ответ #27 : Июль 07, 2012, 00:25 »

Я бы взял функторы или сигналы из boost и не парился.
Нужно было узнать, как это работает.
Записан
m_ax
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 2095



Просмотр профиля
« Ответ #28 : Июль 07, 2012, 09:37 »

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

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

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

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

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

Arch Linux Plasma 5
alexis031182
Гость
« Ответ #29 : Июль 07, 2012, 10:18 »

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

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

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

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


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