Указатели функций-членов и наиболее быстрые делегаты C++ - Статические функции как цели делегатов

ОГЛАВЛЕНИЕ

Статические функции как цели делегатов

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

Вы можете сохранить указатель функции вместо указателя this, так что когда вызывается функция-вызыватель, она только лишь должна преобразовать this в указатель статической функции и вызвать его. Это абсолютно не влияет на код для обычных функций-членов. Проблема в том, что этот прием требует преобразования между указателями кода и данных. Это не будет работать в системах, где указатели кода больше, чем указатели данных (компиляторы DOS используют модель средней памяти). Это будет работать для 32- и 64-битных процессоров, известных нам. Но данному методу нужна альтернатива.

Безопасный метод состоит в сохранении указателя функции как дополнительного члена делегата. Делегат ссылается на свою собственную функцию-член. Во всех случаях, когда делегат копируется, данные ссылки на самих себя нужно преобразовывать, что усложняет операторы = и ==. При этом размер делегата увеличивается на 4 байта и повышается сложность кода, но это не влияет на скорость вызова.

Мы реализовали оба метода, так как оба из них имеют свое достоинство: второй метод гарантированно будет работать, а первый метод порождает такой ассемблерный код, какой мог бы порождать компилятор, если бы он имел встроенную поддержку для делегатов. Первый метод можно включить с помощью #define (FASTDELEGATE_USESTATICFUNCTIONHACK).

Примечание: почему первый метод вообще работает? Если вы внимательно рассмотрите алгоритм, используемый каждым компилятором при вызове указателя функции-члена, вы увидите, что во всех этих компиляторах при использовании не виртуальной функции с одиночным наследованием (то есть delta=vtordisp=vindex=0) указатель экземпляра не применяется для вычисления того, какую функцию вызывать. Поэтому даже с помощью бессмысленного указателя экземпляра будет вызвана правильная функция. Внутри этой функции получаемый указатель this будет равен garbage + delta = garbage. (другими словами, бессмыслица на входе, немодифицированная бессмыслица на выходе!) При этом можно преобразовать нашу бессмыслицу обратно в указатель функции. Данный метод не будет работать, если вызыватель статической функции является виртуальной функцией.