понедельник, 23 мая 2011 г.

Partial template specialization for pointers to __stdcall functions

Если мы хотим написать частичную специализацию шаблона, например, для указателя на функцию, то обычно пишем что-то вроде следующего кода:
template<typename F> class TFunction;

template<typename R, typename T0>
class TFunction<R(*)(T0)>
{
};
Здесь мы пытаемся специализировать шаблон TFunction для указателя на функцию одного аргумента. Далее имеем два указателя:
typedef bool (*my_function_f)(int);             // (1)
typedef bool (__stdcall *my_function_f2)(int);  // (2)
Для (1) специализация, которая написана выше вполне подходит, а вот для (2) — нет. По умолчанию, предполагается что функция использует соглашение вызова __cdecl. Если мы хотим передавать указатели на функции с соглашением вызова __stdcall, то необходимо для этого случая написать отдельную специализацию:
template<typename R, typename T0>
class TFunction<R(__stdcall *)(T0)>
{
};
Кажется, этого достаточно. Однако, при компиляции кода под x64 процессоры ключевое слово __stdcall, которое обозначает порядок вызова функций, игнорируется. Обычно это незаметно, но если вы пытаетесь написать частичную специализацию шаблона, то проявляются неприятные эффекты. А именно, две специализации, которые написаны выше, получаются абсолютно одинаковыми, что приводит к ошибке компиляции. Решением будет отключить компиляцию второй специализации, например, так:
#ifndef _M_X64
template<typename R, typename T0>
class TFunction<R(__stdcall *)(T0)>
{
};
#endif
Макрос _M_X64 предопределен для x64 процессоров.

Ссылки по теме:
  1. What is __stdcall?
  2. Partial template specialization
  3. Calling convention
  4. Predefined Macros

Комментировать в ВКонтакте