diff options
Diffstat (limited to 'fn_signature_checker.cc')
-rw-r--r-- | fn_signature_checker.cc | 110 |
1 files changed, 110 insertions, 0 deletions
diff --git a/fn_signature_checker.cc b/fn_signature_checker.cc new file mode 100644 index 0000000..985c719 --- /dev/null +++ b/fn_signature_checker.cc @@ -0,0 +1,110 @@ +#if 0 +set -xe +g++ -std=c++11 -fsyntax-only fn_signature_checker.cc || echo "EXPECTED FAILURE" +clang++ -std=c++11 -fsyntax-only fn_signature_checker.cc || echo "EXPECTED FAILURE" +g++ -std=c++20 -fsyntax-only fn_signature_checker.cc || echo "EXPECTED FAILURE" +clang++ -std=c++20 -fsyntax-only fn_signature_checker.cc || echo "EXPECTED FAILURE" +exit 0 +#endif + +#include <type_traits> + +// Function signature checker for generic functions. +// +// This demonstrates a way to do some simple function signature checking on +// template arguments w/o implementing the whole concept emulation with +// std::void_t prior to cpp20. This may not be the best solution, but it gives a +// small tool at hand which can easily be employed into older cpp code bases. +// +// Starting with cpp20, one can fully switch to concepts for this task. + +// -- PRE CPP20 ---------------------------------------------------------------- + +// (1) Primary template to extract the function signature type. +template <typename Fn> +struct fn; + +// (2) Partial specialization for function types. +template <typename Ret, typename... Args> +struct fn<Ret(Args...)> { + using type = Ret(Args...); +}; + +// (3) Partial specialization for function pointer types. +// +// This specialization is disabled on purpose as it allows for writing ambiguous +// checks for either non-static or static member functions. +// +// template <typename Ret, typename... Args> +// struct fn<Ret (*)(Args...)> { +// using type = Ret(Args...); +// }; + +// (4) Partial specialization for member function pointer types. +template <typename T, typename Ret, typename... Args> +struct fn<Ret (T::*)(Args...)> { + using type = Ret(Args...); +}; + +template <typename Fn> +using fn_t = typename fn<Fn>::type; + +template <typename T> +void do_work() { + // Check for non-static member function. + // + // If the partial specialization (3) is enabled, this could also match a + // static member function T::get. + static_assert(std::is_same<fn_t<int(int)>, fn_t<decltype(&T::get)>>::value, + "T has no member function 'int get(int)'"); + // Check for non-static member function. + static_assert(std::is_same<fn_t<void(int)>, fn_t<decltype(&T::set)>>::value, + "T has no member function 'void set(int)'"); + // Check for static member function. + static_assert(std::is_same<fn_t<int()>, fn_t<decltype(T::cnt)>>::value, + "T has no static function 'static int cnt()'"); +} + +struct good { + int get(int); + void set(int); + static int cnt(); +}; + +struct bad { + int get(int); + int set(); + static int cnt(); +}; + +void check() { + do_work<good>(); + + // TODO: Enable to see a failing check due to wrong function signature. + // do_work<bad>(); +} + +// -- SINCE CPP20 -------------------------------------------------------------- + +#if __cplusplus >= 202002L +#include <concepts> + +template <typename T> +concept MyType = requires(T t) { + { t.get(int{}) } -> std::same_as<int>; + { t.set(int{}) } -> std::same_as<void>; + { T::cnt() } -> std::same_as<int>; +}; + +template <MyType T> +void do_work2() { +} + +void check2() { + do_work2<good>(); + + // TODO: Enable to see failing to satisfy the concept MyType. + // do_work2<bad>(); +} + +#endif |