diff options
author | Johannes Stoelp <johannes.stoelp@gmail.com> | 2024-10-04 20:10:05 +0200 |
---|---|---|
committer | Johannes Stoelp <johannes.stoelp@gmail.com> | 2024-10-04 20:17:06 +0200 |
commit | f838e7c4aaa04d5084f6922aa516a845b19e78e9 (patch) | |
tree | bbc68e7d9c059fc9593b55500ec59d37c593da5a | |
parent | 0bb7c1ccf2fef23cbe0e263614fc4050b96305ea (diff) | |
download | cpp-templates-f838e7c4aaa04d5084f6922aa516a845b19e78e9.tar.gz cpp-templates-f838e7c4aaa04d5084f6922aa516a845b19e78e9.zip |
fn: add simple fn signature checker example
-rw-r--r-- | fn_alias.cc | 32 | ||||
-rw-r--r-- | fn_signature_checker.cc | 110 |
2 files changed, 142 insertions, 0 deletions
diff --git a/fn_alias.cc b/fn_alias.cc new file mode 100644 index 0000000..2d9fd1d --- /dev/null +++ b/fn_alias.cc @@ -0,0 +1,32 @@ +#include <cassert> +#include <cstdio> + +// Function alias. + +void say() { + std::puts("say"); +} + +inline constexpr auto say_alias = say; + +// Template function alias. +// +// One drawback is that template type deducation does not work through the +// function pointer. One would need to write some macro wrapper. + +template <typename T, typename U> +auto add(T t, U u) -> decltype(t + u) { + return t + u; +} + +template <typename U> +inline constexpr auto add_int = add<int, U>; + +// Test me. + +int main() { + say(); + say_alias(); + + assert(add(2, 3) == add_int<int>(3, 2)); +} 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 |