简体   繁体   中英

C++ test if lambda function

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'

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, eg:

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.

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
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM