Panda3D
Loading...
Searching...
No Matches
typeRegistry.cxx
Go to the documentation of this file.
1/**
2 * PANDA 3D SOFTWARE
3 * Copyright (c) Carnegie Mellon University. All rights reserved.
4 *
5 * All use of this software is subject to the terms of the revised BSD
6 * license. You should have received a copy of this license along
7 * with this source code in a file named "LICENSE."
8 *
9 * @file typeRegistry.cxx
10 * @author drose
11 * @date 2001-08-06
12 */
13
14#include "typeRegistry.h"
15#include "typeRegistryNode.h"
16#include "typeHandle.h"
17#include "typedObject.h"
18#include "indent.h"
19#include "numeric_types.h"
20
21#include <algorithm>
22
23using std::cerr;
24using std::ostream;
25using std::ostringstream;
26using std::string;
27
28MutexImpl *TypeRegistry::_lock = nullptr;
29TypeRegistry *TypeRegistry::_global_pointer = nullptr;
30
31/**
32 * Creates a new Type of the given name and assigns a unique value to the
33 * type_handle. All type names must be unique. If the type name has already
34 * been used, the supplied type_handle value must match the name's assigned
35 * type_handle or an error is triggered. Returns true if the name wasn't
36 * defined before, false if it was.
37 */
39register_type(TypeHandle &type_handle, const string &name) {
40 _lock->lock();
41
42 if (type_handle != TypeHandle::none()) {
43 // Here's a type that was already registered. Just make sure everything's
44 // still kosher.
45 TypeRegistryNode *rnode = look_up(type_handle, nullptr);
46 if (&type_handle == &rnode->_ref) {
47 // No problem.
48 _lock->unlock();
49 assert(rnode->_name == name);
50 return false;
51 }
52 }
53
54 NameRegistry::iterator ri;
55 ri = _name_registry.find(name);
56
57 if (ri == _name_registry.end()) {
58 // The name was not already used; this is the first time this class has
59 // been defined.
60
61 TypeHandle new_handle;
62 new_handle._index = (int)_handle_registry.size();
63
64 TypeRegistryNode *rnode = new TypeRegistryNode(new_handle, name, type_handle);
65 _handle_registry.push_back(rnode);
66 _name_registry[name] = rnode;
67 _derivations_fresh = false;
68
69 type_handle = new_handle;
70 _lock->unlock();
71 return true;
72 }
73 TypeRegistryNode *rnode = (*ri).second;
74 assert(rnode->_name == (*ri).first);
75 assert(rnode->_handle._index >= 0 &&
76 rnode->_handle._index < (int)_handle_registry.size());
77 assert(_handle_registry[rnode->_handle._index] == rnode);
78 assert(rnode->_handle._index != 0);
79
80 // The name was previously used; make sure the type_handle matches.
81 if (&type_handle == &rnode->_ref) {
82 // Ok, this was just a repeated attempt to register the same type.
83
84 if (type_handle == rnode->_handle) {
85 // No problem.
86 _lock->unlock();
87 return false;
88 }
89 // But wait--the type_handle has changed! We kept a reference to the
90 // static _type_handle member in the class that was passed in at the first
91 // call to register_type(), and we got the same reference passed in this
92 // time, but now it's different! Bad juju.
93 cerr << "Reregistering " << name << "\n";
94 type_handle = rnode->_handle;
95 _lock->unlock();
96 return false;
97 }
98
99 if (type_handle != rnode->_handle) {
100 // Hmm, we seem to have a contradictory type registration!
101 cerr
102 << "Attempt to register type " << name << " more than once!\n";
103
104 // This is invalid, but we'll allow it anyway. It seems to happen for
105 // some reason under GNU libc5 that we occasionally end up with two
106 // legitimate copies of the same class object in memory--each with its own
107 // static _type_handle member.
108
109 type_handle = rnode->_handle;
110 }
111 _lock->unlock();
112 return false;
113}
114
115/**
116 * Registers a new type on-the-fly, presumably at runtime. A new TypeHandle
117 * is returned if the typename was not seen before; otherwise the same
118 * TypeHandle that was last used for this typename is returned.
119 */
121register_dynamic_type(const string &name) {
122 _lock->lock();
123
124 NameRegistry::iterator ri;
125 ri = _name_registry.find(name);
126
127 if (ri == _name_registry.end()) {
128 // The name was not already used; this is the first time this class has
129 // been defined.
130
131 // We must dynamically allocate a new handle so the TypeRegistryNode has
132 // something unique to point to. This doesn't really mean anything,
133 // though.
134 TypeHandle *new_handle = new TypeHandle;
135 new_handle->_index = (int)_handle_registry.size();
136
137 TypeRegistryNode *rnode = new TypeRegistryNode(*new_handle, name, *new_handle);
138 _handle_registry.push_back(rnode);
139 _name_registry[name] = rnode;
140 _derivations_fresh = false;
141
142 _lock->unlock();
143 return *new_handle;
144 }
145
146 // Return the TypeHandle previously obtained.
147 TypeRegistryNode *rnode = (*ri).second;
148 TypeHandle handle = rnode->_handle;
149 _lock->unlock();
150 return handle;
151}
152
153/**
154 * Records that the type referenced by child inherits directly from the type
155 * referenced by parent. In the event of multiple inheritance, this should be
156 * called once for each parent class.
157 */
160 _lock->lock();
161
162 TypeRegistryNode *cnode = look_up(child, nullptr);
163 assert(cnode != nullptr);
164 TypeRegistryNode *pnode = look_up(parent, nullptr);
165 assert(pnode != nullptr);
166
167 // First, we'll just run through the list to make sure we hadn't already
168 // made this connection.
169 TypeRegistryNode::Classes::iterator ni;
170 ni = find(cnode->_parent_classes.begin(), cnode->_parent_classes.end(),
171 pnode);
172
173 if (ni == cnode->_parent_classes.end()) {
174 cnode->_parent_classes.push_back(pnode);
175 pnode->_child_classes.push_back(cnode);
176 _derivations_fresh = false;
177 }
178
179 _lock->unlock();
180}
181
182/**
183 * Indicates an alternate name for the same type. This is particularly useful
184 * when a type has changed names, since the type is stored in a Bam file by
185 * name; setting the original name as the alternate will allow the type to be
186 * correctly read from old Bam files.
187 */
189record_alternate_name(TypeHandle type, const string &name) {
190 _lock->lock();
191
192 TypeRegistryNode *rnode = look_up(type, nullptr);
193 if (rnode != nullptr) {
194 NameRegistry::iterator ri =
195 _name_registry.insert(NameRegistry::value_type(name, rnode)).first;
196
197 if ((*ri).second != rnode) {
198 _lock->unlock();
199 cerr
200 << "Name " << name << " already assigned to TypeHandle "
201 << rnode->_name << "; cannot reassign to " << type << "\n";
202 return;
203 }
204
205 }
206
207 _lock->unlock();
208}
209
210#ifdef HAVE_PYTHON
211/**
212 * Records the given Python type pointer in the type registry for the benefit
213 * of interrogate, which expects this to contain a Dtool_PyTypedObject.
214 */
215void TypeRegistry::
216record_python_type(TypeHandle type, PyObject *python_type) {
217 _lock->lock();
218
219 TypeRegistryNode *rnode = look_up(type, nullptr);
220 if (rnode != nullptr) {
221 rnode->_python_type = python_type;
222 }
223
224 _lock->unlock();
225}
226#endif
227
228/**
229 * Looks for a previously-registered type of the given name. Returns its
230 * TypeHandle if it exists, or TypeHandle::none() if there is no such type.
231 */
233find_type(const string &name) const {
234 _lock->lock();
235
236 TypeHandle handle = TypeHandle::none();
237 NameRegistry::const_iterator ri;
238 ri = _name_registry.find(name);
239 if (ri != _name_registry.end()) {
240 handle = (*ri).second->_handle;
241 }
242 _lock->unlock();
243
244 return handle;
245}
246
247/**
248 * Looks for a previously-registered type with the given id number (as
249 * returned by TypeHandle::get_index()). Returns its TypeHandle if it exists,
250 * or TypeHandle::none() if there is no such type.
251 */
253find_type_by_id(int id) const {
254 if (id < 0 ||id >= (int)_handle_registry.size()) {
255 cerr
256 << "Invalid TypeHandle index " << id
257 << "! Is memory corrupt?\n";
258 return TypeHandle::none();
259 }
260
261 return _handle_registry[id]->_handle;
262}
263
264
265/**
266 * Returns the name of the indicated type.
267 *
268 * The "object" pointer is an optional pointer to the TypedObject class that
269 * owns this TypeHandle. It is only used in case the TypeHandle is
270 * inadvertantly undefined.
271 */
273get_name(TypeHandle type, TypedObject *object) const {
274 _lock->lock();
275 TypeRegistryNode *rnode = look_up(type, object);
276 assert(rnode != nullptr);
277 string name = rnode->_name;
278 _lock->unlock();
279
280 return name;
281}
282
283/**
284 * Returns true if the first type is derived from the second type, false
285 * otherwise.
286 *
287 * The "child_object" pointer is an optional pointer to the TypedObject class
288 * that owns the child TypeHandle. It is only used in case the TypeHandle is
289 * inadvertently undefined.
290 *
291 * This function definition follows the definitions for look_up() and
292 * freshen_derivations() just to maximize the chance the the compiler will be
293 * able to inline the above functions. Yeah, a compiler shouldn't care, but
294 * there's a big different between "shouldn't" and "doesn't".
295 */
298 TypedObject *child_object) {
299 _lock->lock();
300
301 const TypeRegistryNode *child_node = look_up(child, child_object);
302 const TypeRegistryNode *base_node = look_up(base, nullptr);
303
304 assert(child_node != nullptr);
305 assert(base_node != nullptr);
306
307 freshen_derivations();
308
309 bool result = TypeRegistryNode::is_derived_from(child_node, base_node);
310 _lock->unlock();
311 return result;
312}
313
314/**
315 * Returns the total number of unique TypeHandles in the system.
316 */
319 _lock->lock();
320 int num_types = (int)_handle_registry.size();
321 _lock->unlock();
322 return num_types;
323}
324
325/**
326 * Returns the nth TypeHandle in the system. See get_num_typehandles().
327 */
329get_typehandle(int n) {
330 _lock->lock();
331 TypeRegistryNode *rnode = nullptr;
332 if (n >= 0 && n < (int)_handle_registry.size()) {
333 rnode = _handle_registry[n];
334 }
335 _lock->unlock();
336
337 if (rnode != nullptr) {
338 return rnode->_handle;
339 }
340
341 return TypeHandle::none();
342}
343
344/**
345 * Returns the number of root classes--that is, classes that do not inherit
346 * from any other classes--known in the system.
347 */
350 _lock->lock();
351 freshen_derivations();
352 int num_roots = (int)_root_classes.size();
353 _lock->unlock();
354 return num_roots;
355}
356
357/**
358 * Returns the nth root class in the system. See get_num_root_classes().
359 */
361get_root_class(int n) {
362 _lock->lock();
363 freshen_derivations();
364 TypeHandle handle;
365 if (n >= 0 && n < (int)_root_classes.size()) {
366 handle = _root_classes[n]->_handle;
367 } else {
368 handle = TypeHandle::none();
369 }
370 _lock->unlock();
371
372 return handle;
373}
374
375/**
376 * Returns the number of parent classes that the indicated type is known to
377 * have. This may then be used to index into get_parent_class(). The result
378 * will be 0 if this class does not inherit from any other classes, 1 if
379 * normal, single inheritance is in effect, or greater than one if multiple
380 * inheritance is in effect.
381 *
382 * The "object" pointer is an optional pointer to the TypedObject class that
383 * owns this TypeHandle. It is only used in case the TypeHandle is
384 * inadvertantly undefined.
385 */
387get_num_parent_classes(TypeHandle child, TypedObject *child_object) const {
388 _lock->lock();
389 TypeRegistryNode *rnode = look_up(child, child_object);
390 assert(rnode != nullptr);
391 int num_parents = (int)rnode->_parent_classes.size();
392 _lock->unlock();
393 return num_parents;
394}
395
396/**
397 * Returns the nth parent class of this type. The index should be in the
398 * range 0 <= index < get_num_parent_classes().
399 */
401get_parent_class(TypeHandle child, int index) const {
402 _lock->lock();
403 TypeHandle handle;
404 TypeRegistryNode *rnode = look_up(child, nullptr);
405 assert(rnode != nullptr);
406 if (index >= 0 && index < (int)rnode->_parent_classes.size()) {
407 handle = rnode->_parent_classes[index]->_handle;
408 } else {
409 handle = TypeHandle::none();
410 }
411 _lock->unlock();
412 return handle;
413}
414
415/**
416 * Returns the number of child classes that the indicated type is known to
417 * have. This may then be used to index into get_child_class().
418 *
419 * The "object" pointer is an optional pointer to the TypedObject class that
420 * owns this TypeHandle. It is only used in case the TypeHandle is
421 * inadvertantly undefined.
422 */
424get_num_child_classes(TypeHandle child, TypedObject *child_object) const {
425 _lock->lock();
426 TypeRegistryNode *rnode = look_up(child, child_object);
427 assert(rnode != nullptr);
428 int num_children = (int)rnode->_child_classes.size();
429 _lock->unlock();
430 return num_children;
431}
432
433/**
434 * Returns the nth child class of this type. The index should be in the range
435 * 0 <= index < get_num_child_classes().
436 */
438get_child_class(TypeHandle child, int index) const {
439 _lock->lock();
440 TypeHandle handle;
441 TypeRegistryNode *rnode = look_up(child, nullptr);
442 assert(rnode != nullptr);
443 if (index >= 0 && index < (int)rnode->_child_classes.size()) {
444 handle = rnode->_child_classes[index]->_handle;
445 } else {
446 handle = TypeHandle::none();
447 }
448 _lock->unlock();
449 return handle;
450}
451
452/**
453 * Returns the parent of the indicated child class that is in a direct line of
454 * inheritance to the indicated ancestor class. This is useful in the
455 * presence of multiple inheritance to try to determine what properties an
456 * unknown type may have.
457 *
458 * The "object" pointer is an optional pointer to the TypedObject class that
459 * owns this TypeHandle. It is only used in case the TypeHandle is
460 * inadvertantly undefined.
461 */
464 TypedObject *child_object) {
465 _lock->lock();
466 TypeHandle handle;
467 const TypeRegistryNode *child_node = look_up(child, child_object);
468 const TypeRegistryNode *base_node = look_up(base, nullptr);
469 assert(child_node != nullptr &&
470 base_node != nullptr);
471 freshen_derivations();
472 handle = TypeRegistryNode::get_parent_towards(child_node, base_node);
473 _lock->unlock();
474 return handle;
475}
476
477
478/**
479 * Walks through the TypeRegistry tree and makes sure that each type that was
480 * previously registered is *still* registered. This seems to get broken in
481 * certain circumstances when compiled against libc5--it is as if the static
482 * initializer stomps on the _type_handle values of each class after they've
483 * been registered.
484 */
487 init_lock();
488 _lock->lock();
489 HandleRegistry::iterator ri;
490 TypeRegistry *reg = ptr();
491 for (ri = reg->_handle_registry.begin();
492 ri != reg->_handle_registry.end();
493 ++ri) {
494 TypeRegistryNode *rnode = (*ri);
495 if (rnode != nullptr && rnode->_handle != rnode->_ref) {
496 cerr << "Reregistering " << rnode->_name << "\n";
497 }
498 }
499 _lock->unlock();
500}
501
502
503/**
504 * Makes an attempt to format the entire TypeRegistry in a nice way that shows
505 * the derivation tree as intelligently as possible.
506 */
508write(ostream &out) const {
509 _lock->lock();
510 do_write(out);
511 _lock->unlock();
512}
513
514/**
515 *
516 */
517TypeRegistry::
518TypeRegistry() {
519 // We'll start out our handle_registry with a default entry for the
520 // TypeHandles whose index number is zero, and are therefore (probably)
521 // uninitialized.
522 _handle_registry.push_back(nullptr);
523
524 _derivations_fresh = false;
525
526 // Here's a few sanity checks on the sizes of our words. We have to put it
527 // here, at runtime, since there doesn't appear to be a cross-platform
528 // compile-time way to verify that we've chosen the right word sizes.
529 assert(sizeof(uint8_t) == 1 && sizeof(int8_t) == 1);
530 assert(sizeof(uint16_t) == 2 && sizeof(int16_t) == 2);
531 assert(sizeof(uint32_t) == 4 && sizeof(int32_t) == 4);
532 assert(sizeof(uint64_t) == 8 && sizeof(int64_t) == 8);
533
534 assert(sizeof(PN_float32) == 4);
535 assert(sizeof(PN_float64) == 8);
536}
537
538/**
539 * Constructs the TypeRegistry object for the first time.
540 */
541void TypeRegistry::
542init_global_pointer() {
543 init_lock();
545 _global_pointer = new TypeRegistry;
546}
547
548/**
549 * Rebuilds the derivation data structures after some derivation relationship
550 * has been modified, so that class relationships can quickly be determined.
551 */
552void TypeRegistry::
553rebuild_derivations() {
554 // First, remove all of the old data from the last type
555 // rebuild_derivations() was called.
556 _root_classes.clear();
557
558 HandleRegistry::iterator hi;
559 for (hi = _handle_registry.begin();
560 hi != _handle_registry.end();
561 ++hi) {
562 TypeRegistryNode *node = *hi;
563 if (node != nullptr) {
564 node->clear_subtree();
565 }
566 }
567
568 // Start by getting the list of root classes: those classes which do not
569 // derive from anything.
570 for (hi = _handle_registry.begin();
571 hi != _handle_registry.end();
572 ++hi) {
573 TypeRegistryNode *node = *hi;
574 if (node != nullptr && node->_parent_classes.empty()) {
575 _root_classes.push_back(node);
576
577 // Also, for each root class, define a subtree.
578 node->define_subtree();
579 }
580 }
581}
582
583/**
584 * The private implementation of write(), this assumes the lock is already
585 * held.
586 */
587void TypeRegistry::
588do_write(ostream &out) const {
589 // Recursively write out the tree, starting from each node that has no
590 // parent.
591 HandleRegistry::const_iterator hi;
592 for (hi = _handle_registry.begin();
593 hi != _handle_registry.end();
594 ++hi) {
595 const TypeRegistryNode *root = *hi;
596 if (root != nullptr && root->_parent_classes.empty()) {
597 write_node(out, 2, root);
598 }
599 }
600}
601
602/**
603 * Writes a single TypeRegistryNode out, along with all of its descendants.
604 * Assumes the lock is already held.
605 */
606void TypeRegistry::
607write_node(ostream &out, int indent_level, const TypeRegistryNode *node) const {
608 indent(out, indent_level) << node->_handle.get_index() << " " << node->_name;
609 if (!node->_parent_classes.empty()) {
610 out << " : " << node->_parent_classes[0]->_name;
611 for (int pi = 1; pi < (int)node->_parent_classes.size(); pi++) {
612 out << ", " << node->_parent_classes[pi]->_name;
613 }
614 }
615 out << "\n";
616
617 for (int i = 0; i < (int)node->_child_classes.size(); i++) {
618 write_node(out, indent_level + 2, node->_child_classes[i]);
619 }
620}
621
622/**
623 * Called by look_up when it detects an invalid TypeHandle pointer. In non-
624 * release builds, this method will do what it can to recover from this and
625 * initialize the type anyway.
626 *
627 * Assumes the lock is already held.
628 */
629TypeRegistryNode *TypeRegistry::
630look_up_invalid(TypeHandle handle, TypedObject *object) const {
631#ifndef NDEBUG
632 if (handle._index == 0) {
633 // The TypeHandle is unregistered. This is an error condition.
634
635 if (object != nullptr) {
636 // But we're lucky enough to have a TypedObject pointer handy! Maybe we
637 // can use it to resolve the error. We have to drop the lock while we
638 // do this, so we don't get a recursive lock.
639 _lock->unlock();
640 handle = object->force_init_type();
641 _lock->lock();
642
643 if (handle._index == 0) {
644 // Strange.
645 cerr
646 << "Unable to force_init_type() on unregistered TypeHandle.\n";
647 return nullptr;
648 }
649
650 // Now get the name for printing. We can't use TypeHandle:: get_name()
651 // since that recursively calls look_up().
652 ostringstream name;
653 if (handle._index > 0 && handle._index < (int)_handle_registry.size()) {
654 TypeRegistryNode *rnode = _handle_registry[handle._index];
655 if (rnode != nullptr) {
656 name << rnode->_name;
657 name << " (index " << handle._index << ")";
658 } else {
659 name << "NULL (index " << handle._index << ")";
660 }
661 } else {
662 name << "index " << handle._index;
663 }
664
665 if (handle == object->get_type()) {
666 // Problem solved!
667 cerr
668 << "Type " << name.str() << " was unregistered!\n";
669 } else {
670 // No good; it looks like the TypeHandle belongs to a class that
671 // defined get_type(), but didn't define force_init_type().
672 cerr
673 << "Attempt to reference unregistered TypeHandle. Type is of some\n"
674 << "class derived from type " << name.str() << " that doesn't define\n"
675 << "a good force_init_type() method.\n";
676 return nullptr;
677 }
678
679 } else {
680 // We don't have a TypedObject pointer, so there's nothing we can do
681 // about it.
682 cerr
683 << "Attempt to reference unregistered TypeHandle!\n"
684 << "Registered TypeHandles are:\n";
685 do_write(cerr);
686 return nullptr;
687 }
688 }
689
690 if (handle._index < 0 ||
691 handle._index >= (int)_handle_registry.size()) {
692 cerr
693 << "Invalid TypeHandle index " << handle._index
694 << "! Is memory corrupt?\n";
695 return nullptr;
696 }
697#endif // NDEBUG
698
699 return _handle_registry[handle._index];
700}
701
702/**
703
704 */
705extern "C" int
706get_best_parent_from_Set(int id, const std::set<int> &this_set) {
707 // most common case..
708 if (this_set.find(id) != this_set.end()) {
709 return id;
710 }
711
713 if (th == TypeHandle::none()) {
714 return -1;
715 }
716
717 return th.get_best_parent_from_Set(this_set);
718}
A fake mutex implementation for single-threaded applications that don't need any synchronization cont...
TypeHandle is the identifier used to differentiate C++ class types.
Definition typeHandle.h:81
get_index
Returns the integer index associated with this TypeHandle.
Definition typeHandle.h:135
int get_best_parent_from_Set(const std::set< int > &legal_vals) const
Return the Index of the BEst fit Classs from a set.
This is a single entry in the TypeRegistry.
static bool is_derived_from(const TypeRegistryNode *child, const TypeRegistryNode *base)
Returns true if the child RegistryNode represents a class that inherits directly or indirectly from t...
void clear_subtree()
Removes any subtree definition previously set up via define_subtree(), in preparation for rebuilding ...
static TypeHandle get_parent_towards(const TypeRegistryNode *child, const TypeRegistryNode *base)
Returns the first parent class of child that is a descendant of the indicated base class.
void define_subtree()
Indicates that this TypeRegistryNode is the top of a subtree within the inheritance graph (typically,...
The TypeRegistry class maintains all the assigned TypeHandles in a given system.
get_typehandle
Returns the nth TypeHandle in the system.
get_num_typehandles
Returns the total number of unique TypeHandles in the system.
void record_alternate_name(TypeHandle type, const std::string &name)
Indicates an alternate name for the same type.
TypeHandle find_type_by_id(int id) const
Looks for a previously-registered type with the given id number (as returned by TypeHandle::get_index...
TypeHandle get_parent_class(TypeHandle child, int index) const
Returns the nth parent class of this type.
void write(std::ostream &out) const
Makes an attempt to format the entire TypeRegistry in a nice way that shows the derivation tree as in...
TypeHandle register_dynamic_type(const std::string &name)
Registers a new type on-the-fly, presumably at runtime.
get_num_root_classes
Returns the number of root classes–that is, classes that do not inherit from any other classes–known ...
void record_derivation(TypeHandle child, TypeHandle parent)
Records that the type referenced by child inherits directly from the type referenced by parent.
TypeHandle get_child_class(TypeHandle child, int index) const
Returns the nth child class of this type.
bool is_derived_from(TypeHandle child, TypeHandle base, TypedObject *child_object)
Returns true if the first type is derived from the second type, false otherwise.
static TypeRegistry * ptr()
Returns the pointer to the global TypeRegistry object.
int get_num_child_classes(TypeHandle child, TypedObject *child_object) const
Returns the number of child classes that the indicated type is known to have.
std::string get_name(TypeHandle type, TypedObject *object) const
Returns the name of the indicated type.
TypeHandle find_type(const std::string &name) const
Looks for a previously-registered type of the given name.
TypeHandle get_parent_towards(TypeHandle child, TypeHandle base, TypedObject *child_object)
Returns the parent of the indicated child class that is in a direct line of inheritance to the indica...
int get_num_parent_classes(TypeHandle child, TypedObject *child_object) const
Returns the number of parent classes that the indicated type is known to have.
bool register_type(TypeHandle &type_handle, const std::string &name)
Creates a new Type of the given name and assigns a unique value to the type_handle.
static void reregister_types()
Walks through the TypeRegistry tree and makes sure that each type that was previously registered is *...
get_root_class
Returns the nth root class in the system.
This is an abstract class that all classes which use TypeHandle, and also provide virtual functions t...
Definition typedObject.h:88
void init_memory_hook()
Any code that might need to use PANDA_MALLOC or PANDA_FREE, or any methods of the global memory_hook ...
Definition dtoolbase.cxx:38
std::ostream & indent(std::ostream &out, int indent_level)
A handy function for doing text formatting.
Definition indent.cxx:20
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.