Skip to content

Instantly share code, notes, and snippets.

@yayj
Last active September 27, 2016 03:57
Show Gist options
  • Save yayj/56d45fdbbafe5ddb78ee08ca11e86456 to your computer and use it in GitHub Desktop.
Save yayj/56d45fdbbafe5ddb78ee08ca11e86456 to your computer and use it in GitHub Desktop.
Statically checking function signature in C++14
_Pragma("once");
/*
* Usage:
* 1. static_assert(FunctionConcept<ReturnType, Args...>::check<FunctionType>(), "msg");
* 2. static_assert(FunctionConcept<ReturnType, Args...>::check(functionVariable), "msg");
*
* Supported:
* - Implicitly type conversion on arguments or return value(type in signature implicitly
* converted to type in actual function)
* - CR modifiers on arguments or return value(volatile is not tested)
* - Implicitly checking copy/move constructors on type of arguments or return value
*/
#include <tuple>
#include <type_traits>
template <typename Func, typename Ret, typename... Args> struct FunctionChecker {
static constexpr auto fp = static_cast<std::remove_reference_t<Func>*>(nullptr);
template <typename... Params> static constexpr bool check(Params... params)
{
using R = decltype((*fp)(std::forward<Args>(*params)...));
return std::is_convertible<Ret, R>::value;
}
};
template <typename Func, typename Ret, std::size_t index, typename... Args>
struct ArgumentsIterator {
using Arg = std::tuple_element_t<index, std::tuple<Args...>>;
using NextIter = ArgumentsIterator<Func, Ret, index - 1, Args...>;
template <typename... Params> static constexpr bool next(Params... params)
{
return NextIter::next(static_cast<std::remove_reference_t<Arg>*>(nullptr), params...);
}
};
template <typename Func, typename Ret, typename... Args>
struct ArgumentsIterator<Func, Ret, 0, Args...> {
using Arg = std::tuple_element_t<0, std::tuple<Args...>>;
template <typename... Params> static constexpr bool next(Params... params)
{
return FunctionChecker<Func, Ret, Args...>::check(
static_cast<std::remove_reference_t<Arg>*>(nullptr), params...);
}
};
template <typename Ret, typename... Args> struct FunctionConcept {
template <typename Func> static constexpr bool check()
{
return ArgumentsIterator<Func, Ret, sizeof...(Args)-1, Args...>::next();
}
template <typename Func> static constexpr bool check(Func&& f) { return check<decltype(f)>(); }
};
template <typename Ret> struct FunctionConcept<Ret> {
template <typename Func> static constexpr bool check()
{
return FunctionChecker<Func, Ret>::check();
}
template <typename Func> static constexpr bool check(Func&& f) { return check<decltype(f)>(); }
};
#include "FunctionConcept.hpp"
#include <functional>
#include <memory>
struct Pod {
};
struct Base {
};
struct Derived : public Base {
};
struct Functor {
Functor() = default;
Functor(Functor const&) = delete;
Functor(Functor&&) = default;
void operator()() {}
void operator()(std::int8_t, std::int16_t) {}
};
extern bool unaryPredicate(int);
void checkRawFunction()
{
static_assert(FunctionConcept<bool, int>::check(&unaryPredicate), "Function concept not met");
}
void checkBind()
{
auto bind = std::bind(Functor{});
static_assert(FunctionConcept<void>::check(bind), "Function concept not met");
}
void checkStdFunction()
{
auto f = std::function<void()>{[] {}};
static_assert(FunctionConcept<void>::check(f), "Function concept not met");
}
void checkFunctor()
{
static_assert(FunctionConcept<void>::check(Functor{}), "Function concept not met");
static_assert(FunctionConcept<void, std::int8_t, std::int16_t>::check(Functor{}),
"Function concept not met");
}
void checkLambda()
{
auto generator = [] {};
auto unary = [](int) { return '0'; };
auto binary = [](long long, double) { return 0; };
static_assert(FunctionConcept<void>::check(generator), "Function concept not met");
static_assert(FunctionConcept<char, int>::check(unary), "Function concept not met");
static_assert(FunctionConcept<int, long long, double>::check(binary), "Function concept not met");
}
void checkPrimitiveParamConversion()
{
auto raw = [](int) {};
static_assert(FunctionConcept<void, int>::check(raw), "Function concept not met");
static_assert(FunctionConcept<void, int const>::check(raw), "Function concept not met");
static_assert(FunctionConcept<void, int&>::check(raw), "Function concept not met");
static_assert(FunctionConcept<void, int const&>::check(raw), "Function concept not met");
static_assert(FunctionConcept<void, int&&>::check(raw), "Function concept not met");
static_assert(FunctionConcept<void, int const&&>::check(raw), "Function concept not met");
auto lr = [](int&) {};
static_assert(FunctionConcept<void, int&>::check(lr), "Function concept not met");
auto c = [](int const) {};
static_assert(FunctionConcept<void, int>::check(c), "Function concept not met");
static_assert(FunctionConcept<void, int const>::check(c), "Function concept not met");
static_assert(FunctionConcept<void, int&>::check(c), "Function concept not met");
static_assert(FunctionConcept<void, int const&>::check(c), "Function concept not met");
static_assert(FunctionConcept<void, int&&>::check(c), "Function concept not met");
static_assert(FunctionConcept<void, int const&&>::check(c), "Function concept not met");
auto clr = [](int const&) {};
static_assert(FunctionConcept<void, int>::check(clr), "Function concept not met");
static_assert(FunctionConcept<void, int const>::check(clr), "Function concept not met");
static_assert(FunctionConcept<void, int&>::check(clr), "Function concept not met");
static_assert(FunctionConcept<void, int const&>::check(clr), "Function concept not met");
static_assert(FunctionConcept<void, int&&>::check(clr), "Function concept not met");
static_assert(FunctionConcept<void, int const&&>::check(clr), "Function concept not met");
auto rr = [](int&&) {};
static_assert(FunctionConcept<void, int>::check(rr), "Function concept not met");
static_assert(FunctionConcept<void, int&&>::check(rr), "Function concept not met");
auto crr = [](int const&&) {};
static_assert(FunctionConcept<void, int>::check(crr), "Function concept not met");
static_assert(FunctionConcept<void, int const>::check(crr), "Function concept not met");
static_assert(FunctionConcept<void, int&&>::check(crr), "Function concept not met");
static_assert(FunctionConcept<void, int const&&>::check(crr), "Function concept not met");
}
void checkPodParamConversion()
{
auto raw = [](Pod) {};
static_assert(FunctionConcept<void, Pod>::check(raw), "Function concept not met");
static_assert(FunctionConcept<void, Pod const>::check(raw), "Function concept not met");
static_assert(FunctionConcept<void, Pod&>::check(raw), "Function concept not met");
static_assert(FunctionConcept<void, Pod const&>::check(raw), "Function concept not met");
static_assert(FunctionConcept<void, Pod&&>::check(raw), "Function concept not met");
static_assert(FunctionConcept<void, Pod const&&>::check(raw), "Function concept not met");
auto lr = [](Pod&) {};
static_assert(FunctionConcept<void, Pod&>::check(lr), "Function concept not met");
auto c = [](Pod const) {};
static_assert(FunctionConcept<void, Pod>::check(c), "Function concept not met");
static_assert(FunctionConcept<void, Pod const>::check(c), "Function concept not met");
static_assert(FunctionConcept<void, Pod&>::check(c), "Function concept not met");
static_assert(FunctionConcept<void, Pod const&>::check(c), "Function concept not met");
static_assert(FunctionConcept<void, Pod&&>::check(c), "Function concept not met");
static_assert(FunctionConcept<void, Pod const&&>::check(c), "Function concept not met");
auto clr = [](Pod const&) {};
static_assert(FunctionConcept<void, Pod>::check(clr), "Function concept not met");
static_assert(FunctionConcept<void, Pod const>::check(clr), "Function concept not met");
static_assert(FunctionConcept<void, Pod&>::check(clr), "Function concept not met");
static_assert(FunctionConcept<void, Pod const&>::check(clr), "Function concept not met");
static_assert(FunctionConcept<void, Pod&&>::check(clr), "Function concept not met");
static_assert(FunctionConcept<void, Pod const&&>::check(clr), "Function concept not met");
auto rr = [](Pod&&) {};
static_assert(FunctionConcept<void, Pod>::check(rr), "Function concept not met");
static_assert(FunctionConcept<void, Pod&&>::check(rr), "Function concept not met");
auto crr = [](Pod const&&) {};
static_assert(FunctionConcept<void, Pod>::check(crr), "Function concept not met");
static_assert(FunctionConcept<void, Pod const>::check(crr), "Function concept not met");
static_assert(FunctionConcept<void, Pod&&>::check(crr), "Function concept not met");
static_assert(FunctionConcept<void, Pod const&&>::check(crr), "Function concept not met");
}
void checkRetConversion()
{
auto pod = Pod{};
auto raw = [] { return Pod{}; };
static_assert(FunctionConcept<Pod>::check(raw), "Function concept not met");
auto c = []() -> Pod const { return Pod{}; };
static_assert(FunctionConcept<Pod const>::check(c), "Function concept not met");
auto lr = [&]() -> Pod& { return pod; };
static_assert(FunctionConcept<Pod&>::check(lr), "Function concept not met");
auto clr = [&]() -> Pod const& { return pod; };
static_assert(FunctionConcept<Pod const&>::check(clr), "Function concept not met");
auto rr = [&]() -> Pod&& { return std::move(pod); };
static_assert(FunctionConcept<Pod&&>::check(rr), "Function concept not met");
auto crr = [&]() -> Pod const&& { return std::move(pod); };
static_assert(FunctionConcept<Pod const&&>::check(crr), "Function concept not met");
}
void checkImplicitConversion()
{
auto f = [](long) { return 1l; };
static_assert(FunctionConcept<short, short>::check(f), "Function concept not met");
}
void checkDynamicCasting()
{
auto unique = [p = std::make_unique<Base>()](std::unique_ptr<Base>) mutable
{
return std::move(p);
};
static_assert(FunctionConcept<std::unique_ptr<Derived>, std::unique_ptr<Derived>>::check(unique),
"Function concept not met");
auto shared = [p = std::make_shared<Base>()](std::shared_ptr<Base>) { return p; };
static_assert(FunctionConcept<std::shared_ptr<Derived>, std::shared_ptr<Derived>>::check(shared),
"Function concept not met");
auto weak = [p = std::make_shared<Base>()](std::weak_ptr<Base>)
{
return std::weak_ptr<Base>(p);
};
static_assert(FunctionConcept<std::weak_ptr<Derived>, std::weak_ptr<Derived>>::check(weak),
"Function concept not met");
auto raw = [](Base*) -> Base* { return nullptr; };
static_assert(FunctionConcept<Derived*, Derived*>::check(raw), "Function concept not met");
}
int main(int argc, char const* argv[])
{
// Empty
return 0;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment