Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Overload depending on result of lambda

Tags:

c++

c++11

lambda

I have a class like:

class foo
{
    EventManager<LRESULT(UINT, WPARAM, LPARAM)> Events;

    template<typename T>
    void addListener(int e, T&& lambda) { events.add(e, lambda); }
};

However, I need to accept TWO types of lambdas. One with the signature:

[&](UINT, WPARAM, LPARAM) -> LRESULT {} and one with the signature: [&](UINT, WPARAM, LPARAM) -> void {}.

So I want to determine the return type of the lambdas..

I tried something like:

template<typename T>
void addListener(int e, T&& listener)
{
    if (std::is_void<decltype(listener(0, 0, 0))>::value)
    {
        Events.Subscribe(e, [&](UINT msg, WPARAM wp, LPARAM lp) -> LRESULT {
            listener(msg, wp, lp);
            return DefWindowProcW(this->Handle(), msg, wp, lp);
        });
    }

    Events.Subscribe(e, [&](UINT msg, WPARAM wp, LPARAM lp) -> LRESULT {
        std::function<LRESULT()> func = std::bind(listener, msg, wp, lp);
        return func();
    });
}

I also tried declaring the function as a template with the following signature:

template<typename T, typename = typename std::enable_if<std::is_void<typename std::result_of<T(UINT, WPARAM, LPARAM)>::type>::value>::type>

to overload it but it doesn't work either..

Any ideas what I can do without having to create two methods (one for each signature)?

like image 838
Brandon Avatar asked Apr 05 '26 20:04

Brandon


1 Answers

Option #1

Dispatch the call to a proper overload:

#include <functional>
#include <type_traits>
#include <utility>

class foo
{
public:
    template <typename T>
    void addListener(int e, T&& listener)
    {
        addListener(e, std::forward<T>(listener), std::is_void<decltype(listener(0, 0, 0))>{});
    }

private:
    EventManager<LRESULT(UINT, WPARAM, LPARAM)> Events;

    template <typename T>
    void addListener(int e, T&& listener, std::true_type)
    {
        Events.Subscribe(e, [=](UINT msg, WPARAM wp, LPARAM lp) -> LRESULT {
            listener(msg, wp, lp);
            return DefWindowProcW(this->Handle(), msg, wp, lp);
        });
    }

    template <typename T>
    void addListener(int e, T&& listener, std::false_type)
    {
        Events.Subscribe(e, [=](UINT msg, WPARAM wp, LPARAM lp) -> LRESULT {
            std::function<LRESULT()> func = std::bind(listener, msg, wp, lp);
            return func();
        });
    }
};

DEMO 1

Option #2

Add overloads with a SFINAE condition (you need a dummy type template parameter typename = void if you want to hide the enable_if within a template parameters list in the second overload, to make the function templates' declarations distinct):

#include <functional>
#include <type_traits>

class foo
{
public:    
    template <typename T,
              typename = typename std::enable_if<std::is_void<typename std::result_of<T(UINT, WPARAM, LPARAM)>::type>::value>::type>
    void addListener(int e, T&& listener)
    {
        Events.Subscribe(e, [=](UINT msg, WPARAM wp, LPARAM lp) -> LRESULT {
            listener(msg, wp, lp);
            return DefWindowProcW(this->Handle(), msg, wp, lp);
        });
    }

    template <typename T,
              typename = typename std::enable_if<!std::is_void<typename std::result_of<T(UINT, WPARAM, LPARAM)>::type>::value>::type,
              typename = void>
    void addListener(int e, T&& listener)
    {
        Events.Subscribe(e, [=](UINT msg, WPARAM wp, LPARAM lp) -> LRESULT {
            std::function<LRESULT()> func = std::bind(listener, msg, wp, lp);
            return func();
        });
    }

private:
    EventManager<LRESULT(UINT, WPARAM, LPARAM)> Events;
};

DEMO 2

Option #3

Use a trailing return type with a decltype() specifier to enable/disable overloads:

#include <functional>
#include <type_traits>
#include <utility>

class foo
{
public:    
    template <typename T>
    auto addListener(int e, T&& listener)
        -> typename std::enable_if<std::is_void<decltype(std::forward<T>(listener)(0, 0, 0))>{}>::type
    {
        Events.Subscribe(e, [=](UINT msg, WPARAM wp, LPARAM lp) -> LRESULT {
            listener(msg, wp, lp);
            return DefWindowProcW(this->Handle(), msg, wp, lp);
        });
    }

    template <typename T>
    auto addListener(int e, T&& listener)
        -> typename std::enable_if<!std::is_void<decltype(std::forward<T>(listener)(0, 0, 0))>{}>::type
    {
        Events.Subscribe(e, [=](UINT msg, WPARAM wp, LPARAM lp) -> LRESULT {
            std::function<LRESULT()> func = std::bind(listener, msg, wp, lp);
            return func();
        });
    }

private:
    EventManager<LRESULT(UINT, WPARAM, LPARAM)> Events;
};

DEMO 3

like image 130
Piotr Skotnicki Avatar answered Apr 08 '26 10:04

Piotr Skotnicki



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!