1

I have the following class who has a method called errorHandler that needs to use several different callbacks:

class IOPin;
class IOPinHandler
{
    IOPinHandler();
    virtual ~IOPinHandler();

    static bool ptrFun(IOPinHandler&) { return true; };

    template<typename T>
    bool init(IOPin& obj, const T& param);

    template<typename HandlerReturn = void, typename ...Args>
    HandlerReturn errorHandler(const GpioStatusCode& code, HandlerReturn(*callback)(IOPinHandler& obj, const Args&...), const Args&... args);

    // Added this overload to support passing lambdas as arguments, 
    // however does not seems to be called
    template<typename HandlerReturn = void, typename ...Args>
    HandlerReturn errorHandler(const GpioStatusCode& code, const std::function<HandlerReturn(IOPinHandler&, const Args&...)>& callback, Args&... args);
};

I can use the following:

return errorHandler(GpioStatusCode::wrongArgumentsError, &IOPinHandler::ptrFun);

And the code compiles successfully and runs as I expect it to do.


I can also use pass a lambda function in the following way:

auto fix = [](IOPinHandler& obj) -> bool 
{
    return true; 
};
return errorHandler(GpioStatusCode::wrongArgumentsError, static_cast<bool(*)(IOPinHandler&)>(fix));

This way, the code also compiles and runs successfully.


However, if I add capture to the lambda function, the code won't compile:

auto fix = [&](IOPinHandler& obj) -> bool 
{
    return true; 
};
return errorHandler(GpioStatusCode::wrongArgumentsError, fix);

I get the following compile time error:

error: no matching function for call to 'IOPinHandler::errorHandler(GpioStatusCode, IOPinHandler::init<GpioMode>::<lambda(IOPinHandler&)>&)'
   12 |         return errorHandler(GpioStatusCode::wrongArgumentsError, fix);

I thought that adding:

template<typename HandlerReturn = void, typename ...Args>
HandlerReturn errorHandler(const GpioStatusCode& code, const std::function<HandlerReturn(IOPinHandler&, const Args&...)>& callback, Args&... args);

Would match the template instantiation for receiving the lambda function as argument, but the fact that I have to cast the lambda without capture and that the lambda with capture does not work either, kinda suggest to me that only the first definition or "errorHandler" is being invoked.

Is my reasoning right? How could I get pass this?


Edit: Full code.

#include <functional>

class IOPin;

class IOPinHandler
{

    public:
        template<typename ...T>
        IOPinHandler(const T&... args);
        virtual ~IOPinHandler(); 

        static bool ptrFun(IOPinHandler&);

        template<typename T>
        bool init(const T& param);

        template<typename HandlerReturn = void, typename ...Args>
        HandlerReturn errorHandler(const int& code, HandlerReturn(*callback)(IOPinHandler& obj, const Args&...), const Args&... args);

        // Added this overload to support passing lambdas as arguments, 
        // however does not seems to be called
        template<typename HandlerReturn = void, typename ...Args>
        HandlerReturn errorHandler(const int& code, const std::function<HandlerReturn(IOPinHandler&, const Args&...)>& callback, Args&... args);
};

template<typename ...T>
IOPinHandler::IOPinHandler(const T&... args)
{
    (init(args),...);
}

bool IOPinHandler::ptrFun(IOPinHandler&)
{
    return true;
}

template<typename T>
bool IOPinHandler::init(const T& param)
{
    // Compiles with no problem
    // return errorHandler(1, &IOPinHandler::ptrFun); 

    // Compiles with no problem
    // auto fix = [](IOPinHandler& obj) -> bool 
    // {
    //    return true; 
    // };
    // return errorHandler(1, static_cast<bool(*)(IOPinHandler&)>(fix));

    // Does not compiles
    auto fix = [&](IOPinHandler& obj) -> bool 
    {
        return true; 
    };
    return errorHandler(1, fix);    
}

int main(void)
{
    IOPinHandler a(1,'c',true);
    return 0;
}
4
  • 2
    This is what std::function is for, and the shown code makes use of it. Unfortunately, the disjointed code snippets seem to be too scattered, and are all over the place in order to see a clear picture. Can you edit your question and present a single, concise, minimal reproducible example that demonstrates your question? Commented Jan 29, 2023 at 21:04
  • 1
    FYI, in the non-capturing example, static_cast<bool(*)(IOPinHandler&)>(fix) can be simplified to just +fix, see Resolving ambiguous overload on function pointer and std::function for a lambda using + (unary plus). Of course, std::function handles many different kinds of callables, including lambdas and function pointers, so you don't really need the function-pointer overload of errorHandler() to begin with. Commented Jan 29, 2023 at 21:14
  • @SamVarshavchik. I just added a minimal reproducible example. Thanks for making me realize about that. Commented Jan 29, 2023 at 21:35
  • @RemyLebeau. I just removed the const and refference from the argument. the declaration is now: "HandlerReturn errorHandler(const GpioStatusCode& code, std::function<HandlerReturn(IOPinHandler&, const Args&...)> callback, Args&... args);". Still fails to compile Commented Jan 29, 2023 at 21:37

1 Answer 1

5

If I have understood correctly, you don't need any member function overload or even std::function.

You can make the Callable as template parameter and let the compiler deduce the type of it for you.

template<typename Callable, typename... Args>
auto errorHandler(const GpioStatusCode& code, Callable callback, Args&&... args)
  // -> decltype(callback(args...))---> if compiler does not support C++14 or later
{
    // if needed within the function body
    using HandlerReturn = decltype(callback(args...)); 
    // ... do something
    return callback(std::forward<Args>(args)...);
}

Live Demo

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

Comments

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.