Main Page   Class Hierarchy   Alphabetical List   Compound List   File List   Compound Members   File Members  

panda/src/express/typeRegistry.cxx

Go to the documentation of this file.
00001 // Filename: typeRegistry.cxx
00002 // Created by:  drose (06Aug01)
00003 //
00004 ////////////////////////////////////////////////////////////////////
00005 //
00006 // PANDA 3D SOFTWARE
00007 // Copyright (c) 2001, Disney Enterprises, Inc.  All rights reserved
00008 //
00009 // All use of this software is subject to the terms of the Panda 3d
00010 // Software license.  You should have received a copy of this license
00011 // along with this source code; you will also find a current copy of
00012 // the license at http://www.panda3d.org/license.txt .
00013 //
00014 // To contact the maintainers of this program write to
00015 // panda3d@yahoogroups.com .
00016 //
00017 ////////////////////////////////////////////////////////////////////
00018 
00019 #include "typeRegistry.h"
00020 #include "typedObject.h"
00021 #include "indent.h"
00022 #include "config_express.h"
00023 
00024 #include <algorithm>
00025 
00026 // In general, we use the express_cat->info() syntax in this file
00027 // (instead of express_cat.info()), because much of this work is done at
00028 // static init time, and we must use the arrow syntax to force
00029 // initialization of the express_cat category.
00030 
00031 TypeRegistry *TypeRegistry::_global_pointer = NULL;
00032 
00033 
00034 ////////////////////////////////////////////////////////////////////
00035 //     Function: TypeRegistry::register_type
00036 //       Access: Public
00037 //  Description: Creates a new Type of the given name and assigns a
00038 //               unique value to the type_handle.  All type names must
00039 //               be unique.  If the type name has already been used,
00040 //               the supplied type_handle value must match the name's
00041 //               assigned type_handle or an error is triggered.
00042 //               Returns true if the name wasn't defined before, false
00043 //               if it was.
00044 ////////////////////////////////////////////////////////////////////
00045 bool TypeRegistry::
00046 register_type(TypeHandle &type_handle, const string &name) {
00047   if (type_handle != TypeHandle::none()) {
00048     // Here's a type that was already registered.  Just make sure
00049     // everything's still kosher.
00050     TypeRegistryNode *rnode = look_up(type_handle, NULL);
00051     if (&type_handle == &rnode->_ref) {
00052       // No problem.
00053       nassertr(rnode->_name == name, false);
00054       return false;
00055     }
00056   }
00057 
00058   NameRegistry::iterator ri;
00059   ri = _name_registry.find(name);
00060 
00061   if (ri == _name_registry.end()) {
00062     // The name was not already used; this is the first time this
00063     // class has been defined.
00064 
00065 #ifdef NOTIFY_DEBUG
00066     // This code runs at static init time, so cannot use the
00067     // express_cat.is_spam() syntax.
00068     if (express_cat->is_spam()) {
00069       express_cat->spam() << "Registering type " << name << "\n";
00070     }
00071 #endif
00072 
00073     TypeHandle new_handle;
00074     new_handle._index = _handle_registry.size();
00075 
00076     TypeRegistryNode *rnode = new TypeRegistryNode(new_handle, name, type_handle);
00077     _handle_registry.push_back(rnode);
00078     _name_registry[name] = rnode;
00079     _derivations_fresh = false;
00080 
00081     type_handle = new_handle;
00082     return true;
00083   }
00084   TypeRegistryNode *rnode = (*ri).second;
00085   nassertr(rnode->_name == (*ri).first, false);
00086   nassertr(rnode->_handle._index >= 0 &&
00087            rnode->_handle._index < (int)_handle_registry.size(), false);
00088   nassertr(_handle_registry[rnode->_handle._index] == rnode, false);
00089   nassertr(rnode->_handle._index != 0, false);
00090 
00091   // The name was previously used; make sure the type_handle matches.
00092   if (&type_handle == &rnode->_ref) {
00093     // Ok, this was just a repeated attempt to register the same type.
00094 
00095     if (type_handle == rnode->_handle) {
00096       // No problem.
00097       return false;
00098     }
00099     // But wait--the type_handle has changed!  We kept a reference to
00100     // the static _type_handle member in the class that was passed in
00101     // at the first call to register_type(), and we got the same
00102     // reference passed in this time, but now it's different!  Bad
00103     // juju.
00104     express_cat->error()
00105       << "Reregistering " << name << "\n";
00106     type_handle == rnode->_handle;
00107     return false;
00108   }
00109 
00110   if (type_handle != rnode->_handle) {
00111     // Hmm, we seem to have a contradictory type registration!
00112     express_cat->warning()
00113       << "Attempt to register type " << name << " more than once!\n";
00114 
00115     // This is invalid, but we'll allow it anyway.  It seems to happen
00116     // for some reason under GNU libc5 that we occasionally end up
00117     // with two legitimate copies of the same class object in
00118     // memory--each with its own static _type_handle member.
00119 
00120     type_handle = rnode->_handle;
00121   }
00122   return false;
00123 }
00124 
00125 ////////////////////////////////////////////////////////////////////
00126 //     Function: TypeRegistry::register_dynamic_type
00127 //       Access: Public
00128 //  Description: Registers a new type on-the-fly, presumably at
00129 //               runtime.  A new TypeHandle is returned if the
00130 //               typename was not seen before; otherwise the same
00131 //               TypeHandle that was last used for this typename is
00132 //               returned.
00133 ////////////////////////////////////////////////////////////////////
00134 TypeHandle TypeRegistry::
00135 register_dynamic_type(const string &name) {
00136   NameRegistry::iterator ri;
00137   ri = _name_registry.find(name);
00138 
00139   if (ri == _name_registry.end()) {
00140     // The name was not already used; this is the first time this
00141     // class has been defined.
00142 
00143 #ifdef NOTIFY_DEBUG
00144     // This code runs at static init time, so cannot use the
00145     // express_cat.is_spam() syntax.
00146     if (express_cat->is_spam()) {
00147       express_cat->spam() << "Registering type " << name << "\n";
00148     }
00149 #endif
00150 
00151     // We must dynamically allocate a new handle so the TypeRegistryNode
00152     // has something unique to point to.  This doesn't really mean
00153     // anything, though.
00154     TypeHandle *new_handle = new TypeHandle;
00155     new_handle->_index = _handle_registry.size();
00156 
00157     TypeRegistryNode *rnode = new TypeRegistryNode(*new_handle, name, *new_handle);
00158     _handle_registry.push_back(rnode);
00159     _name_registry[name] = rnode;
00160     _derivations_fresh = false;
00161 
00162     return *new_handle;
00163   }
00164 
00165   // Return the TypeHandle previously obtained.
00166   TypeRegistryNode *rnode = (*ri).second;
00167   return rnode->_handle;
00168 }
00169 
00170 ////////////////////////////////////////////////////////////////////
00171 //     Function: TypeRegistry::record_derivation
00172 //       Access: Public
00173 //  Description: Records that the type referenced by child inherits
00174 //               directly from the type referenced by parent.  In the
00175 //               event of multiple inheritance, this should be called
00176 //               once for each parent class.
00177 ////////////////////////////////////////////////////////////////////
00178 void TypeRegistry::
00179 record_derivation(TypeHandle child, TypeHandle parent) {
00180   TypeRegistryNode *cnode = look_up(child, NULL);
00181   nassertv(cnode != (TypeRegistryNode *)NULL);
00182   TypeRegistryNode *pnode = look_up(parent, NULL);
00183   nassertv(pnode != (TypeRegistryNode *)NULL);
00184 
00185   // First, we'll just run through the list to make sure we hadn't
00186   // already made this connection.
00187   TypeRegistryNode::Classes::iterator ni;
00188   ni = find(cnode->_parent_classes.begin(), cnode->_parent_classes.end(),
00189             pnode);
00190 
00191   if (ni == cnode->_parent_classes.end()) {
00192     cnode->_parent_classes.push_back(pnode);
00193     pnode->_child_classes.push_back(cnode);
00194     _derivations_fresh = false;
00195   }
00196 }
00197 
00198 ////////////////////////////////////////////////////////////////////
00199 //     Function: TypeRegistry::record_alternate_name
00200 //       Access: Public
00201 //  Description: Indicates an alternate name for the same type.  This
00202 //               is particularly useful when a type has changed names,
00203 //               since the type is stored in a Bam file by name;
00204 //               setting the original name as the alternate will allow
00205 //               the type to be correctly read from old Bam files.
00206 ////////////////////////////////////////////////////////////////////
00207 void TypeRegistry::
00208 record_alternate_name(TypeHandle type, const string &name) {
00209   TypeRegistryNode *rnode = look_up(type, (TypedObject *)NULL);
00210   if (rnode != (TypeRegistryNode *)NULL) {
00211     NameRegistry::iterator ri =
00212       _name_registry.insert(NameRegistry::value_type(name, rnode)).first;
00213     if ((*ri).second != rnode) {
00214       express_cat.warning()
00215         << "Name " << name << " already assigned to TypeHandle "
00216         << rnode->_name << "; cannot reassign to " << type << "\n";
00217     }
00218   }
00219 }
00220 
00221 ////////////////////////////////////////////////////////////////////
00222 //     Function: TypeRegistry::find_type
00223 //       Access: Public
00224 //  Description: Looks for a previously-registered type of the given
00225 //               name.  Returns its TypeHandle if it exists, or
00226 //               TypeHandle::none() if there is no such type.
00227 ////////////////////////////////////////////////////////////////////
00228 TypeHandle TypeRegistry::
00229 find_type(const string &name) const {
00230   NameRegistry::const_iterator ri;
00231   ri = _name_registry.find(name);
00232   if (ri == _name_registry.end()) {
00233     return TypeHandle::none();
00234   } else {
00235     return (*ri).second->_handle;
00236   }
00237 }
00238 
00239 
00240 ////////////////////////////////////////////////////////////////////
00241 //     Function: TypeRegistry::get_name
00242 //       Access: Public
00243 //  Description: Returns the name of the indicated type.
00244 //
00245 //               The "object" pointer is an optional pointer to the
00246 //               TypedObject class that owns this TypeHandle.  It is
00247 //               only used in case the TypeHandle is inadvertantly
00248 //               undefined.
00249 ////////////////////////////////////////////////////////////////////
00250 string TypeRegistry::
00251 get_name(TypeHandle type, TypedObject *object) const {
00252   TypeRegistryNode *rnode = look_up(type, object);
00253   nassertr(rnode != (TypeRegistryNode *)NULL, "");
00254   return rnode->_name;
00255 }
00256 
00257 ////////////////////////////////////////////////////////////////////
00258 //     Function: TypeRegistry::get_num_root_classes
00259 //       Access: Public
00260 //  Description: Returns the number of root classes--that is, classes
00261 //               that do not inherit from any other classes--known in
00262 //               the system.
00263 ////////////////////////////////////////////////////////////////////
00264 int TypeRegistry::
00265 get_num_root_classes() {
00266   freshen_derivations();
00267   return _root_classes.size();
00268 }
00269 
00270 ////////////////////////////////////////////////////////////////////
00271 //     Function: TypeRegistry::get_root_class
00272 //       Access: Public
00273 //  Description: Returns the nth root class in the system.  See
00274 //               get_num_root_classes().
00275 ////////////////////////////////////////////////////////////////////
00276 TypeHandle TypeRegistry::
00277 get_root_class(int n) {
00278   freshen_derivations();
00279   nassertr(n >= 0 && n < get_num_root_classes(), TypeHandle::none());
00280   return _root_classes[n]->_handle;
00281 }
00282 
00283 ////////////////////////////////////////////////////////////////////
00284 //     Function: TypeRegistry::get_num_parent_classes
00285 //       Access: Public
00286 //  Description: Returns the number of parent classes that the
00287 //               indicated type is known to have.  This may then be
00288 //               used to index into get_parent_class().  The result
00289 //               will be 0 if this class does not inherit from any
00290 //               other classes, 1 if normal, single inheritance is in
00291 //               effect, or greater than one if multiple inheritance
00292 //               is in effect.
00293 //
00294 //               The "object" pointer is an optional pointer to the
00295 //               TypedObject class that owns this TypeHandle.  It is
00296 //               only used in case the TypeHandle is inadvertantly
00297 //               undefined.
00298 ////////////////////////////////////////////////////////////////////
00299 int TypeRegistry::
00300 get_num_parent_classes(TypeHandle child, TypedObject *child_object) const {
00301   TypeRegistryNode *rnode = look_up(child, child_object);
00302   nassertr(rnode != (TypeRegistryNode *)NULL, 0);
00303   return rnode->_parent_classes.size();
00304 }
00305 
00306 ////////////////////////////////////////////////////////////////////
00307 //     Function: TypeRegistry::get_parent_class
00308 //       Access: Public
00309 //  Description: Returns the nth parent class of this type.  The index
00310 //               should be in the range 0 <= index <
00311 //               get_num_parent_classes().
00312 ////////////////////////////////////////////////////////////////////
00313 TypeHandle TypeRegistry::
00314 get_parent_class(TypeHandle child, int index) const {
00315   TypeRegistryNode *rnode = look_up(child, (TypedObject *)NULL);
00316   nassertr(rnode != (TypeRegistryNode *)NULL, TypeHandle::none());
00317   nassertr(index >= 0 && index < (int)rnode->_parent_classes.size(),
00318            TypeHandle::none());
00319   return rnode->_parent_classes[index]->_handle;
00320 }
00321 
00322 ////////////////////////////////////////////////////////////////////
00323 //     Function: TypeRegistry::get_num_child_classes
00324 //       Access: Public
00325 //  Description: Returns the number of child classes that the
00326 //               indicated type is known to have.  This may then be
00327 //               used to index into get_child_class().
00328 //
00329 //               The "object" pointer is an optional pointer to the
00330 //               TypedObject class that owns this TypeHandle.  It is
00331 //               only used in case the TypeHandle is inadvertantly
00332 //               undefined.
00333 ////////////////////////////////////////////////////////////////////
00334 int TypeRegistry::
00335 get_num_child_classes(TypeHandle child, TypedObject *child_object) const {
00336   TypeRegistryNode *rnode = look_up(child, child_object);
00337   nassertr(rnode != (TypeRegistryNode *)NULL, 0);
00338   return rnode->_child_classes.size();
00339 }
00340 
00341 ////////////////////////////////////////////////////////////////////
00342 //     Function: TypeRegistry::get_child_class
00343 //       Access: Public
00344 //  Description: Returns the nth child class of this type.  The index
00345 //               should be in the range 0 <= index <
00346 //               get_num_child_classes().
00347 ////////////////////////////////////////////////////////////////////
00348 TypeHandle TypeRegistry::
00349 get_child_class(TypeHandle child, int index) const {
00350   TypeRegistryNode *rnode = look_up(child, (TypedObject *)NULL);
00351   nassertr(rnode != (TypeRegistryNode *)NULL, TypeHandle::none());
00352   nassertr(index >= 0 && index < (int)rnode->_child_classes.size(),
00353            TypeHandle::none());
00354   return rnode->_child_classes[index]->_handle;
00355 }
00356 
00357 ////////////////////////////////////////////////////////////////////
00358 //     Function: TypeRegistry::get_parent_towards
00359 //       Access: Public
00360 //  Description: Returns the parent of the indicated child class that
00361 //               is in a direct line of inheritance to the indicated
00362 //               ancestor class.  This is useful in the presence of
00363 //               multiple inheritance to try to determine what
00364 //               properties an unknown type may have.
00365 //
00366 //               The "object" pointer is an optional pointer to the
00367 //               TypedObject class that owns this TypeHandle.  It is
00368 //               only used in case the TypeHandle is inadvertantly
00369 //               undefined.
00370 ////////////////////////////////////////////////////////////////////
00371 TypeHandle TypeRegistry::
00372 get_parent_towards(TypeHandle child, TypeHandle base,
00373                    TypedObject *child_object) {
00374   const TypeRegistryNode *child_node = look_up(child, child_object);
00375   const TypeRegistryNode *base_node = look_up(base, NULL);
00376   nassertr(child_node != (TypeRegistryNode *)NULL && 
00377            base_node != (TypeRegistryNode *)NULL, TypeHandle::none());
00378   freshen_derivations();
00379   return TypeRegistryNode::get_parent_towards(child_node, base_node);
00380 }
00381 
00382 
00383 ////////////////////////////////////////////////////////////////////
00384 //     Function: TypeRegistry::reregister_types
00385 //       Access: Public, Static
00386 //  Description: Walks through the TypeRegistry tree and makes sure
00387 //               that each type that was previously registered is
00388 //               *still* registered.  This seems to get broken in
00389 //               certain circumstances when compiled against libc5--it
00390 //               is as if the static initializer stomps on the
00391 //               _type_handle values of each class after they've been
00392 //               registered.
00393 ////////////////////////////////////////////////////////////////////
00394 void TypeRegistry::
00395 reregister_types() {
00396   HandleRegistry::iterator ri;
00397   TypeRegistry *reg = ptr();
00398   for (ri = reg->_handle_registry.begin();
00399        ri != reg->_handle_registry.end();
00400        ++ri) {
00401     TypeRegistryNode *rnode = (*ri);
00402     if (rnode != NULL && rnode->_handle != rnode->_ref) {
00403       express_cat->warning()
00404         << "Reregistering " << rnode->_name << "\n";
00405     }
00406   }
00407 }
00408 
00409 
00410 ////////////////////////////////////////////////////////////////////
00411 //     Function: TypeRegistry::write
00412 //       Access: Public
00413 //  Description: Makes an attempt to format the entire TypeRegistry in
00414 //               a nice way that shows the derivation tree as
00415 //               intelligently as possible.
00416 ////////////////////////////////////////////////////////////////////
00417 void TypeRegistry::
00418 write(ostream &out) const {
00419   // Recursively write out the tree, starting from each node that has
00420   // no parent.
00421   HandleRegistry::const_iterator hi;
00422   for (hi = _handle_registry.begin();
00423        hi != _handle_registry.end();
00424        ++hi) {
00425     const TypeRegistryNode *root = *hi;
00426     if (root != NULL && root->_parent_classes.empty()) {
00427       write_node(out, 2, root);
00428     }
00429   }
00430 }
00431 
00432 ////////////////////////////////////////////////////////////////////
00433 //     Function: TypeRegistry::ptr
00434 //       Access: Public, Static
00435 //  Description: Returns the pointer to the global TypeRegistry
00436 //               object.
00437 ////////////////////////////////////////////////////////////////////
00438 TypeRegistry *TypeRegistry::
00439 ptr() {
00440   if (_global_pointer == NULL) {
00441 #ifdef NOTIFY_DEBUG
00442     if (express_cat->is_spam()) {
00443       express_cat->spam()
00444         << "Creating global TypeRegistry\n";
00445     }
00446 #endif
00447     init_global_pointer();
00448   }
00449   return _global_pointer;
00450 }
00451 
00452 ////////////////////////////////////////////////////////////////////
00453 //     Function: TypeRegistry::Constructor
00454 //       Access: Private
00455 //  Description:
00456 ////////////////////////////////////////////////////////////////////
00457 TypeRegistry::
00458 TypeRegistry() {
00459   // We'll start out our handle_registry with a default entry for the
00460   // TypeHandles whose index number is zero, and are therefore
00461   // (probably) uninitialized.
00462   _handle_registry.push_back(NULL);
00463 
00464   _derivations_fresh = false;
00465 }
00466 
00467 ////////////////////////////////////////////////////////////////////
00468 //     Function: TypeRegistry::init_global_pointer
00469 //       Access: Private, Static
00470 //  Description: Constructs the TypeRegistry object for the first
00471 //               time.  It is initially created on the local heap,
00472 //               then as soon as shared memory becomes available, it
00473 //               should be moved into shared memory.
00474 ////////////////////////////////////////////////////////////////////
00475 void TypeRegistry::
00476 init_global_pointer() {
00477   _global_pointer = new TypeRegistry;
00478 
00479   // Now that we've created the TypeRegistry, we can assign this
00480   // Config variable.
00481   TypeRegistryNode::_paranoid_inheritance = get_paranoid_inheritance();
00482 }
00483 
00484 ////////////////////////////////////////////////////////////////////
00485 //     Function: TypeRegistry::rebuild_derivations
00486 //       Access: Private
00487 //  Description: Rebuilds the derivation data structures after some
00488 //               derivation relationship has been modified, so that
00489 //               class relationships can quickly be determined.
00490 ////////////////////////////////////////////////////////////////////
00491 void TypeRegistry::
00492 rebuild_derivations() {
00493 #ifdef NOTIFY_DEBUG
00494   express_cat->debug()
00495     << "Rebuilding derivation tree.\n";
00496 #endif
00497 
00498   // First, remove all of the old data from the last type
00499   // rebuild_derivations() was called.
00500   _root_classes.clear();
00501 
00502   HandleRegistry::iterator hi;
00503   for (hi = _handle_registry.begin();
00504        hi != _handle_registry.end();
00505        ++hi) {
00506     TypeRegistryNode *node = *hi;
00507     if (node != (TypeRegistryNode *)NULL) {
00508       node->clear_subtree();
00509     }
00510   }
00511   
00512   // Start by getting the list of root classes: those classes which do
00513   // not derive from anything.
00514   for (hi = _handle_registry.begin();
00515        hi != _handle_registry.end();
00516        ++hi) {
00517     TypeRegistryNode *node = *hi;
00518     if (node != NULL && node->_parent_classes.empty()) {
00519       _root_classes.push_back(node);
00520 
00521       // Also, for each root class, define a subtree.
00522       node->define_subtree();
00523     }
00524   }
00525 }
00526 
00527 ////////////////////////////////////////////////////////////////////
00528 //     Function: TypeRegistry::write_node
00529 //       Access: Private
00530 //  Description: Writes a single TypeRegistryNode out, along with all of
00531 //               its descendants.
00532 ////////////////////////////////////////////////////////////////////
00533 void TypeRegistry::
00534 write_node(ostream &out, int indent_level, const TypeRegistryNode *node) const {
00535   indent(out, indent_level) << node->_handle.get_index() << " " << node->_name;
00536   if (!node->_parent_classes.empty()) {
00537     out << " : " << node->_parent_classes[0]->_name;
00538     for (int pi = 1; pi < (int)node->_parent_classes.size(); pi++) {
00539       out << ", " << node->_parent_classes[pi]->_name;
00540     }
00541   }
00542   out << "\n";
00543 
00544   for (int i = 0; i < (int)node->_child_classes.size(); i++) {
00545     write_node(out, indent_level + 2, node->_child_classes[i]);
00546   }
00547 }
00548 
00549 #ifndef NDEBUG
00550 // This function is only non-inline if NDEBUG is not defined.
00551 // Otherwise, it is inline and its definition appears in
00552 // typeRegistry.I.
00553 
00554 ////////////////////////////////////////////////////////////////////
00555 //     Function: TypeRegistry::look_up
00556 //       Access: Private
00557 //  Description: Returns the TypeRegistryNode associated with the
00558 //               indicated TypeHandle.  If there is no associated
00559 //               TypeRegistryNode, reports an error condition and
00560 //               returns NULL.
00561 //
00562 //               The associated TypedObject pointer is the pointer to
00563 //               the object that owns the handle, if available.  It is
00564 //               only used in an error condition, if for some reason
00565 //               the handle was uninitialized.
00566 ////////////////////////////////////////////////////////////////////
00567 TypeRegistryNode *TypeRegistry::
00568 look_up(TypeHandle handle, TypedObject *object) const {
00569   if (handle._index == 0) {
00570     // The TypeHandle is unregistered.  This is an error condition.
00571 
00572     if (object != NULL) {
00573       // But we're lucky enough to have a TypedObject pointer handy!
00574       // Maybe we can use it to resolve the error.
00575       handle = object->force_init_type();
00576       if (handle._index == 0) {
00577         // Strange.
00578         express_cat->error()
00579           << "Unable to force_init_type() on unregistered TypeHandle.\n";
00580         nassertr(false, NULL);
00581       }
00582       if (handle == object->get_type()) {
00583         // Problem solved!
00584         express_cat->warning()
00585           << "Type " << handle << " was unregistered!\n";
00586       } else {
00587         // No good; it looks like the TypeHandle belongs to a class
00588         // that defined get_type(), but didn't define
00589         // force_init_type().
00590         express_cat->error()
00591           << "Attempt to reference unregistered TypeHandle.  Type is of some\n"
00592           << "class derived from " << handle << " that doesn't define a good\n"
00593           << "force_init_type() method.\n";
00594         nassertr(false, NULL);
00595       }
00596 
00597     } else {
00598       // We don't have a TypedObject pointer, so there's nothing we
00599       // can do about it.
00600       express_cat->error()
00601         << "Attempt to reference unregistered TypeHandle!\n"
00602         << "Registered TypeHandles are:\n";
00603       write(express_cat->error(false));
00604       nassertr(false, NULL);
00605     }
00606   }
00607 
00608   if (handle._index < 0 ||
00609       handle._index >= (int)_handle_registry.size()) {
00610     express_cat->fatal()
00611       << "Invalid TypeHandle index " << handle._index
00612       << "!  Is memory corrupt?\n";
00613     nassertr(false, NULL);
00614   }
00615 
00616   return _handle_registry[handle._index];
00617 }
00618 #endif  // NDEBUG
00619 

Generated on Fri May 2 00:38:44 2003 for Panda by doxygen1.3