Audacity 3.2.0
TypeSwitch.h
Go to the documentation of this file.
1/*!********************************************************************
2
3 Audacity: A Digital Audio Editor
4
5 @file TypeSwitch.h
6 @brief Dispatch to one of a set of functions by the run-time type of an object
7
8 Paul Licameli
9
10**********************************************************************/
11#ifndef __AUDACITY_TYPE_SWITCH__
12#define __AUDACITY_TYPE_SWITCH__
13
14#include "Callable.h"
15#include "Tuple.h"
16#include "TypeList.h"
17#include <cassert>
18
19namespace TypeSwitch {
20using namespace TypeList;
21
22namespace detail {
23
24template<typename... Functions> using FunctionTupleType =
25 std::tuple<const Functions &...>;
26template<typename Functions> using FunctionTuple =
28
30
34template<typename R, typename ArgumentTypes, typename Funs, typename... Args>
35struct Executor {
36 static_assert(!Null_v<ArgumentTypes>);
38
39 struct NoOp {
40 struct type {
42
44 enum : unsigned { SetUsed = 0 };
46 R operator ()(ArgumentType &, const Functions &, Args&&...) const {
47 if constexpr (std::is_void_v<R>)
48 return;
49 else
50 return R{};
51 }
52 };
53 };
54
55 template<typename Fs, typename Wrapped> struct Combine {
57 struct Transparent {
59 struct type {
60 using Next = typename Wrapped::type;
61
63 enum : unsigned { SetUsed = Next::SetUsed << 1 };
64
67 ArgumentType &object, const Functions &functions, Args&&... args)
68 const {
69 return Next{}(object, Tuple::ForwardNext(functions),
70 std::forward<Args>(args)...);
71 }
72 };
73 };
74
75 template<typename BaseClass, typename NextBase> struct CombineOp {
76 static_assert(
77 std::is_const_v<BaseClass> == std::is_const_v<ArgumentType>);
79 using Compatible = std::is_base_of<BaseClass, ArgumentType>;
80
83 struct Opaque {
85 enum : unsigned { SetUsed = 1u };
86
89 BaseClass &object, const Functions &functions, Args&&... args)
90 const {
91 return std::get<0>(functions)(
92 object, std::forward<Args>(args)...);
93 }
94 };
95
98 struct Wrapper {
99 using Next = typename Wrapped::type;
101 enum : unsigned { SetUsed = (Next::SetUsed << 1) | 1u };
102
106 BaseClass &object, const Functions &functions, Args&&... args)
107 const {
108 // The first function in the tuple is curried!
109 // Its first argument is the call-through and its second
110 // is the object
111 const auto next = [&](){ return
112 Next{}(object, Tuple::ForwardNext(functions),
113 std::forward<Args>(args)...); };
114 return std::get<0>(functions)(next)(
115 object, std::forward<Args>(args)...);
116 }
117 };
118
119 using F = Head_t<Fs>;
120
121 // whether Function looks like a generic callable
122 struct Dummy { R operator ()() const {
123 if constexpr(std::is_void_v<R>) return; else return R{};
124 } };
125 using curried = std::is_invocable<F, Dummy&&>;
126
127 // Case 1: Compatible, and invocable on the next function, giving
128 // another function, that accepts BaseClass:
129 struct Case1_;
130 using Case1 = std::conjunction<Compatible, curried, Case1_>;
131 struct Case1_ {
132 static constexpr bool value = std::is_invocable_v<
133 std::invoke_result_t<F, Dummy &&>, BaseClass&, Args&&...>;
134 using type = Wrapper;
135 };
136
137 // Case 2: Invocable directly on the object
138 struct Case2 : std::conjunction<
139 Compatible, std::negation<curried>,
140 std::is_invocable<F, BaseClass&, Args&&...>
141 > {
142 using type = Opaque;
143 };
144 struct Default : std::true_type {
145 using type = typename NextBase::type;
146 };
147 using type = typename std::disjunction<Case1, Case2, Default>::type;
148 };
149 using type =
151 };
152
154};
155
157
161template<
162 typename R, typename ArgumentTypes, typename Functions, typename... Args>
164 typename Executor<R, ArgumentTypes, Functions, Args...>::type;
165
166// Executors for more derived classes are later in the given type list
167template<typename R, typename Exec, typename ObjectTypes>
168struct Invoker {
169private:
170 struct Base {
171 template<typename Object, typename Functions, typename... Args>
172 R operator ()(Object &object, const Functions &functions, Args&&...)
173 const {
174 // This should never be reached at run-time, because an Executor
175 // generated for (const) Object should have been the catch-all.
176 assert(false);
177 if constexpr (std::is_void_v<R>)
178 return;
179 else
180 return R{};
181 }
182 };
183 template<typename ObjectType, typename Recur> struct Op {
184 template<typename Object, typename Functions, typename... Args>
185 R operator ()(Object &object, const Functions &functions, Args&&... args)
186 const {
187 // Dynamic type test of object
188 if (const auto pObject = dynamic_cast<ObjectType*>(&object))
189 // Dispatch to an Executor that knows which of functions applies
190 return Exec{}(*pObject, functions, std::forward<Args>(args)...);
191 else
192 // Recur, with fewer candidate Executors and all of functions
193 return Recur{}(object, functions, std::forward<Args>(args)...);
194 }
195 };
196public:
197 template<typename Object, typename Functions, typename... Args>
198 R operator ()(Object &object, const Functions &functions, Args&&... args)
199 const {
201 return fn(object, functions, std::forward<Args>(args)...);
202 }
203};
204
205template<typename ...Executors> struct UsedCases {
206 constexpr auto operator ()() {
207 return std::integral_constant<unsigned, (Executors::SetUsed | ...)>{};
208 };
209};
210
211template<size_t... Is, typename TupleLike> auto MakeFunctionTuple(
212 std::index_sequence<Is...>, const TupleLike &functions) {
213 return std::forward_as_tuple(std::get<Is>(functions)...);
214}
215
216template<
217 typename R,
218 typename ObjectTypes,
219 typename Functions,
220 typename... Args
221>
223 static_assert(!Null_v<ObjectTypes>);
225 // Generate Executor classes, for each of ObjectTypes,
226 // each zero-sized and with an operator () that calls the correct
227 // one of functions, assuming the object is of the corresponding type
228 template<typename Tail> using Executor_ =
229 Executor_t<R, Tail, Functions, Args...>;
230 using Executors = MapList_t<Fn<Executor_>, ObjectTypes>;
231
232 // Compile time reachability check of the given functions
233 enum { All = Length_v<Functions>, AllBits = (1u << All) - 1u };
234 static_assert(std::is_same_v<
235 std::integral_constant<unsigned, AllBits>,
237 >,
238 "Uncallable case in TypeSwitch");
239
241 template<typename TupleLike> R operator ()(
242 Object &object, const TupleLike &functions,
243 Args&&... args) const {
244 // Do dynamic dispatch to one of the Executors
245 return Invoker<R, Exec, ObjectTypes>{}(object,
246 MakeFunctionTuple(std::make_index_sequence<All>{}, functions),
247 std::forward<Args>(args)...);
248 }
249};
250
251template<typename TypeList> constexpr bool RootTypeCheck_v =
252 Every_v<Bind1st<std::is_base_of, Head_t<TypeList>>, Tail_t<TypeList>>;
253
254template<typename TypeList> using InheritanceCheck =
256
257}
258
259template<typename TypeList> constexpr bool TypeListCheck_v =
260 std::is_polymorphic_v<Head_t<TypeList>> &&
261 detail::RootTypeCheck_v<TypeList> &&
262 Every_v<Fn<detail::InheritanceCheck>, NonEmptyTails_t<TypeList>> &&
263 (Every_v<Fn<std::is_const>, TypeList> ||
264 NotAny_v<Fn<std::is_const>, TypeList>);
265
307template<
308 typename R = void,
309 typename Types,
310 class TupleLike,
311 typename... Args
312>
313auto Dispatch(Head_t<Types> &object, const TupleLike &functions,
314 Args&&... args)
315 -> std::enable_if_t<TypeListCheck_v<Types>, R>
316{
317 // Generate a function that dispatches dynamically on track type
319 object, functions, std::forward<Args>(args)...);
320}
321
323
326template<
327 typename R = void,
328 typename Types,
329 typename ...Functions
330>
331auto VDispatch(Head_t<Types> &object, const Functions &...functions)
332{
333 return Dispatch<R, Types>(object, std::forward_as_tuple(functions...));
334}
335
336}
337
338#endif
Functions and classes that generate callable objects.
Extraction of sub-tuples of std::tuple (or other tuple-like classes)
Metaprogramming utilities for manipulating lists of types.
static const auto fn
auto ForwardNext(Tuple &&tuple)
Forwarding of the tail of a tuple.
Definition: Tuple.h:161
Utilities for compile-time type manipulation. Some terminology as in Lisp.
Definition: TypeList.h:22
typename NonEmptyTails< TypeList >::type NonEmptyTails_t
Definition: TypeList.h:173
typename LeftFold< Op, TypeList, Initial >::type LeftFold_t
Definition: TypeList.h:309
typename Tail< TypeList >::type Tail_t
Definition: TypeList.h:107
Call< Predicate, T > Is
Apply a metapredicate to a type.
Definition: TypeList.h:391
typename Apply< Template, TypeList >::type Apply_t
Definition: TypeList.h:235
typename MapList< Metafunction, TypeList >::type MapList_t
Definition: TypeList.h:270
typename Head< TypeList >::type Head_t
Definition: TypeList.h:95
typename RightFoldList< Op, TypeList, Initial >::type RightFoldList_t
Definition: TypeList.h:365
Apply_t< FunctionTupleType, Functions > FunctionTuple
Definition: TypeSwitch.h:27
auto MakeFunctionTuple(std::index_sequence< Is... >, const TupleLike &functions)
Definition: TypeSwitch.h:211
std::tuple< const Functions &... > FunctionTupleType
Definition: TypeSwitch.h:25
constexpr bool RootTypeCheck_v
Definition: TypeSwitch.h:251
typename Executor< R, ArgumentTypes, Functions, Args... >::type Executor_t
Synthesize a function appropriate for ArgumentType.
Definition: TypeSwitch.h:164
auto VDispatch(Head_t< Types > &object, const Functions &...functions)
Definition: TypeSwitch.h:331
auto Dispatch(Head_t< Types > &object, const TupleLike &functions, Args &&... args) -> std::enable_if_t< TypeListCheck_v< Types >, R >
Definition: TypeSwitch.h:313
constexpr bool TypeListCheck_v
Definition: TypeSwitch.h:259
Delays expansion of nested alias rest; so Tail<Nil> is legal.
Definition: TypeList.h:101
R operator()(BaseClass &object, const Functions &functions, Args &&... args) const
Ignore the remaining functions and call the first only.
Definition: TypeSwitch.h:88
R operator()(BaseClass &object, const Functions &functions, Args &&... args) const
Definition: TypeSwitch.h:105
std::conjunction< Compatible, curried, Case1_ > Case1
Definition: TypeSwitch.h:130
typename std::disjunction< Case1, Case2, Default >::type type
Definition: TypeSwitch.h:147
std::is_invocable< F, Dummy && > curried
Definition: TypeSwitch.h:125
std::is_base_of< BaseClass, ArgumentType > Compatible
Whether upcast of ArgumentType* to BaseClass* works.
Definition: TypeSwitch.h:79
No BaseClass of ArgumentType is acceptable to the first function.
Definition: TypeSwitch.h:59
R operator()(ArgumentType &object, const Functions &functions, Args &&... args) const
Ignore the first, inapplicable function and try others.
Definition: TypeSwitch.h:66
typename LeftFold_t< CombineOp, ArgumentTypes, Transparent >::type type
Definition: TypeSwitch.h:150
R operator()(ArgumentType &, const Functions &, Args &&...) const
No functions matched, so do nothing.
Definition: TypeSwitch.h:46
Metafunction implementing TypeSwitch.
Definition: TypeSwitch.h:35
typename RightFoldList_t< Combine, Funs, NoOp >::type type
Definition: TypeSwitch.h:153
Head_t< ArgumentTypes > ArgumentType
Definition: TypeSwitch.h:37
R operator()(Object &object, const Functions &functions, Args &&...) const
Definition: TypeSwitch.h:172
R operator()(Object &object, const Functions &functions, Args &&... args) const
Definition: TypeSwitch.h:185
R operator()(Object &object, const Functions &functions, Args &&... args) const
Definition: TypeSwitch.h:198
MapList_t< Fn< Executor_ >, ObjectTypes > Executors
Definition: TypeSwitch.h:230
R operator()(Object &object, const TupleLike &functions, Args &&... args) const
Definition: TypeSwitch.h:241
Executor_t< R, Tail, Functions, Args... > Executor_
Definition: TypeSwitch.h:229
Apply_t< Callable::OverloadSet, Executors > Exec
Definition: TypeSwitch.h:240
Head_t< ObjectTypes > Object
Definition: TypeSwitch.h:224
constexpr auto operator()()
Definition: TypeSwitch.h:206