xref: /petsc/include/petsc/private/cpp/functional.hpp (revision f4d061e980d13bc62f06124c58b76593bdf99e72)
1 #ifndef PETSC_CPP_FUNCTIONAL_HPP
2 #define PETSC_CPP_FUNCTIONAL_HPP
3 
4 #if defined(__cplusplus)
5 #include <petsc/private/cpp/macros.hpp>
6 #include <petsc/private/cpp/utility.hpp>     // index_sequence
7 #include <petsc/private/cpp/type_traits.hpp> // decay_t
8 #include <petsc/private/cpp/tuple.hpp>       // tuple_element_t
9 
10 #include <functional>
11 
12 namespace Petsc {
13 
14 namespace util {
15 
16 namespace detail {
17 
18 struct can_call_test {
19   template <typename F, typename... A>
20   static decltype(std::declval<F>()(std::declval<A>()...), std::true_type()) f(int);
21 
22   template <typename F, typename... A>
23   static std::false_type f(...);
24 };
25 
26 // generic template
27 template <typename T>
28 struct func_traits_impl : func_traits_impl<decltype(&T::operator())> { };
29 
30 // function pointers
31 template <typename Ret, typename... Args>
32 struct func_traits_impl<Ret (*)(Args...)> {
33   using result_type = Ret;
34 
35   template <std::size_t ix>
36   struct arg {
37     using type = util::tuple_element_t<ix, std::tuple<Args...>>;
38   };
39 };
40 
41 // class-like operator()
42 template <typename C, typename Ret, typename... Args>
43 struct func_traits_impl<Ret (C::*)(Args...) const> {
44   using result_type = Ret;
45 
46   template <std::size_t ix>
47   struct arg {
48     using type = util::tuple_element_t<ix, std::tuple<Args...>>;
49   };
50 };
51 
52 template <typename C, typename Ret, typename... Args>
53 struct func_traits_impl<Ret (C::*)(Args...)> {
54   using result_type = Ret;
55 
56   template <std::size_t ix>
57   struct arg {
58     using type = util::tuple_element_t<ix, std::tuple<Args...>>;
59   };
60 };
61 
62 } // namespace detail
63 
64 template <typename F, typename... A>
65 struct can_call : decltype(detail::can_call_test::f<F, A...>(0)) { };
66 
67 template <typename... A, typename F>
68 inline constexpr can_call<F, A...> is_callable_with(F &&) noexcept {
69   return can_call<F, A...>{};
70 }
71 
72 template <typename T>
73 struct func_traits : detail::func_traits_impl<decay_t<T>> {
74   template <std::size_t idx>
75   using arg_t = typename detail::func_traits_impl<decay_t<T>>::template arg<idx>::type;
76 };
77 
78 } // namespace util
79 
80 } // namespace Petsc
81 
82 #define PETSC_ALIAS_FUNCTION_WITH_PROLOGUE_AND_EPILOGUE_(alias, original, dispatch, prologue, epilogue) \
83   template <typename... Args> \
84   static inline auto dispatch(int, Args &&...args) PETSC_DECLTYPE_NOEXCEPT_AUTO_RETURNS(original(std::forward<Args>(args)...)) template <typename... Args> \
85   static inline int  dispatch(char, Args...) { \
86      using namespace Petsc::util; \
87      static_assert(is_callable_with<Args...>(original) && always_false<Args...>::value, "function " PetscStringize(original) "() is not callable with given arguments"); \
88      return EXIT_FAILURE; \
89   } \
90   template <typename... Args> \
91   PETSC_NODISCARD auto alias(Args &&...args) PETSC_DECLTYPE_NOEXCEPT_AUTO(dispatch(0, std::forward<Args>(args)...)) { \
92     prologue; \
93     auto ret = dispatch(0, std::forward<Args>(args)...); \
94     epilogue; \
95     return ret; \
96   }
97 
98 #define PETSC_ALIAS_FUNCTION_(alias, original, dispatch) PETSC_ALIAS_FUNCTION_WITH_PROLOGUE_AND_EPILOGUE_(alias, original, dispatch, ((void)0), ((void)0))
99 
100 #ifndef PetscConcat5
101 #define PetscConcat5_(a, b, c, d, e) a##b##c##d##e
102 #define PetscConcat5(a, b, c, d, e)  PetscConcat5_(a, b, c, d, e)
103 #endif
104 
105 // makes prefix_lineno_name
106 #define PETSC_ALIAS_UNIQUE_NAME_INTERNAL(prefix, name) PetscConcat5(prefix, _, __LINE__, _, name)
107 
108 // PETSC_ALIAS_FUNCTION() - Alias a function
109 //
110 // input params:
111 // alias    - the new name for the function
112 // original - the name of the function you would like to alias
113 //
114 // notes:
115 // Using this macro in effect creates
116 //
117 // template <typename... T>
118 // auto alias(T&&... args)
119 // {
120 //   return original(std::forward<T>(args)...);
121 // }
122 //
123 // meaning it will transparently work for any kind of alias (including overloads).
124 //
125 // example usage:
126 // PETSC_ALIAS_FUNCTION(bar,foo);
127 #define PETSC_ALIAS_FUNCTION(alias, original) PETSC_ALIAS_FUNCTION_(alias, original, PETSC_ALIAS_UNIQUE_NAME_INTERNAL(PetscAliasFunctionDispatch, original))
128 
129 // Similar to PETSC_ALIAS_FUNCTION() this macro creates a thin wrapper which passes all
130 // arguments to the target function ~except~ the last N arguments. So
131 //
132 // PETSC_ALIAS_FUNCTION_GOBBLE_NTH_ARGS(bar,foo,3);
133 //
134 // creates a function with the effect of
135 //
136 // returnType bar(argType1 arg1, argType2 arg2, ..., argTypeN argN)
137 // {
138 //   IGNORE(argN);
139 //   IGNORE(argN-1);
140 //   IGNORE(argN-2);
141 //   return foo(arg1,arg2,...,argN-3);
142 // }
143 //
144 // for you.
145 #define PETSC_ALIAS_FUNCTION_GOBBLE_NTH_LAST_ARGS_(alias, original, gobblefn, N) \
146   static_assert(std::is_integral<decltype(N)>::value && ((N) >= 0), ""); \
147   template <typename TupleT, std::size_t... idx> \
148   static inline auto   gobblefn(TupleT &&tuple, Petsc::util::index_sequence<idx...>) PETSC_DECLTYPE_NOEXCEPT_AUTO_RETURNS(original(std::get<idx>(tuple)...)) template <typename... Args> \
149   PETSC_NODISCARD auto alias(Args &&...args) PETSC_DECLTYPE_NOEXCEPT_AUTO_RETURNS(gobblefn(std::forward_as_tuple(args...), Petsc::util::make_index_sequence<sizeof...(Args) - (N)>{}))
150 
151 #define PETSC_ALIAS_FUNCTION_GOBBLE_NTH_LAST_ARGS(alias, original, N) PETSC_ALIAS_FUNCTION_GOBBLE_NTH_LAST_ARGS_(alias, original, PETSC_ALIAS_UNIQUE_NAME_INTERNAL(PetscAliasFunctionGobbleDispatch, original), N)
152 
153 #endif // __cplusplus
154 
155 #endif // PETSC_CPP_FUNCTIONAL_HPP
156