2015-04-12 3 views
1

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

// Cast a function pointer to a void * 
template <typename RET_TYPE, typename...ARGs> 
void* fnPtrToVoidPtr(RET_TYPE(WINAPI * pOriginalFunction)(ARGs...)) 
{ 
    return (void*)pOriginalFunction; 
} 

// Cast a function pointer that is referencable to a void *& 
template <typename RET_TYPE, typename...ARGs> 
void*& fnPtrRefToVoidPtrRef(RET_TYPE(WINAPI*& pOriginalFunction)(ARGs...)) 
{ 
    return (void*&)pOriginalFunction; 
} 

Это позволяет мне сделать следующий вызов:

BOOL (WINAPI *pDestroyIcon)(HICON) = DestroyIcon; 
DetourAttach(&fnPtrRefToVoidPtrRef(pDestroyIcon), fnPtrToVoidPtr(DestroyIcon)); 

Однако, мне было интересно, если я мог бы объединить два названия функций fnPtrRefToVoidPtrRef и fnPtrToVoidPtr в одно имя.

Выполнение следующих не работает, поскольку она не может вывести аргументы шаблона:

// Cast a function pointer to a void * 
template <typename RET_TYPE, typename...ARGs> 
void* fnPtrToVoidPtr(RET_TYPE(WINAPI * & pOriginalFunction)(ARGs...)) 
{ 
    return (void*)pOriginalFunction; 
} 

// Cast a function pointer that is referencable to a void *& 
template <typename RET_TYPE, typename...ARGs> 
void*& fnPtrToVoidPtr(RET_TYPE(WINAPI * && pOriginalFunction)(ARGs...)) 
{ 
    return (void*&)pOriginalFunction; 
} 

BOOL (WINAPI *pDestroyIcon)(HICON) = DestroyIcon; 
void* p1 = fnPtrToVoidPtr(DestroyIcon); 
void** p2 = &fnPtrToVoidPtr(pDestroyIcon); 

вызывает следующую ошибку:

// error C2784: 'void *&fnPtrToVoidPtr(RET_TYPE (__stdcall *&&)(ARGs...))' : could not deduce template argument for 'overloaded function type' from 'overloaded function type' 

Используя свои первоначальные функции, это работает отлично:

BOOL (WINAPI *pDestroyIcon)(HICON) = DestroyIcon; 
    void* p1 = fnPtrToVoidPtr(DestroyIcon); 
    void** p2 = &fnPtrRefToVoidPtrRef(pDestroyIcon); 

Однако, если я изменю fnPtrRefToVoidPtrRef на это:

// Cast a function pointer that is referencable to a void *& 
template <typename RET_TYPE, typename...ARGs> 
void*& fnPtrRefToVoidPtrRef(RET_TYPE(WINAPI*&& pOriginalFunction)(ARGs...)) 
{ 
    return (void*&)pOriginalFunction; 
} 

Я получаю следующее сообщение об ошибке:

error C2664: 'void *&fnPtrRefToVoidPtrRef<BOOL,HICON>(RET_TYPE (__stdcall *&&)(HICON))' : cannot convert argument 1 from 'BOOL (__stdcall *)(HICON)' to 'BOOL (__stdcall *&&)(HICON)' 

который, кажется, почему он не может сделать вывод шаблона, он не признает его, чтобы быть того же типа (или кабриолет?). Есть ли способ заставить C++ правильно выводить указатель на функцию?

ответ

2

Есть две проблемы с вашим кодом. Давайте исправим их по порядку.

Во-первых, необходимо переключить тела и типы возврата двух перегрузок fnPtrToVoidPtr. Вы хотите преобразовать lvalues ​​типа указателя функции в lvalues ​​типа void*, через листинг (void*&), так что это литье должно идти в тело функции, принимающей - это тип параметра, который будет связываться с изменяемыми значениями lvalues, другой, * && свяжется с rvalues. И наоборот, листинг (void*) должен перейти в функцию, принимающую * &&. Очевидно, что соответственно необходимо изменить типы возврата.

Теперь причина отказа при вычете: при совершении вызова, например fnPtrToVoidPtr(DestroyIcon), в вашей первоначальной версии вы полагаетесь на преобразование функции в указатель. Это преобразование не выполняется, если параметр (назначение) является ссылкой.

Итак, в вашей второй версии, где обе перегрузки берут ссылки, параметры являются ссылками на указатели, но аргумент является идентификатором функции; аргументы шаблона для первого не могут быть выведены из последнего, поэтому вывод не выполняется. Простейшим решением для этого является явное предоставление указателя функции для вызова, например: fnPtrToVoidPtr(&DestroyIcon).

&DestroyIcon - rvalue, поэтому параметр * && будет привязан к нему, * & не будет, именно то, что мы хотим.

С помощью этих двух исправлений, компилируемая версия коды становится:

#include "windows.h" 

// Cast a function pointer to a void * 
template <typename RET_TYPE, typename...ARGs> 
void* fnPtrToVoidPtr(RET_TYPE(WINAPI * && pOriginalFunction)(ARGs...)) 
{ 
    return (void*)pOriginalFunction; 
} 

// Cast a function pointer that is referencable to a void *& 
template <typename RET_TYPE, typename...ARGs> 
void*& fnPtrToVoidPtr(RET_TYPE(WINAPI * & pOriginalFunction)(ARGs...)) 
{ 
    return (void*&)pOriginalFunction; 
} 

BOOL(WINAPI *pDestroyIcon)(HICON) = DestroyIcon; 

int main() 
{ 
    void* p1 = fnPtrToVoidPtr(&DestroyIcon); 
    void** p2 = &fnPtrToVoidPtr(pDestroyIcon); 
} 

Если вам не нравится необходимость использовать оператор & перед именами функций, вы можете также изменить перегрузку принимая * && взять & - ссылку на функцию lvalue. Теперь эту версию можно назвать fnPtrToVoidPtr(DestroyIcon). A && (rvalue reference to function) также будет работать, так как ссылки rvalue на функции также привязываются к функциям lvalues ​​(все идентификаторы выражений, обозначающие функции, являются lvalues).

+0

Я пытался понять это на пару дней! Конечно, вычет шаблона не работает, если он должен принуждать тип. Это имеет такое значение. СПАСИБО! – Adrian