dimanche 29 janvier 2023

How to create a function that takes both function pointers and lambda as arguments?

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;
}

Aucun commentaire:

Enregistrer un commentaire