I would like to check whether a function with the given signature exists. The twist is that the signature is given by the tuple:
#include <boost/hana/type.hpp>
#include <boost/hana/tuple.hpp>
#include <tuple>
namespace hana = boost::hana;
struct LibA {
static int foo(int, float, double) { return 1;}
};
struct LibB {
static int bar(float, float, float) { return 2;}
};
template<typename Lib, typename... Args>
int call(Lib, hana::tuple<Args...> args) {
auto hasFoo = hana::is_valid(hana::unpack)(args, Lib::foo);
auto hasBar = hana::is_valid(hana::unpack)(args, Lib::bar);
static_assert(hana::if_(hasFoo || hasBar, true, false), "Cannot find both positional and named version of the functor.");
return(hana::if_(hasFoo,
hana::unpack(args, Lib::foo),
hana::unpack(args, Lib::bar)
));
}
int main() {
int i; float f; double d;
call(LibA(), hana::make_tuple(i, f, d)); //compile error: 'bar' is not a member of LibA
call(LibB(), hana::make_tuple(f, f, f)); //compile error: 'foo' is not a member of libB
}
The hana::is_valid
returns a compile error rather than handle it as it is supposed to. I understand it is caused by the indirection - it only validates the call to the hana::unpack
, which is ok, but not the nested call to the Lib::foo
. Is there a walkaround for this problem?
When you refer inside the function template definition to Lib::x
that is not an immediate template expansion context, so SFINAE doesn't apply there and hence you get a compiler error.
Only invalid types and expressions in the immediate context of the function type and its template parameter types can result in a deduction failure.
An alternative working solution:
struct LibA {
static int foo(int, float, double) { return 1; }
};
struct LibB {
static int bar(float, float, float) { return 2; }
};
template<typename Lib, typename... Args>
auto call(Lib, hana::tuple<Args...> args) -> decltype(hana::unpack(args, Lib::foo)) {
return hana::unpack(args, Lib::foo);
}
template<typename Lib, typename... Args>
auto call(Lib, hana::tuple<Args...> args) -> decltype(hana::unpack(args, Lib::bar)) {
return hana::unpack(args, Lib::bar);
}
int main() {
int i; float f; double d;
call(LibA(), hana::make_tuple(i, f, d));
call(LibB(), hana::make_tuple(f, f, f));
}
In the above, that trailing return type with decltype
is an immediate template expansion context and SFINAE works as expected there.