OLD | NEW |
1 $$ This is a pump file for generating file templates. Pump is a python | 1 $$ This is a pump file for generating file templates. Pump is a python |
2 $$ script that is part of the Google Test suite of utilities. Description | 2 $$ script that is part of the Google Test suite of utilities. Description |
3 $$ can be found here: | 3 $$ can be found here: |
4 $$ | 4 $$ |
5 $$ http://code.google.com/p/googletest/wiki/PumpManual | 5 $$ http://code.google.com/p/googletest/wiki/PumpManual |
6 $$ | 6 $$ |
7 | 7 |
8 $var MAX_ARITY = 6 | 8 $var MAX_ARITY = 6 |
9 | 9 |
10 // Copyright (c) 2011 The Chromium Authors. All rights reserved. | 10 // Copyright (c) 2011 The Chromium Authors. All rights reserved. |
11 // Use of this source code is governed by a BSD-style license that can be | 11 // Use of this source code is governed by a BSD-style license that can be |
12 // found in the LICENSE file. | 12 // found in the LICENSE file. |
13 | 13 |
14 #ifndef BASE_BIND_INTERNAL_H_ | 14 #ifndef BASE_BIND_INTERNAL_H_ |
15 #define BASE_BIND_INTERNAL_H_ | 15 #define BASE_BIND_INTERNAL_H_ |
16 #pragma once | 16 #pragma once |
17 | 17 |
18 #include "base/bind_helpers.h" | 18 #include "base/bind_helpers.h" |
19 #include "base/callback_helpers.h" | 19 #include "base/callback_helpers.h" |
20 #include "base/template_util.h" | 20 #include "base/template_util.h" |
21 | 21 |
22 namespace base { | 22 namespace base { |
23 namespace internal { | 23 namespace internal { |
24 | 24 |
25 // The method by which a function is invoked is determined by 3 different | 25 // The method by which a function is invoked is determined by 3 different |
26 // dimensions: | 26 // dimensions: |
27 // | 27 // |
28 // 1) The type of function (normal, method, const-method) | 28 // 1) The type of function (normal, or method). |
29 // 2) The arity of the function | 29 // 2) The arity of the function. |
30 // 3) The number of bound parameters. | 30 // 3) The number of bound parameters. |
31 // | 31 // |
32 // The FunctionTraitsN classes unwrap the function signature type to | 32 // The templates below handle the determination of each of these dimensions. |
33 // specialize based on the first two dimensions. The N in FunctionTraitsN | 33 // In brief: |
34 // specifies the 3rd dimension. We could have specified the unbound parameters | |
35 // via template parameters, but this method looked cleaner. | |
36 // | 34 // |
37 // The FunctionTraitsN contains a static DoInvoke() function that is the key to | 35 // FunctionTraits<> -- Provides a normalied signature, and other traits. |
38 // implementing type erasure in the Callback() classes. DoInvoke() is a static | 36 // InvokerN<> -- Provides a DoInvoke() function that actually executes |
39 // function with a fixed signature that is independent of StorageType; its | 37 // a calback. |
40 // first argument is a pointer to the non-templated common baseclass of | 38 // InvokerStorageN<> -- Provides storage for the bound parameters, and |
41 // StorageType. This lets us store pointer to DoInvoke() in a function pointer | 39 // typedefs to the above. |
42 // that has knowledge of the specific StorageType, and thus no knowledge of the | 40 // |
43 // bound function and bound parameter types. | 41 // More details about the design of each class is included in a comment closer |
| 42 // to their defition. |
| 43 |
| 44 |
| 45 // FunctionTraits<> |
| 46 // |
| 47 // The FunctionTraits<> template determines the type of function, and also |
| 48 // creates a NormalizedType used to select the InvokerN classes. It turns out |
| 49 // that syntactically, you only really have 2 variations when invoking a |
| 50 // funciton pointer: normal, and method. One is invoked func_ptr(arg1). The |
| 51 // other is invoked (*obj_->methd_ptr(arg1)). |
| 52 // |
| 53 // However, in the type system, there are many more distinctions. In standard |
| 54 // C++, there's all variations of const, and volatile on the function pointer. |
| 55 // In Windows, there are additional calling conventions (eg., __stdcall, |
| 56 // __fastcall, etc.). FunctionTraits<> handles categorizing each of these into |
| 57 // a normalized signature. |
| 58 // |
| 59 // Having a NoramlizedSignature signature, reduces the combinatoric |
| 60 // complexity of defintions for the InvokerN<> later. Even though there are |
| 61 // only 2 syntactic variantions on invoking a function, without |
| 62 // normalizing the signature, there would need to be on specialization of |
| 63 // InvokerN for each unique (function_type, bound_arg, unbound_args) tuple |
| 64 // in order to match all function signatures. |
| 65 // |
| 66 // By normalizing the function signature, we reduce function_type to exactly 2. |
| 67 |
| 68 template <typename Sig> |
| 69 struct FunctionTraits; |
| 70 |
| 71 $range ARITY 0..MAX_ARITY |
| 72 $for ARITY [[ |
| 73 $range ARG 1..ARITY |
| 74 |
| 75 // Function: Arity $(ARITY). |
| 76 template <typename R[[]] |
| 77 $if ARITY > 0[[, ]] $for ARG , [[typename X$(ARG)]]> |
| 78 struct FunctionTraits<R(*)($for ARG , [[X$(ARG)]])> { |
| 79 typedef R (*NormalizedSig)($for ARG , [[X$(ARG)]]); |
| 80 typedef base::false_type IsMethod; |
| 81 }; |
| 82 |
| 83 // Method: Arity $(ARITY). |
| 84 template <typename R, typename T[[]] |
| 85 $if ARITY > 0[[, ]] $for ARG , [[typename X$(ARG)]]> |
| 86 struct FunctionTraits<R(T::*)($for ARG , [[X$(ARG)]])> { |
| 87 typedef R (T::*NormalizedSig)($for ARG , [[X$(ARG)]]); |
| 88 typedef base::true_type IsMethod; |
| 89 }; |
| 90 |
| 91 // Const Method: Arity $(ARITY). |
| 92 template <typename R, typename T[[]] |
| 93 $if ARITY > 0[[, ]] $for ARG , [[typename X$(ARG)]]> |
| 94 struct FunctionTraits<R(T::*)($for ARG , [[X$(ARG)]]) const> { |
| 95 typedef R (T::*NormalizedSig)($for ARG , [[X$(ARG)]]); |
| 96 typedef base::true_type IsMethod; |
| 97 }; |
| 98 |
| 99 ]] $$for ARITY |
| 100 |
| 101 // InvokerN<> |
| 102 // |
| 103 // The InvokerN templates contain a static DoInvoke() function that is the key |
| 104 // to implementing type erasure in the Callback() classes. |
| 105 // |
| 106 // DoInvoke() is a static function with a fixed signature that is independent |
| 107 // of StorageType; its first argument is a pointer to the non-templated common |
| 108 // baseclass of StorageType. This lets us store pointer to DoInvoke() in a |
| 109 // function pointer that has knowledge of the specific StorageType, and thus |
| 110 // no knowledge of the bound function and bound parameter types. |
44 // | 111 // |
45 // As long as we ensure that DoInvoke() is only used with pointers there were | 112 // As long as we ensure that DoInvoke() is only used with pointers there were |
46 // upcasted from the correct StorageType, we can be sure that execution is | 113 // upcasted from the correct StorageType, we can be sure that execution is |
47 // safe. | 114 // safe. |
| 115 // |
| 116 // The InvokerN templates are the only point that knows the number of bound |
| 117 // and unbound arguments. This is intentional because it allows the other |
| 118 // templates classes in the system to only have as many specializations as |
| 119 // the max arity of function we wish to support. |
48 | 120 |
49 $range BOUND 0..MAX_ARITY | 121 $range BOUND 0..MAX_ARITY |
50 $for BOUND [[ | 122 $for BOUND [[ |
51 | 123 |
52 template <typename StorageType, typename Sig> | 124 template <typename StorageType, typename NormalizedSig> |
53 struct FunctionTraits$(BOUND); | 125 struct Invoker$(BOUND); |
54 | 126 |
55 $range ARITY 0..MAX_ARITY | 127 $range ARITY 0..MAX_ARITY |
56 $for ARITY [[ | 128 $for ARITY [[ |
57 | 129 |
58 $var UNBOUND = ARITY - BOUND | 130 $var UNBOUND = ARITY - BOUND |
59 $if UNBOUND >= 0 [[ | 131 $if UNBOUND >= 0 [[ |
60 | 132 |
61 $$ Variables for function traits generation. | 133 $$ Variables for function traits generation. |
62 $range ARG 1..ARITY | 134 $range ARG 1..ARITY |
63 $range BOUND_ARG 1..BOUND | 135 $range BOUND_ARG 1..BOUND |
64 $range UNBOUND_ARG (ARITY - UNBOUND + 1)..ARITY | 136 $range UNBOUND_ARG (ARITY - UNBOUND + 1)..ARITY |
65 | 137 |
66 $$ Variables for method traits generation. We are always short one arity since | 138 $$ Variables for method traits generation. We are always short one arity since |
67 $$ the first bound parameter is the object. | 139 $$ the first bound parameter is the object. |
68 $var M_ARITY = ARITY - 1 | 140 $var M_ARITY = ARITY - 1 |
69 $range M_ARG 1..M_ARITY | 141 $range M_ARG 1..M_ARITY |
70 $range M_BOUND_ARG 2..BOUND | 142 $range M_BOUND_ARG 2..BOUND |
71 $range M_UNBOUND_ARG (M_ARITY - UNBOUND + 1)..M_ARITY | 143 $range M_UNBOUND_ARG (M_ARITY - UNBOUND + 1)..M_ARITY |
72 | 144 |
73 // Function: Arity $(ARITY) -> $(UNBOUND). | 145 // Function: Arity $(ARITY) -> $(UNBOUND). |
74 template <typename StorageType, typename R[[]] | 146 template <typename StorageType, typename R[[]] |
75 $if ARITY > 0 [[,]][[]] | 147 $if ARITY > 0 [[,]][[]] |
76 $for ARG , [[typename X$(ARG)]]> | 148 $for ARG , [[typename X$(ARG)]]> |
77 struct FunctionTraits$(BOUND)<StorageType, R(*)($for ARG , [[X$(ARG)]])> { | 149 struct Invoker$(BOUND)<StorageType, R(*)($for ARG , [[X$(ARG)]])> { |
78 $if ARITY > 0 [[ | 150 $if ARITY > 0 [[ |
79 | 151 |
80 COMPILE_ASSERT( | 152 COMPILE_ASSERT( |
81 !($for ARG || [[ is_non_const_reference<X$(ARG)>::value ]]), | 153 !($for ARG || [[ is_non_const_reference<X$(ARG)>::value ]]), |
82 do_not_bind_functions_with_nonconst_ref); | 154 do_not_bind_functions_with_nonconst_ref); |
83 | 155 |
84 ]] | 156 ]] |
85 | 157 |
86 typedef base::false_type IsMethod; | |
87 | |
88 static R DoInvoke(InvokerStorageBase* base[[]] | 158 static R DoInvoke(InvokerStorageBase* base[[]] |
89 $if UNBOUND != 0 [[, ]][[]] | 159 $if UNBOUND != 0 [[, ]][[]] |
90 $for UNBOUND_ARG , [[const X$(UNBOUND_ARG)& x$(UNBOUND_ARG)]]) { | 160 $for UNBOUND_ARG , [[const X$(UNBOUND_ARG)& x$(UNBOUND_ARG)]]) { |
91 StorageType* invoker = static_cast<StorageType*>(base); | 161 StorageType* invoker = static_cast<StorageType*>(base); |
92 return invoker->f_($for BOUND_ARG , [[Unwrap(invoker->p$(BOUND_ARG)_)]][[]] | 162 return invoker->f_($for BOUND_ARG , [[Unwrap(invoker->p$(BOUND_ARG)_)]][[]] |
93 $$ Add comma if there are both boudn and unbound args. | 163 $$ Add comma if there are both boudn and unbound args. |
94 $if UNBOUND > 0 [[$if BOUND > 0 [[, ]]]][[]] | 164 $if UNBOUND > 0 [[$if BOUND > 0 [[, ]]]][[]] |
95 $for UNBOUND_ARG , [[x$(UNBOUND_ARG)]]); | 165 $for UNBOUND_ARG , [[x$(UNBOUND_ARG)]]); |
96 } | 166 } |
97 }; | 167 }; |
98 | 168 |
99 $if BOUND > 0 [[ | 169 $if BOUND > 0 [[ |
100 | 170 |
101 // Method: Arity $(M_ARITY) -> $(UNBOUND). | 171 // Method: Arity $(M_ARITY) -> $(UNBOUND). |
102 template <typename StorageType, typename R, typename T[[]] | 172 template <typename StorageType, typename R, typename T[[]] |
103 $if M_ARITY > 0[[, ]] $for M_ARG , [[typename X$(M_ARG)]]> | 173 $if M_ARITY > 0[[, ]] $for M_ARG , [[typename X$(M_ARG)]]> |
104 struct FunctionTraits$(BOUND)<StorageType, R(T::*)($for M_ARG , [[X$(M_ARG)]])>
{ | 174 struct Invoker$(BOUND)<StorageType, R(T::*)($for M_ARG , [[X$(M_ARG)]])> { |
105 $if M_ARITY > 0 [[ | 175 $if M_ARITY > 0 [[ |
106 | 176 |
107 COMPILE_ASSERT( | 177 COMPILE_ASSERT( |
108 !($for M_ARG || [[ is_non_const_reference<X$(M_ARG)>::value ]]), | 178 !($for M_ARG || [[ is_non_const_reference<X$(M_ARG)>::value ]]), |
109 do_not_bind_functions_with_nonconst_ref); | 179 do_not_bind_functions_with_nonconst_ref); |
110 | 180 |
111 ]] | 181 ]] |
112 | 182 |
113 typedef base::true_type IsMethod; | |
114 | |
115 static R DoInvoke(InvokerStorageBase* base[[]] | 183 static R DoInvoke(InvokerStorageBase* base[[]] |
116 $if UNBOUND > 0 [[, ]][[]] | 184 $if UNBOUND > 0 [[, ]][[]] |
117 $for M_UNBOUND_ARG , [[const X$(M_UNBOUND_ARG)& x$(M_UNBOUND_ARG)]]) { | 185 $for M_UNBOUND_ARG , [[const X$(M_UNBOUND_ARG)& x$(M_UNBOUND_ARG)]]) { |
118 StorageType* invoker = static_cast<StorageType*>(base); | 186 StorageType* invoker = static_cast<StorageType*>(base); |
119 return (Unwrap(invoker->p1_)->*invoker->f_)([[]] | 187 return (Unwrap(invoker->p1_)->*invoker->f_)([[]] |
120 $for M_BOUND_ARG , [[Unwrap(invoker->p$(M_BOUND_ARG)_)]][[]] | 188 $for M_BOUND_ARG , [[Unwrap(invoker->p$(M_BOUND_ARG)_)]][[]] |
121 $if UNBOUND > 0 [[$if BOUND > 1 [[, ]]]][[]] | 189 $if UNBOUND > 0 [[$if BOUND > 1 [[, ]]]][[]] |
122 $for M_UNBOUND_ARG , [[x$(M_UNBOUND_ARG)]]); | 190 $for M_UNBOUND_ARG , [[x$(M_UNBOUND_ARG)]]); |
123 } | 191 } |
124 }; | 192 }; |
125 | 193 |
126 // Const Method: Arity $(M_ARITY) -> $(UNBOUND). | |
127 template <typename StorageType, typename R, typename T[[]] | |
128 $if M_ARITY > 0[[, ]] $for M_ARG , [[typename X$(M_ARG)]]> | |
129 struct FunctionTraits$(BOUND)<StorageType, R(T::*)($for M_ARG , [[X$(M_ARG)]]) c
onst> { | |
130 $if M_ARITY > 0 [[ | |
131 | |
132 COMPILE_ASSERT( | |
133 !($for M_ARG || [[is_non_const_reference<X$(M_ARG)>::value ]]), | |
134 do_not_bind_functions_with_nonconst_ref); | |
135 | |
136 ]] | |
137 | |
138 typedef base::true_type IsMethod; | |
139 | |
140 static R DoInvoke(InvokerStorageBase* base[[]] | |
141 $if UNBOUND > 0 [[, ]] | |
142 [[]] $for M_UNBOUND_ARG , [[const X$(M_UNBOUND_ARG)& x$(M_UNBOUND_ARG)]]) { | |
143 StorageType* invoker = static_cast<StorageType*>(base); | |
144 return (Unwrap(invoker->p1_)->*invoker->f_)([[]] | |
145 $for M_BOUND_ARG , [[Unwrap(invoker->p$(M_BOUND_ARG)_)]][[]] | |
146 $if UNBOUND > 0 [[$if BOUND > 1 [[, ]]]][[]] | |
147 $for M_UNBOUND_ARG , [[x$(M_UNBOUND_ARG)]]); | |
148 } | |
149 }; | |
150 | |
151 ]] $$ if BOUND | 194 ]] $$ if BOUND |
152 | 195 |
153 ]] $$ if UNBOUND | 196 ]] $$ if UNBOUND |
154 ]] $$ for ARITY | 197 ]] $$ for ARITY |
155 ]] $$ for BOUND | 198 ]] $$ for BOUND |
156 | 199 |
157 | 200 |
158 // These are the actual storage classes for the invokers. | 201 // InvokerStorageN<> |
| 202 // |
| 203 // These are the actual storage classes for the Invokers. |
159 // | 204 // |
160 // Though these types are "classes", they are being used as structs with | 205 // Though these types are "classes", they are being used as structs with |
161 // all member variable public. We cannot make it a struct because it inherits | 206 // all member variable public. We cannot make it a struct because it inherits |
162 // from a class which causes a compiler warning. We cannot add a "Run()" method | 207 // from a class which causes a compiler warning. We cannot add a "Run()" method |
163 // that forwards the unbound arguments because that would require we unwrap the | 208 // that forwards the unbound arguments because that would require we unwrap the |
164 // Sig type like in FunctionTraitsN above to know the return type, and the arity | 209 // Sig type like in InvokerN above to know the return type, and the arity |
165 // of Run(). | 210 // of Run(). |
166 // | 211 // |
167 // An alternate solution would be to merge FunctionTraitsN and InvokerStorageN, | 212 // An alternate solution would be to merge InvokerN and InvokerStorageN, |
168 // but the generated code seemed harder to read. | 213 // but the generated code seemed harder to read. |
169 | 214 |
170 $for BOUND [[ | 215 $for BOUND [[ |
171 $range BOUND_ARG 1..BOUND | 216 $range BOUND_ARG 1..BOUND |
172 | 217 |
173 template <typename Sig[[]] | 218 template <typename Sig[[]] |
174 $if BOUND > 0 [[, ]] | 219 $if BOUND > 0 [[, ]] |
175 $for BOUND_ARG , [[typename P$(BOUND_ARG)]]> | 220 $for BOUND_ARG , [[typename P$(BOUND_ARG)]]> |
176 class InvokerStorage$(BOUND) : public InvokerStorageBase { | 221 class InvokerStorage$(BOUND) : public InvokerStorageBase { |
177 public: | 222 public: |
178 typedef InvokerStorage$(BOUND) StorageType; | 223 typedef InvokerStorage$(BOUND) StorageType; |
179 typedef FunctionTraits$(BOUND)<StorageType, Sig> FunctionTraits; | 224 typedef FunctionTraits<Sig> TargetTraits; |
180 typedef typename FunctionTraits::IsMethod IsMethod; | 225 typedef Invoker$(BOUND)<StorageType, typename TargetTraits::NormalizedSig> Inv
oker; |
181 | 226 typedef typename TargetTraits::IsMethod IsMethod; |
182 $for BOUND_ARG [[ | 227 $for BOUND_ARG [[ |
183 $if BOUND_ARG == 1 [[ | 228 $if BOUND_ARG == 1 [[ |
184 | 229 |
185 // For methods, we need to be careful for parameter 1. We skip the | 230 // For methods, we need to be careful for parameter 1. We skip the |
186 // scoped_refptr check because the binder itself takes care of this. We also | 231 // scoped_refptr check because the binder itself takes care of this. We also |
187 // disallow binding of an array as the method's target object. | 232 // disallow binding of an array as the method's target object. |
188 COMPILE_ASSERT(IsMethod::value || | 233 COMPILE_ASSERT(IsMethod::value || |
189 !internal::UnsafeBindtoRefCountedArg<P$(BOUND_ARG)>::value, | 234 !internal::UnsafeBindtoRefCountedArg<P$(BOUND_ARG)>::value, |
190 p$(BOUND_ARG)_is_refcounted_type_and_needs_scoped_refptr); | 235 p$(BOUND_ARG)_is_refcounted_type_and_needs_scoped_refptr); |
191 COMPILE_ASSERT(!IsMethod::value || !is_array<P$(BOUND_ARG)>::value, | 236 COMPILE_ASSERT(!IsMethod::value || !is_array<P$(BOUND_ARG)>::value, |
(...skipping 36 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
228 | 273 |
229 ]] | 274 ]] |
230 }; | 275 }; |
231 | 276 |
232 ]] $$ for BOUND | 277 ]] $$ for BOUND |
233 | 278 |
234 } // namespace internal | 279 } // namespace internal |
235 } // namespace base | 280 } // namespace base |
236 | 281 |
237 #endif // BASE_BIND_INTERNAL_H_ | 282 #endif // BASE_BIND_INTERNAL_H_ |
OLD | NEW |