3

I'm making a hooking library, which basically intercepts a function and makes it jump to the intercept function.

class CHook
{
public:

    template<typename S, typename D>
    void SetupHook(S Source, D Destionation)
    {
        pSource = (PBYTE)Source;
        pDestination = (PBYTE)Destionation;
    }

private:
    PBYTE pSource;
    PBYTE pDestination;
};

I want CHook::SetupHook to take (for Destination) either DWORD (address of function), function pointer which both can be type casted to PBYTE.

I want CHook::SetupHook to also be able to take a function pointer from lambda but it cannot be type casted to PBYTE so I overload it and since I know lambda function are classes I use std::is_class to identify them.

template<typename S, typename D>
void SetupHook(S Source, typename std::enable_if<!std::is_class<D>::value, D>::type Destionation)
{
    pSource = (PBYTE)Source;
    pDestination = (PBYTE)Destionation;
}

template<typename S, typename D>
void SetupHook(S Source, typename std::enable_if<std::is_class<D>::value, D>::type Destionation)
{
    pSource = (PBYTE)Source;
    pDestination = (PBYTE)to_function_pointer(Destionation);
}

But it results in these erorrs:

error C2783: 'void CHook::SetupHook(S,std::enable_if<std::is_class<D>::value,D>::type)': could not deduce template argument for 'D'
note: see declaration of 'CHook::SetupHook'
3
  • Try template<typename S, typename D> typename std::enable_if<!std::is_class<D>::value>::type SetupHook(S Source, D Destionation) instead (ditto the second overload) Commented Mar 17, 2016 at 19:26
  • that works, post it as the answer and ill select it as the answer. Also, any explanation for this? Commented Mar 17, 2016 at 19:33
  • If you're storing a pointer in it (assuming you're doing this on Windows) PVOID might be a better choice than DWORD. A DWORD is defined to be a 32-bit unsigned integer, which isn't going to do well when storing a pointer on a 64-bit system. Or avoid the platform-specific types altogether if you're tying to make it portable. Commented Mar 17, 2016 at 22:57

2 Answers 2

1

When you write code like:

template <typename S, typename D>
void SetupHook(S Source, typename std::enable_if<!std::is_class<D>::value, D>::type Destionation)
{
    // (...)
}

you make D a non-deducible type template parameter. That is, a type template parameter list, <typename S, typename D>, does not correspond to the parameter list of a function, and so, the compiler can't tell which type template parameter it is supposed to deduce in place of

typename std::enable_if<!std::is_class<D>::value, D>::type

Formally, a nested name specifier introduces a non-deduced context.

The solution is let the compiler deduce D, as a plain parameter type, and to put std::enable_if elsewhere, e.g.:

template <typename S, typename D>
typename std::enable_if<!std::is_class<D>::value>::type SetupHook(S Source, D Destionation)
{
    // (...)
}

Now, the compiler sees that D is the type of the second argument expression.

Sign up to request clarification or add additional context in comments.

Comments

1

Kind of a hack-y trick, but you can convert a lambda to a function pointer using unary+. Note that you can't convert a lambda to a function pointer if it captures any variables.

template<typename S, typename D>
void SetupHook(S Source, D Destionation)
{
    pSource = (PBYTE)Source;
    // static_cast is required for avoiding Visual Studio bug
    pDestination = (PBYTE)+static_cast<void(*)()>(Destionation);
}

...

void some_func() {}

int main() {
  auto str = "hello";
  auto some_lambda = [](){};
  auto some_capturing_lambda = [=]() {
    std::cout << str;
  };
  SetupHook(str, some_func);               // Works
  SetupHook(str, some_lambda);             // Works
  SetupHook(str, some_capturing_lambda);   // Error in Unary Expression
}

7 Comments

I wouldn’t call this a hack. The only real use of unary + is to invoke conversions.
I had no idea about this, so simple! I was using a long method to get the function pointer, thanks!
I also need to be able to pass raw function address e.g. 0x401000 into the parameter source/destination, will unary + not work with that? I think I will still need a seperate overload for raw function address and have like a std::is_integral check, something like that
Doesn't work for me, I get these errors: >Main.cpp(38): error C2593: 'operator +' is ambiguous 1> Main.cpp(38): note: could be 'built-in C++ operator+(void (__cdecl *)(void))' 1> Main.cpp(38): note: or 'built-in C++ operator+(void (__stdcall *)(void))' 1> Main.cpp(38): note: or 'built-in C++ operator+(void (__fastcall *)(void))' 1> Main.cpp(38): note: or 'built-in C++ operator+(void (__vectorcall *)(void))'
@Budskii what are you passing for that case? Looks like you're using visual studio, what version? I tested this on clang and gcc.
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.