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)?
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
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
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
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With