#include <functional>  // std::function, std::bind (for example)
#include <iostream>    // std::cout, std::endl, std::boolalpha (for example)
#include <utility>     // std::declval (for example)
#include <type_traits> // std::remove_reference_t, std::true_type, std::false_type (for implementation)

// is_callable IMPLEMENTATION

template<typename T, typename U = void>
struct is_callable
{
	static bool const constexpr value = std::conditional_t<
		std::is_class<std::remove_reference_t<T>>::value,
		is_callable<std::remove_reference_t<T>, int>, std::false_type>::value;
};

template<typename T, typename U, typename ...Args>
struct is_callable<T(Args...), U> : std::true_type {};
template<typename T, typename U, typename ...Args>
struct is_callable<T(*)(Args...), U> : std::true_type {};
template<typename T, typename U, typename ...Args>
struct is_callable<T(&)(Args...), U> : std::true_type {};
template<typename T, typename U, typename ...Args>
struct is_callable<T(Args......), U> : std::true_type {};
template<typename T, typename U, typename ...Args>
struct is_callable<T(*)(Args......), U> : std::true_type {};
template<typename T, typename U, typename ...Args>
struct is_callable<T(&)(Args......), U> : std::true_type {};
template<typename T, typename U, typename ...Args>
struct is_callable<T(Args...)const, U> : std::true_type {};
template<typename T, typename U, typename ...Args>
struct is_callable<T(Args...)volatile, U> : std::true_type {};
template<typename T, typename U, typename ...Args>
struct is_callable<T(Args...)const volatile, U> : std::true_type {};
template<typename T, typename U, typename ...Args>
struct is_callable<T(Args......)const, U> : std::true_type {};
template<typename T, typename U, typename ...Args>
struct is_callable<T(Args......)volatile, U> : std::true_type{};
template<typename T, typename U, typename ...Args>
struct is_callable<T(Args......)const volatile, U> : std::true_type {};
template<typename T, typename U, typename ...Args>
struct is_callable<T(Args...)&, U> : std::true_type {};
template<typename T, typename U, typename ...Args>
struct is_callable<T(Args...)const&, U> : std::true_type{};
template<typename T, typename U, typename ...Args>
struct is_callable<T(Args...)volatile&, U> : std::true_type{};
template<typename T, typename U, typename ...Args>
struct is_callable<T(Args...)const volatile&, U> : std::true_type{};
template<typename T, typename U, typename ...Args>
struct is_callable<T(Args......)&, U> : std::true_type {};
template<typename T, typename U, typename ...Args>
struct is_callable<T(Args......)const&, U> : std::true_type{};
template<typename T, typename U, typename ...Args>
struct is_callable<T(Args......)volatile&, U> : std::true_type{};
template<typename T, typename U, typename ...Args>
struct is_callable<T(Args......)const volatile&, U> : std::true_type{};
template<typename T, typename U, typename ...Args>
struct is_callable<T(Args...)&&, U> : std::true_type{};
template<typename T, typename U, typename ...Args>
struct is_callable<T(Args...)const&&, U> : std::true_type{};
template<typename T, typename U, typename ...Args>
struct is_callable<T(Args...)volatile&&, U> : std::true_type{};
template<typename T, typename U, typename ...Args>
struct is_callable<T(Args...)const volatile&&, U> : std::true_type{};
template<typename T, typename U, typename ...Args>
struct is_callable<T(Args......)&&, U> : std::true_type{};
template<typename T, typename U, typename ...Args>
struct is_callable<T(Args......)const&&, U> : std::true_type{};
template<typename T, typename U, typename ...Args>
struct is_callable<T(Args......)volatile&&, U> : std::true_type{};
template<typename T, typename U, typename ...Args>
struct is_callable<T(Args......)const volatile&&, U> : std::true_type{};

template<typename T>
struct is_callable<T, int>
{
private:
	using YesType = char(&)[1];
	using NoType = char(&)[2];
	
	struct Fallback { void operator()(); };
	
	struct Derived : T, Fallback {};
	
	template<typename U, U>
	struct Check;
	
	template<typename>
	static YesType Test(...);
	
