Overload depending on lambda result
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 lambda. One with signature:
[&](UINT, WPARAM, LPARAM) -> LRESULT {}
and one with the signature: [&](UINT, WPARAM, LPARAM) -> void {}
.
So, I want to define the return type of lambdas ..
I've 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 to declare 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.
Any ideas what I can do without creating two methods (one for each signature)?
source to share
Option number 1
Send the call to the correct 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();
});
}
};
Option number 2
Add conditional overloads SFINAE (you need a dummy type template parameter typename = void
if you want to hide enable_if
in the template parameter list in the second overload to make function template declarations great):
#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;
};
Option number 3
Use a return type with a qualifier decltype()
to enable / disable overloading:
#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;
};
source to share