Detecting whether or not a callable object is binary (including generic constrained lambdas)

91 Views Asked by At

I'm trying to detect whether or not a callable object is binary (i.e. if its operator() takes two arguments). I want to perform this check on lambdas, including generic lambdas and constrained generic lambdas (e.g. with trailing std::enable_if_t return type).


NOTE: this is not a duplicate of "arity of a generic lambda". I only care about checking whether or not a generic lambda is binary, and I can already do that for unconstrained generic lambdas or generic lambdas that do not use the arguments in their body.


My current approach consists in applying one of the techniques described in Kris Jusiak's Boost.DI C++Now 2015 talk: any_type. It basically is a class which can be implicitly converted to any other class.

struct any_type
{
    template <typename T>
    constexpr operator T() const noexcept
    {
        return {};
    }
};

After defining any_type, I'm using the detection idiom to check if a particular callable object can be invoked with two arguments:

template<class T>
using is_binary_callable_impl = decltype(std::declval<T>()(any_type{}, any_type{}));

template <typename T>
using is_binary_callable = std::experimental::is_detected<is_binary_callable_impl, T>;

This approach works well for both non-generic and generic lambdas...

auto unary_nongeneric = [](int){};
auto unary_generic = [](auto){};
auto binary_nongeneric = [](int, float){};
auto binary_generic = [](auto, auto){};

static_assert(!is_binary_callable<decltype(unary_nongeneric)>{});
static_assert(!is_binary_callable<decltype(unary_generic)>{});
static_assert(is_binary_callable<decltype(binary_nongeneric)>{});
static_assert(is_binary_callable<decltype(binary_generic)>{});

...but horribly fails when the argument is accessed with an interface that is not supported by any_type or when the lambda is constrained:

auto binary_generic_constrained = [](auto, auto x) 
    -> std::enable_if_t<std::is_arithmetic<std::decay_t<decltype(x)>>{}> {};

// Fails!
static_assert(is_binary_callable<decltype(binary_generic_constrained)>{});

error: static assertion failed

auto binary_generic_body = [](auto, auto x){ x.something(); };

// Compilation error!
static_assert(is_binary_callable<decltype(binary_generic_constrained)>{});

error: 'struct any_type' has no member named 'something'

The full code is on available here (on wandbox).


Assuming I'm on the right track with any_type, is there any way to ignore the lambda's return type and the lambda's body? In more detail:

  • Is it possible to check whether or not a generic constrained lambda is binary?

  • Is it possible to check whether or not a generic lambda that tries to access a specific member of its arguments is binary?

Otherwise, is there any other approach that could work here?

0

There are 0 best solutions below