	template<typename C>
	static NoType Test(Check<void (Fallback::*)(), &C::operator()>*);
	
public:
	static bool const constexpr value = sizeof(Test<Derived>(0)) == sizeof(YesType);
};

// END is_callable IMPLEMENTATION

struct Callable { void operator()() {} };

struct PrivateCallable
{
private:
	void operator()(int) {}
	double operator()(bool, char*) {}
	PrivateCallable(int, int) {}
};

struct NotCallable
{
	void mFoo() {}
	void mConstFoo() const {}
	int mInt;
};

int foo() {}

template<typename ...Args>
void bar(Args &&...args, ...) {}

enum WeakTypeEnum {};

enum class StrongTypeEnum {};

int main(int, char**) noexcept
{
	auto pfoo = &foo;
	int (&rfoo)() = foo;
	auto bfoo = std::bind(&foo);
	NotCallable nc;
	auto bmfoo = std::bind(&NotCallable::mFoo, &nc);
	auto bmcfoo = std::bind(&NotCallable::mConstFoo, &nc);
	auto lamb = [](char**) -> decltype(auto)
	{
		return "Hello World!";
	};
	std::cout << std::endl << std::boolalpha
		<< "SCALARS" << std::endl
		<< "int: " << is_callable<int>::value << std::endl
		<< "long double: " << is_callable<long double>::value << std::endl
		<< "int*: " << is_callable<int*>::value << std::endl
		<< "int const*: " << is_callable<int const*>::value << std::endl
		<< "int const *const: " << is_callable<int const *const>::value << std::endl
		<< "WeakTypeEnum: " << is_callable<WeakTypeEnum>::value << std::endl
		<< "StrongTypeEnum: " << is_callable<StrongTypeEnum>::value << std::endl
		<< "NotCallable::*: " << is_callable<decltype(&NotCallable::mInt)>::value << std::endl
		<< "nullptr_t: " << is_callable<decltype(nullptr)>::value << std::endl
		<< "Callable*: " << is_callable<Callable*>::value << std::endl
		<< std::endl
		<< "REFERENCES" << std::endl
		<< "int&: " << is_callable<int&>::value << std::endl
		<< "int const&: " << is_callable<int const&>::value << std::endl
		<< "NotCallable&: " << is_callable<NotCallable&>::value << std::endl
		<< "NotCallable const&: " << is_callable<NotCallable const&>::value << std::endl
		<< "Callable&: " << is_callable<Callable&>::value << std::endl
		<< "Callable const&: " << is_callable<Callable const&>::value << std::endl
		<< "function reference: " << is_callable<decltype(rfoo)>::value << std::endl
		<< std::endl
		<< "FUNCTIONS" << std::endl
		<< "function (no params): " << is_callable<int()>::value << std::endl
		<< "function (params): " << is_callable<double(char*)>::value << std::endl
		<< "function (const): " << is_callable<void(bool) const>::value << std::endl
		<< "function (ref qualifier): " << is_callable<void(bool)&>::value << std::endl
		<< "function (rvalue ref qualifier): " << is_callable<void(bool)&&>::value << std::endl
		<< "function (variadic): " << is_callable<void(...)>::value << std::endl
		<< "templated variadic function: " << is_callable<decltype(bar<>)>::value << std::endl
		<< "function pointer: " << is_callable<decltype(pfoo)>::value << std::endl
		<< "member function pointer: " << is_callable<decltype(&NotCallable::mFoo)>::value << std::endl
		<< "const member function pointer: " << is_callable<decltype(&NotCallable::mConstFoo)>::value << std::endl
		<< "bind expression (free function): " << is_callable<decltype(bfoo)>::value << std::endl
		<< "bind expression (member function): " << is_callable<decltype(bmfoo)>::value << std::endl
		<< "bind expression (const member function): " << is_callable<decltype(bmcfoo)>::value << std::endl
		<< "functor: " << is_callable<Callable>::value << std::endl
		<< "private functor: " << is_callable<PrivateCallable>::value << std::endl
		<< "std::function: " << is_callable<std::function<int()>>::value << std::endl
		<< "lambda: " << is_callable<decltype(lamb)>::value << std::endl;
	return 0;
}
