Pull to refresh

Простейший делегат с использованием С++11

Недавно наткнулся на статью про простенький делегат на C++. Мне очень понравилась статья и я захотел немного улучшить этот делегат. Я решил использовать «Variadric Templates» — шаблоны с переменным количеством аргументов. Плюс, я хотел добавить возможность подключать несколько методов и функций. Так-же немного увеличил читабельность.



Для начала улучшил 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
Tags:
Hubs:
You can’t comment this publication because its author is not yet a full member of the community. You will be able to contact the author only after he or she has been invited by someone in the community. Until then, author’s username will be hidden by an alias.
Change theme settings