1 // Copyright 2018 The Fuchsia Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #ifndef LIB_FIT_TRAITS_H_
6 #define LIB_FIT_TRAITS_H_
7 
8 #include <tuple>
9 #include <type_traits>
10 
11 namespace fit {
12 
13 // C++ 14 compatible implementation of std::void_t.
14 #if defined(__cplusplus) && __cplusplus >= 201703L
15 template <typename... T>
16 using void_t = std::void_t<T...>;
17 #else
18 template <typename... T>
19 struct make_void { typedef void type; };
20 template <typename... T>
21 using void_t = typename make_void<T...>::type;
22 #endif
23 
24 // Encapsulates capture of a parameter pack. Typical use is to use instances of this empty struct
25 // for type dispatch in function template deduction/overload resolution.
26 //
27 // Example:
28 //  template <typename Callable, typename... Args>
29 //  auto inspect_args(Callable c, parameter_pack<Args...>) {
30 //      // do something with Args...
31 //  }
32 //
33 //  template <typename Callable>
34 //  auto inspect_args(Callable c) {
35 //      return inspect_args(std::move(c), typename callable_traits<Callable>::args{});
36 //  }
37 template <typename... T>
38 struct parameter_pack {
39     static constexpr size_t size = sizeof...(T);
40 
41     template <size_t i>
42     using at = typename std::tuple_element_t<i, std::tuple<T...>>;
43 };
44 
45 // |callable_traits| captures elements of interest from function-like types (functions, function
46 // pointers, and functors, including lambdas). Due to common usage patterns, const and non-const
47 // functors are treated identically.
48 //
49 // Member types:
50 //  |args|        - a |parameter_pack| that captures the parameter types of the function. See
51 //                  |parameter_pack| for usage and details.
52 //  |return_type| - the return type of the function.
53 //  |type|        - the underlying functor or function pointer type. This member is absent if
54 //                  |callable_traits| are requested for a raw function signature (as opposed to a
55 //                  function pointer or functor; e.g. |callable_traits<void()>|).
56 //  |signature|   - the type of the equivalent function.
57 
58 template <typename T>
59 struct callable_traits : public callable_traits<decltype(&T::operator())> {};
60 
61 // Treat mutable call operators the same as const call operators.
62 //
63 // It would be equivalent to erase the const instead, but the common case is lambdas, which are
64 // const, so prefer to nest less deeply for the common const case.
65 template <typename FunctorType, typename ReturnType, typename... ArgTypes>
66 struct callable_traits<ReturnType (FunctorType::*)(ArgTypes...)>
67     : public callable_traits<ReturnType (FunctorType::*)(ArgTypes...) const> {};
68 
69 // Common functor specialization.
70 template <typename FunctorType, typename ReturnType, typename... ArgTypes>
71 struct callable_traits<ReturnType (FunctorType::*)(ArgTypes...) const>
72     : public callable_traits<ReturnType (*)(ArgTypes...)> {
73 
74     using type = FunctorType;
75 };
76 
77 // Function pointer specialization.
78 template <typename ReturnType, typename... ArgTypes>
79 struct callable_traits<ReturnType (*)(ArgTypes...)>
80     : public callable_traits<ReturnType(ArgTypes...)> {
81 
82     using type = ReturnType (*)(ArgTypes...);
83 };
84 
85 // Base specialization.
86 template <typename ReturnType, typename... ArgTypes>
87 struct callable_traits<ReturnType(ArgTypes...)> {
88     using signature = ReturnType(ArgTypes...);
89     using return_type = ReturnType;
90     using args = parameter_pack<ArgTypes...>;
91 
92     callable_traits() = delete;
93 };
94 
95 // Determines whether a type has an operator() that can be invoked.
96 template <typename T, typename = void_t<>>
97 struct is_callable : public std::false_type {};
98 template <typename ReturnType, typename... ArgTypes>
99 struct is_callable<ReturnType (*)(ArgTypes...)>
100     : public std::true_type {};
101 template <typename FunctorType, typename ReturnType, typename... ArgTypes>
102 struct is_callable<ReturnType (FunctorType::*)(ArgTypes...)>
103     : public std::true_type {};
104 template <typename T>
105 struct is_callable<T, void_t<decltype(&T::operator())>>
106     : public std::true_type {};
107 
108 } // namespace fit
109 
110 #endif // LIB_FIT_TRAITS_H_
111