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;
}
JeJo
  • 30,635
  • 6
  • 49
  • 88
Juan_David
  • 126
  • 2
  • 11
  • 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, [mre] that demonstrates your question? – Sam Varshavchik Jan 29 '23 at 21:04
  • 1
    FYI, in the non-capturing example, `static_cast(fix)` can be simplified to just `+fix`, see [Resolving ambiguous overload on function pointer and std::function for a lambda using + (unary plus)](https://stackoverflow.com/questions/17822131/). 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. – Remy Lebeau Jan 29 '23 at 21:14
  • @SamVarshavchik. I just added a minimal reproducible example. Thanks for making me realize about that. – Juan_David Jan 29 '23 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 callback, Args&... args);". Still fails to compile – Juan_David Jan 29 '23 at 21:37

1 Answers1

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

JeJo
  • 30,635
  • 6
  • 49
  • 88