EDIT: The approach outlined in the question is problematic for a few reasons. In the end I solved this by going about it a different way, see my answer below.
I have some template classes where the template parameter is expected to be a callable matching a certain signature. If the user supplies a template argument which is either not callable or does not match the expected signature then compilation fails deep inside the callback machinery and the resulting error messages are very hard to decipher. Instead, I'd like to be able to use static_assert
to provide a nice, easy to understand error message up front if the given template parameter is invalid. Unfortunately, this seems rather difficult to do.
I'm using the following setup:
#include <type_traits>
namespace detail {
template <typename Function, typename Sig>
struct check_function
{
static constexpr bool value =
std::is_convertible<Function, typename std::decay<Sig>::type>::value;
};
template <typename, typename>
struct check_functor;
template <typename Functor, typename Ret, typename... Args>
struct check_functor<Functor, Ret(Args...)>
{
typedef Ret (Functor::*Memfn) (Args...);
static constexpr bool value =
check_function<decltype(&Functor::operator()), Memfn>::value;
};
} // end namespace detail
template <typename Func, typename Sig>
struct check_function_signature
{
using Type =
typename std::conditional<
std::is_function<Func>::value,
detail::check_function<Func, Sig>,
detail::check_functor<Func, Sig>>::type;
static constexpr bool value = Type::value;
};
i.e. if Func
is a function pointer type, it is directly compared to the required signature, and otherwise it is assumed to be a functor and its operator()
is compared instead.
This seems to work for simple functions and user-defined functors, but for some reason I can't understand it fails for lambdas (tested with Clang 3.4 and g++ 4.8):
int f(int i);
struct g
{
int operator() (int i) { return i; }
};
int main()
{
static_assert(check_function_signature<decltype(f), int(int)>::value,
"Fine");
static_assert(check_function_signature<g, int(int)>::value,
"Also fine");
auto l = [](int i) { return i; };
static_assert(check_function_signature<decltype(l), int(int)>::value,
"Fails");
}
My understanding is that the standard requires that lambdas are implemented as anonymous functors equivalent to g
above, so I can't understand why the former works but the latter does not.
So, in summary, my questions:
- Is the approach I've used here actually reasonable, or have I made an obvious mistake?
- Why does this seem to work for user-defined functors but fail for compiler-defined ones (i.e. lambdas)?
- Is there a fix/workaround so that lambdas can be checked in this manner?
- Are there any other improvements I could make to this code? (Probably lots...)
Thanks in advance, this is pushing the limits of my template metaprogramming knowledge so any advice would be gratefully received.