- PVSM.RU - https://www.pvsm.ru -

Делегат на С++11

Всем привет!

Писал когда-то статью, почти такую же (Делегат на С++ [1]).
После этого, начал изучать новые технологии (в частности С++11), где мне понравилась новая возможность, которая называется «Variadic Templates». В общем, изучение и использование новых возможностей этого языка и побудило меня к написанию этой статьи.

Для начала скажу, что было плохо и чего не хватало с предыдущей версии. Конечно же, это принимаемых методом аргументов (ну или параметров, как кому нравится). Да и сделать, в принципе было ничего нельзя… Ну как, можно, но, как-то неправильно, что-ли? Выход был один: писать такое количество классов Delegate, которое соответствовало бы количеству параметров. Т.е. Delegate0, Delegate1, Delegate2, etc… Что, в свою очередь, означает «Делегат без параметров», «Делегат с одним параметром», «Делегат с двумя параметрами», и т.д. Не очень круто, правда?
Но такие варианты есть и работают (как, например, в Qt, если заглянуть в исходники, найти класс QMetaObject (насколько я помню), там есть метод QMetaObject::invokeMethod(...), и там таких методов переопределено аж 9, опять же, насколько я помню, с разными количествами параметров). Но Qt — это отдельная тема, довольно неплохая, в принципе. Qt расширяет возможности языка C++ посредством MOC (MetaObject Compiler), поэтому там доступно много чего, чего на «чистом» С++ реализовать не получится, в частность Reflection [2], который мне очень нравится. Таким же образом реализован их механизм сигналов/слотов. Но сейчас не об этом…

Так вот, появилась новая фича, шаблоны с переменным количеством аргументов. Кстати говоря, не везде. Если Вы используете MSVC компитятор, даже MSVS 2012, вы не сможете использовать эту фичу, ибо «мелкософт», почему-то, считают, что все возможности можно реализовать и позже… Я так, полагаю, после ввода C++14, который уже был анонсирован [3]… В общем, разочароваться в мелкософте можно тут [4], а, пока что, предлагаю выбросить сей несуразный компилятор в утиль…

Поэтому, испольуйте G++ на Linux, MacOS или его порт MinGW на Windows, дабы все ниженаписанное заработало. Кстати, на винде не проверял работоспособность, уже давно ей не пользуюсь вообще, только MacOS/Linux.

Так вот, предлагаю отступить от лирики и перейти к действиям. Опять же, нам понадобятся знания ООП (в частности полиморфизм) и знания С++.
Структура та же.

Первый — базовый класс, который мы не будем создавать вообще. Это надо, т.к. нам еще не известен объект, чей метод мы собираемся вызывать…

template<typename ...Args>
class DelegateBase
{
public:
    virtual ~DelegateBase();
    virtual void notify(Args...) = 0;
};

Второй класс — это уже класс, с объектом которого мы и будем работать. Здесь уже известно, метод какого класса мы будем вызывать…

template<typename T, typename ...Args>
class DelegateCallback : public DelegateBase<Args...>
{
private:
    T *_object;
    void (T::*_method)(Args...);
public:
    DelegateCallback(T *object, void (T::*method)(Args...))
    {
        this->_object = object;
        this->_method = method;
    }
    virtual ~DelegateCallback()
    {
        this->_object = 0;
        this->_method = 0;
    }
    virtual void notify(Args ...args)
    {
        (this->_object->*this->_method)(args...);
    }
};

Ну и третий класс — сам класс делегата. Мы еще не знаем, метод какого объекта класса мы будем вызывать, но знаем все параметры. После вызова метода attach(...) мы укажем объект класса и указатель на метод класса, который надо будет вызвать. Ну и переопределим оператор () для удобства пользования, хотя это не принципиально…

template<typename ...Args>
class Delegate
{
private:
    DelegateBase<Args...> *_delegate;
public:
    Delegate()
    {
        this->_delegate = 0;
    }
    ~Delegate()
    {
        if(this->_delegate != 0) {
            delete this->_delegate;
            this->_delegate = 0;
        }
    }
    template<typename T> void attach(T *object, void (T::*method)(Args...))
    {
        if(this->_delegate != 0) { delete this->_delegate; }
        this->_delegate = new DelegateCallback<T, Args...>(object, method);
    }
    void notify(Args ...args)
    {
        if(this->_delegate != 0) {
            this->_delegate->notify(args...);
        }
    }
    void operator ()(Args ...args)
    {
        this->_notify(args...);
    }
};

В общем, это и все!

Для создания пустого делегата (в смысле без параметров) необходимо объявить Delegate<>, а в остальном — как обычные шаблоны.
Delegate<int, float, double, const std::string &, void *>, в общем, что хотите…
Пример использования есть в предыдущей статье.

Ах да, чуть не забыл… Компилировать надо с ключом -std=c++11, но сам компилятор об этом ругнется, если этого не сделать…

Всем спасибо за внимание!

Автор: lexdevel

Источник [5]


Сайт-источник PVSM.RU: https://www.pvsm.ru

Путь до страницы источника: https://www.pvsm.ru/tutorial/38521

Ссылки в тексте:

[1] Делегат на С++: http://habrahabr.ru/sandbox/51909/

[2] Reflection: http://ru.wikipedia.org/wiki/%D0%9E%D1%82%D1%80%D0%B0%D0%B6%D0%B5%D0%BD%D0%B8%D0%B5_(%D0%BF%D1%80%D0%BE%D0%B3%D1%80%D0%B0%D0%BC%D0%BC%D0%B8%D1%80%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5)

[3] анонсирован: http://habrahabr.ru/post/184606/

[4] тут: http://msdn.microsoft.com/ru-ru/library/hh567368.aspx

[5] Источник: http://habrahabr.ru/post/186370/