Last active
September 27, 2016 03:57
-
-
Save yayj/56d45fdbbafe5ddb78ee08ca11e86456 to your computer and use it in GitHub Desktop.
Statically checking function signature in C++14
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
_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)>(); } | |
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#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