1 /* Dynamic testing for abstract is-a relationships. 2 Copyright (C) 2012-2024 Free Software Foundation, Inc. 3 Contributed by Lawrence Crowl. 4 5 This file is part of GCC. 6 7 GCC is free software; you can redistribute it and/or modify it under 8 the terms of the GNU General Public License as published by the Free 9 Software Foundation; either version 3, or (at your option) any later 10 version. 11 12 GCC is distributed in the hope that it will be useful, but WITHOUT ANY 13 WARRANTY; without even the implied warranty of MERCHANTABILITY or 14 FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 15 for more details. 16 17 You should have received a copy of the GNU General Public License 18 along with GCC; see the file COPYING3. If not see 19 <http://www.gnu.org/licenses/>. */ 20 21 22 /* This header generic type query and conversion functions. 23 24 25 USING THE GENERIC TYPE FACILITY 26 27 28 The user functions are: 29 30 bool is_a <TYPE> (pointer) 31 32 Tests whether the pointer actually points to a more derived TYPE. 33 34 Suppose you have a symtab_node *ptr, AKA symtab_node *ptr. You can test 35 whether it points to a 'derived' cgraph_node as follows. 36 37 if (is_a <cgraph_node *> (ptr)) 38 .... 39 40 41 TYPE as_a <TYPE> (pointer) 42 43 Converts pointer to a TYPE. 44 45 You can just assume that it is such a node. 46 47 do_something_with (as_a <cgraph_node *> *ptr); 48 49 TYPE safe_as_a <TYPE> (pointer) 50 51 Like as_a <TYPE> (pointer), but where pointer could be NULL. This 52 adds a check against NULL where the regular is_a_helper hook for TYPE 53 assumes non-NULL. 54 55 do_something_with (safe_as_a <cgraph_node *> *ptr); 56 57 TYPE dyn_cast <TYPE> (pointer) 58 59 Converts pointer to TYPE if and only if "is_a <TYPE> pointer". Otherwise, 60 returns NULL. This function is essentially a checked down cast. 61 62 This functions reduce compile time and increase type safety when treating a 63 generic item as a more specific item. 64 65 You can test and obtain a pointer to the 'derived' type in one indivisible 66 operation. 67 68 if (cgraph_node *cptr = dyn_cast <cgraph_node *> (ptr)) 69 .... 70 71 As an example, the code change is from 72 73 if (symtab_function_p (node)) 74 { 75 struct cgraph_node *cnode = cgraph (node); 76 .... 77 } 78 79 to 80 81 if (cgraph_node *cnode = dyn_cast <cgraph_node *> (node)) 82 { 83 .... 84 } 85 86 The necessary conditional test defines a variable that holds a known good 87 pointer to the specific item and avoids subsequent conversion calls and 88 the assertion checks that may come with them. 89 90 When, the property test is embedded within a larger condition, the 91 variable declaration gets pulled out of the condition. (This approach 92 leaves some room for using the variable inappropriately.) 93 94 if (symtab_variable_p (node) && varpool (node)->finalized) 95 varpool_analyze_node (varpool (node)); 96 97 becomes 98 99 varpool_node *vnode = dyn_cast <varpool_node *> (node); 100 if (vnode && vnode->finalized) 101 varpool_analyze_node (vnode); 102 103 Note that we have converted two sets of assertions in the calls to varpool 104 into safe and efficient use of a variable. 105 106 TYPE safe_dyn_cast <TYPE> (pointer) 107 108 Like dyn_cast <TYPE> (pointer), except that it accepts null pointers 109 and returns null results for them. 110 111 112 If you use these functions and get a 'inline function not defined' or a 113 'missing symbol' error message for 'is_a_helper<....>::test', it means that 114 the connection between the types has not been made. See below. 115 116 117 EXTENDING THE GENERIC TYPE FACILITY 118 119 Method 1 120 -------- 121 122 If DERIVED is derived from BASE, and if BASE contains enough information 123 to determine whether an object is actually an instance of DERIVED, 124 then you can make the above routines work for DERIVED by defining 125 a specialization of is_a_helper such as: 126 127 template<> 128 struct is_a_helper<DERIVED *> : static_is_a_helper<DERIVED *> 129 { 130 static inline bool test (const BASE *p) { return ...; } 131 }; 132 133 This test function should return true if P is an instanced of DERIVED. 134 This on its own is enough; the comments below for method 2 do not apply. 135 136 Method 2 137 -------- 138 139 Alternatively, if two types are connected in ways other than C++ 140 inheritance, each connection between them must be made by defining a 141 specialization of the template member function 'test' of the template 142 class 'is_a_helper'. For example, 143 144 template <> 145 template <> 146 inline bool 147 is_a_helper <cgraph_node *>::test (symtab_node *p) 148 { 149 return p->type == SYMTAB_FUNCTION; 150 } 151 152 If a simple reinterpret_cast between the pointer types is incorrect, then you 153 must also specialize the template member function 'cast'. Failure to do so 154 when needed may result in a crash. For example, 155 156 template <> 157 template <> 158 inline bool 159 is_a_helper <cgraph_node *>::cast (symtab_node *p) 160 { 161 return &p->x_function; 162 } 163 164 */ 165 166 #ifndef GCC_IS_A_H 167 #define GCC_IS_A_H 168 169 /* A base class that specializations of is_a_helper can use if casting 170 U * to T is simply a reinterpret_cast. */ 171 172 template <typename T> 173 struct reinterpret_is_a_helper 174 { 175 template <typename U> 176 static inline T cast (U *p) { return reinterpret_cast <T> (p); } 177 }; 178 179 /* A base class that specializations of is_a_helper can use if casting 180 U * to T is simply a static_cast. This is more type-safe than 181 reinterpret_is_a_helper. */ 182 183 template <typename T> 184 struct static_is_a_helper 185 { 186 template <typename U> 187 static inline T cast (U *p) { return static_cast <T> (p); } 188 }; 189 190 /* A generic type conversion internal helper class. */ 191 192 template <typename T> 193 struct is_a_helper : reinterpret_is_a_helper<T> 194 { 195 template <typename U> 196 static inline bool test (U *p); 197 }; 198 199 /* Reuse the definition of is_a_helper<T *> to implement 200 is_a_helper<const T *>. */ 201 202 template <typename T> 203 struct is_a_helper<const T *> 204 { 205 template <typename U> 206 static inline const T *cast (const U *p) 207 { 208 return is_a_helper<T *>::cast (const_cast <U *> (p)); 209 } 210 template <typename U> 211 static inline bool test (const U *p) 212 { 213 return is_a_helper<T *>::test (p); 214 } 215 }; 216 217 /* Note that we deliberately do not define the 'test' member template. Not 218 doing so will result in a build-time error for type relationships that have 219 not been defined, rather than a run-time error. See the discussion above 220 for when to define this member. */ 221 222 /* The public interface. */ 223 224 /* A generic test for a type relationship. See the discussion above for when 225 to use this function. The question answered is "Is type T a derived type of 226 type U?". */ 227 228 template <typename T, typename U> 229 inline bool 230 is_a (U *p) 231 { 232 return is_a_helper<T>::test (p); 233 } 234 235 /* Similar to is_a<>, but where the pointer can be NULL, even if 236 is_a_helper<T> doesn't check for NULL. */ 237 238 template <typename T, typename U> 239 inline bool 240 safe_is_a (U *p) 241 { 242 if (p) 243 return is_a_helper <T>::test (p); 244 else 245 return false; 246 } 247 248 /* A generic conversion from a base type U to a derived type T. See the 249 discussion above for when to use this function. */ 250 251 template <typename T, typename U> 252 inline T 253 as_a (U *p) 254 { 255 gcc_checking_assert (is_a <T> (p)); 256 return is_a_helper <T>::cast (p); 257 } 258 259 /* Similar to as_a<>, but where the pointer can be NULL, even if 260 is_a_helper<T> doesn't check for NULL. */ 261 262 template <typename T, typename U> 263 inline T 264 safe_as_a (U *p) 265 { 266 if (p) 267 { 268 gcc_checking_assert (is_a <T> (p)); 269 return is_a_helper <T>::cast (p); 270 } 271 else 272 return NULL; 273 } 274 275 /* A generic checked conversion from a base type U to a derived type T. See 276 the discussion above for when to use this function. */ 277 278 template <typename T, typename U> 279 inline T 280 dyn_cast (U *p) 281 { 282 if (is_a <T> (p)) 283 return is_a_helper <T>::cast (p); 284 else 285 return static_cast <T> (0); 286 } 287 288 /* Similar to dyn_cast, except that the pointer may be null. */ 289 290 template <typename T, typename U> 291 inline T 292 safe_dyn_cast (U *p) 293 { 294 return p ? dyn_cast <T> (p) : 0; 295 } 296 297 #endif /* GCC_IS_A_H */ 298