Указатели функций-членов и наиболее быстрые делегаты C++ - Непустые возвращаемые значения, передача FastDelegate, естественный синтаксис и совместимость

ОГЛАВЛЕНИЕ

Непустые возвращаемые значения

Версия кода 1.3 добавляет возможность использовать непустые возвращаемые значения. Как и std::unary_function, возвращаемый тип – это последний параметр. По умолчанию он равен void, что сохраняет обратную совместимость и также означает то, что самый общий случай остается простым. Мы хотели выполнять это без потери производительности на любой платформе. Это оказалось легко сделать для любого компилятора, за исключением MSVC6. В VC6 есть два существенных ограничения:

  1. нельзя использовать void как аргумент шаблона по умолчанию.
  2. нельзя возвращать void.

Для решения данных проблем мы использовали два приема:

  1. Создается фиктивный класс, названный DefaultVoid. он преобразуется в void при необходимости.
  2. Если нужно вернуть void, вместо этого возвращается const void *. Такие указатели возвращаются в регистре EAX. С точки зрения компилятора, нет абсолютно никакой разницы между функцией void и функцией void *, возвращаемое значение которых никогда не используется. Нельзя преобразовать void в void * в рамках вызова функции, не генерируя неэффективный код. Но если вы преобразовываете указатель функции в момент, когда вы получаете его, вся работа выполняется во время компиляции (то есть, вам нужно преобразовать определение функции, а не само возвращаемое значение).

Все экземпляры FastDelegate0 должны быть заменены на FastDelegate0<>. Это можно выполнить с помощью глобального поиска и замены во всех ваших файлах. Это изменение делает синтаксис более наглядным: объявление любого типа void FastDelegate теперь вынлядет аналогично объявлению функции, за исключением того, что () заменяется на <>. Если это изменение вам не нравится, можно изменить заголовочный файл: поместить определение FastDelegate0<> внутри отдельного пространства имен newstyle { и } typedef newstyle::FastDelegate0<> FastDelegate0;. Вы также должны будете изменить соответствующую функцию MakeDelegate.

Передача FastDelegate в качестве параметра функции

Шаблон MakeDelegate позволяет вам использовать FastDelegate как подставляемую замену для указателя функции. Обычное приложение должно содержать делегат FastDelegate как скрытый член класса и использовать функцию-модификатор для его установки (как Microsoft __event). Например:

// Принимает любую функцию с такой сигнатурой: int func(double, double);
class A {
public:
    typedef FastDelegate2<double, double, int> FunctionA;
    void setFunction(FunctionA somefunc){ m_HiddenDelegate = somefunc; }
private:
    FunctionA m_HiddenDelegate;
};
// Синтаксис для установки делегата:

A a;
a.setFunction( MakeDelegate(&someClass, &someMember) ); // для функций-членов, или
a.setFunction( &somefreefunction ); // для функций- не членов класса или статических функций

Естественный синтаксис и совместимость с Boost (новинка версии 1.4)

Jody Hagins усовершенствовал классы FastDelegateN, реализовав для них такой же привлекательный синтаксис, что и для новых версий Boost.Function и Boost.Signal. В компиляторах с частичной специализацией шаблонов вы можете записать FastDelegate< int (char *, double)> вместо FastDelegate2<char *, double, int>. Если ваш код должен компилироваться в VC6, VC7.0 или в Borland, вы должны использовать старый переносимый синтаксис. Мы убедились, что старый и новый синтаксис на 100% эквиваленты и могут использоваться поочередно.

Jody также создал вспомогательную функцию bind, позволяющую быстро преобразовывать код, написанный для Boost.Function и Boost.Bind, в FastDelegate. Это позволяет вам быстро определять, насколько повысится производительность вашего кода, если вы перейдете на FastDelegate. Это можно найти в "FastDelegateBind.h". Если у нас есть код:

      using boost::bind;
      bind(&Foo:func, &foo, _1, _2);

мы можем заменить "using" на using fastdelegate::bind, при этом все должно работать правильно. Предупреждение: аргументы bind игнорируются! На самом деле никакое связывание не выполняется. Поведение эквивалентно boost::bind только в тривиальном (самом обычном) случае, когда используются только базовые аргументы-заполнители _1, _2, _3, и т.д.. Будущие версии смогут поддерживать boost::bind должным образом.