Недавно наткнулся на статью про простенький делегат на C++. Мне очень понравилась статья и я захотел немного улучшить этот делегат. Я решил использовать «Variadric Templates» — шаблоны с переменным количеством аргументов. Плюс, я хотел добавить возможность подключать несколько методов и функций. Так-же немного увеличил читабельность.
Для начала улучшил IContainer.
Добавил variadric template и указатель на следующий элемент для односвязного списка.
Обновил реализацию для метода
И для обычной функции.
Ну и наконец сам делегат:
Использовать его довольно просто:
В выводе у нас будет:
a+b+c = 6
a = 1
b = 2
c = 3
a+b+c = 11
Это в общем-то и всё чем я хотел поделиться.
Я понимаю, что использовал не оптимальные методы. И вообще написал велосипед. Но ради интереса и разобраться как это работает.
Сравнивал скорость с FastDelegate — мой делегат оказался в 2 раза медленнее. Но для моих задач вполне подходит.
Полный исходный код здесь pastie.org/4725946
Для начала улучшил IContainer.
template<typename... Args>
class IContainer
{
public:
virtual void Call(Args...) {} //Вызов функции
virtual ~IContainer() {}
IContainer<Args...> *next;//Указатель на следующий элемент
};
Добавил variadric template и указатель на следующий элемент для односвязного списка.
Обновил реализацию для метода
template< typename T, typename M, typename... Args > class MContainer : public IContainer<Args...>
{
public:
MContainer( T* c, M m ) : mClass( c ), mMethod( m ) {}
void Call(Args... args)
{
(mClass->*mMethod)( args... );
}
private:
T *mClass;
M mMethod;
};
И для обычной функции.
template< typename M, typename... Args > class FContainer : public IContainer<Args...>
{
public:
FContainer( M m ) : mMethod( m ) {}
void Call(Args... args)
{
(mMethod)( args... );
}
private:
M mMethod;
};
Ну и наконец сам делегат:
template<typename... Args>
class Delegate
{
public:
Delegate()
{
mContainerHead = new IContainer<Args...>(); //Создаем начальный элемент
mContainerTail = mContainerHead; //Назначаем конец списка для быстрого добавления новых элементов
mContainerHead->next = 0;
}
~Delegate()
{
//Тут мы полностью удаляем все элементы
IContainer<Args...> *container = mContainerHead;
while(container)
{
IContainer<Args...> *temp = container->next;
delete container;
container = temp;
}
}
//Удаление всех связей
void Clear()
{
IContainer<Args...> *container = mContainerHead->next;
while(container)
{
IContainer<Args...> *temp = container->next;
delete container;
container = temp;
}
mContainerHead->next = 0;
mContainerTail = mContainerHead;
}
//Подключение метода
template<typename T, typename M>
void Connect(T *c, M m)
{
mContainerTail->next = new MContainer< T, M, Args... >(c,m);
mContainerTail->next->next = 0;
mContainerTail = mContainerTail->next;
}
//Подключение функции
template<typename M>
void Connect(M m)
{
mContainerTail->next = new FContainer< M, Args... >(m);
mContainerTail->next->next = 0;
mContainerTail = mContainerTail->next;
}
//Отключение метода
template<typename T, typename M>
void Disconnect(T *c, M m)
{
IContainer<Args...> *container = mContainerHead;
while(container->next)
{
//Тут я использовал dynamic_cast для нахождения нужного элемента.
//Возможно не лучший спосо, но ничего другого не придумал.
MContainer<T, M, Args...> *temp = dynamic_cast< MContainer<T, M, Args...>* >(container->next);
if(temp)
{
if(container->next == mContainerTail)
{
mContainerTail = container;
}
container->next = container->next->next;
delete temp;
break;
}
container = container->next;
}
}
//Отключение функции
template<typename M>
void Disconnect(M m)
{
IContainer<Args...> *container = mContainerHead;
while(container->next)
{
FContainer<M, Args...> *temp = dynamic_cast< FContainer<M, Args...>* >(container->next);
if(temp)
{
if(container->next == mContainerTail)
{
mContainerTail = container;
}
container->next = container->next->next;
delete temp;
break;
}
container = container->next;
}
}
//Перегружаем оператор вызова функции
void operator ()(Args... args)
{
Call(args...);
}
//Ну и сам вызов делегата
void Call(Args... args)
{
IContainer<Args...> *container = mContainerHead;
while(container)
{
container->Call(args...);
container = container->next;
}
}
private:
IContainer<Args...> *mContainerHead;
IContainer<Args...> *mContainerTail;
};
Использовать его довольно просто:
#include <iostream>
#include "Delegate.h"
using namespace std;
//Простой класс для примера
class A
{
public:
void f(int a, int b, int c)
{
cout << "a+b+c = " << a+b+c << endl;
}
};
//И простая функция
void f2(int a, int b, int c)
{
cout << "a = " << a << endl << "b = " << b << endl << "c = " << c << endl;
}
int main()
{
//Создаем делегат принмающий фукнции с тремя переменными типа int
Delegate<int,int,int> d;
//Тестовый класс
A testA;
//Подключаем метод и функцию
d.Connect(&testA, &A::f);
d.Connect(f2);
//Вызываем
d.Call(1,2,3);
//Отключаем функцию
d.Disconnect(f2);
//Вызываем делегат еще раз
d.Call(2,4,5);
return 0;
}
В выводе у нас будет:
a+b+c = 6
a = 1
b = 2
c = 3
a+b+c = 11
Это в общем-то и всё чем я хотел поделиться.
Я понимаю, что использовал не оптимальные методы. И вообще написал велосипед. Но ради интереса и разобраться как это работает.
Сравнивал скорость с FastDelegate — мой делегат оказался в 2 раза медленнее. Но для моих задач вполне подходит.
Полный исходный код здесь pastie.org/4725946