]> de.git.xonotic.org Git - xonotic/netradiant.git/blob - libs/generic/functional.h
Embrace variadic templates
[xonotic/netradiant.git] / libs / generic / functional.h
1 #if !defined( INCLUDED_FUNCTIONAL_H )
2 #define INCLUDED_FUNCTIONAL_H
3
4 #include <tuple>
5
6 namespace detail {
7     template<class F>
8     struct Fn;
9
10     template<class R, class... Ts>
11     struct Fn<R(Ts...)> {
12         using result_type = R;
13
14         template<int N>
15         using get = typename std::tuple_element<N, std::tuple<Ts...>>::type;
16     };
17 }
18
19 template<class Caller>
20 using get_result_type = typename detail::Fn<typename Caller::func>::result_type;
21
22 template<class Caller, int N>
23 using get_argument = typename detail::Fn<typename Caller::func>::template get<N>;
24
25 template<class Object, class F>
26 class MemberN;
27
28 template<class Object, class R, class... Ts>
29 class MemberN<Object, R(Ts...)> {
30 public:
31     template<R(Object::*f)(Ts...)>
32     class instance {
33     public:
34         using func = R(Object &, Ts...);
35
36         static R call(Object &object, Ts... args) {
37             return (object.*f)(args...);
38         }
39     };
40 };
41
42 template<class Object, class F>
43 class ConstMemberN;
44
45 template<class Object, class R, class... Ts>
46 class ConstMemberN<Object, R(Ts...)> {
47 public:
48     template<R(Object::*f)(Ts...) const>
49     class instance {
50     public:
51         using func = R(const Object &, Ts...);
52
53         static R call(const Object &object, Ts... args) {
54             return (object.*f)(args...);
55         }
56     };
57 };
58
59 template<class F>
60 class FunctionN;
61
62 template<class R, class... Ts>
63 class FunctionN<R(Ts...)> {
64 public:
65     template<R(*f)(Ts...)>
66     class instance {
67     public:
68         using func = R(Ts...);
69
70         static R call(Ts... args) {
71             return (f)(args...);
72         }
73     };
74 };
75
76 template<class Caller, class F>
77 class CallerShiftFirst;
78
79 template<class Caller, class R, class FirstArgument, class... Ts>
80 class CallerShiftFirst<Caller, R(FirstArgument, Ts...)> {
81 public:
82     using func = R(FirstArgument, Ts...);
83
84     static R call(FirstArgument, Ts... args) {
85         return Caller::call(args...);
86     }
87 };
88
89 template<class Functor, class F>
90 class FunctorNInvoke;
91
92 namespace detail {
93     template<int ...>
94     struct seq {
95     };
96
97     template<int N, int... S>
98     struct gens : gens<N - 1, N - 1, S...> {
99     };
100
101     template<int... S>
102     struct gens<0, S...> {
103         using type = seq<S...>;
104     };
105
106     template<int N>
107     using seq_new = typename gens<N>::type;
108 }
109
110 template<class Functor, class R, class... Ts>
111 class FunctorNInvoke<Functor, R(Ts...)> {
112     std::tuple<Ts...> args;
113
114     template<class T>
115     struct caller;
116
117     template<int ...I>
118     struct caller<detail::seq<I...>> {
119         static inline R call(FunctorNInvoke<Functor, R(Ts...)> *self, Functor functor) {
120             (void) self;
121             return functor(std::get<I>(self->args)...);
122         }
123     };
124
125 public:
126     FunctorNInvoke(Ts... args) : args(args...) {
127     }
128
129     inline R operator()(Functor functor) {
130         return caller<detail::seq_new<sizeof...(Ts)>>::call(this, functor);
131     }
132 };
133
134 template<class Functor>
135 using FunctorInvoke = FunctorNInvoke<Functor, typename Functor::func>;
136
137 template<class Object, class R, R(Object::*member)()>
138 using Member = typename MemberN<Object, R()>::template instance<member>;
139
140 template<class Object, class R, R(Object::*member)() const>
141 using ConstMember = typename ConstMemberN<Object, R()>::template instance<member>;
142
143 template<class Object, class A1, class R, R(Object::*member)(A1)>
144 using Member1 = typename MemberN<Object, R(A1)>::template instance<member>;
145
146 template<class Object, class A1, class R, R(Object::*member)(A1) const>
147 using ConstMember1 = typename ConstMemberN<Object, R(A1)>::template instance<member>;
148
149 template<class Object, class A1, class A2, class R, R(Object::*member)(A1, A2)>
150 using Member2 = typename MemberN<Object, R(A1, A2)>::template instance<member>;
151
152 template<class Object, class A1, class A2, class R, R(Object::*member)(A1, A2) const>
153 using ConstMember2 = typename ConstMemberN<Object, R(A1, A2)>::template instance<member>;
154
155 template<class Object, class A1, class A2, class A3, class R, R(Object::*member)(A1, A2, A3)>
156 using Member3 = typename MemberN<Object, R(A1, A2, A3)>::template instance<member>;
157
158 template<class Object, class A1, class A2, class A3, class R, R(Object::*member)(A1, A2, A3) const>
159 using ConstMember3 = typename ConstMemberN<Object, R(A1, A2, A3)>::template instance<member>;
160
161 template<class R, R(*func)()>
162 using Function0 = typename FunctionN<R()>::template instance<func>;
163
164 template<class A1, class R, R(*func)(A1)>
165 using Function1 = typename FunctionN<R(A1)>::template instance<func>;
166
167 template<class A1, class A2, class R, R(*func)(A1, A2)>
168 using Function2 = typename FunctionN<R(A1, A2)>::template instance<func>;
169
170 template<class A1, class A2, class A3, class R, R(*func)(A1, A2, A3)>
171 using Function3 = typename FunctionN<R(A1, A2, A3)>::template instance<func>;
172
173 template<class A1, class A2, class A3, class A4, class R, R(*func)(A1, A2, A3, A4)>
174 using Function4 = typename FunctionN<R(A1, A2, A3, A4)>::template instance<func>;
175
176 template<class Caller, class FirstArgument = void *>
177 using Caller0To1 = CallerShiftFirst<Caller, get_result_type<Caller>(
178         FirstArgument
179 )>;
180
181 template<class Caller, class FirstArgument = void *>
182 using Caller1To2 = CallerShiftFirst<Caller, get_result_type<Caller>(
183         FirstArgument,
184         get_argument<Caller, 0>
185 )>;
186
187 template<class Caller, class FirstArgument = void *>
188 using Caller2To3 = CallerShiftFirst<Caller, get_result_type<Caller>(
189         FirstArgument,
190         get_argument<Caller, 0>,
191         get_argument<Caller, 1>
192 )>;
193
194 template<class Caller, class FirstArgument = void *>
195 using Caller3To4 = CallerShiftFirst<Caller, get_result_type<Caller>(
196         FirstArgument,
197         get_argument<Caller, 0>,
198         get_argument<Caller, 1>,
199         get_argument<Caller, 2>
200 )>;
201
202 #endif