Browse Source

struct constructors

David Rose 21 years ago
parent
commit
3f96b1adf9

+ 83 - 15
direct/src/dcparser/dcClass.cxx

@@ -34,6 +34,7 @@ DCClass(const string &name, bool is_struct, bool bogus_class) :
   _bogus_class(bogus_class)
 {
   _number = -1;
+  _constructor = NULL;
       
 #ifdef HAVE_PYTHON
   _class_def = NULL;
@@ -47,6 +48,10 @@ DCClass(const string &name, bool is_struct, bool bogus_class) :
 ////////////////////////////////////////////////////////////////////
 DCClass::
 ~DCClass() {
+  if (_constructor != (DCField *)NULL) {
+    delete _constructor;
+  }
+
   Fields::iterator fi;
   for (fi = _fields.begin(); fi != _fields.end(); ++fi) {
     delete (*fi);
@@ -112,6 +117,29 @@ get_parent() const {
   nassertr(has_parent(), NULL);
   return _parents.front();
 }
+  
+////////////////////////////////////////////////////////////////////
+//     Function: DCClass::has_constructor
+//       Access: Published
+//  Description: Returns true if this class has a constructor method,
+//               false if it just uses the default constructor.
+////////////////////////////////////////////////////////////////////
+bool DCClass::
+has_constructor() const {
+  return (_constructor != (DCField *)NULL);
+}
+  
+////////////////////////////////////////////////////////////////////
+//     Function: DCClass::get_constructor
+//       Access: Published
+//  Description: Returns the constructor method for this class if it
+//               is defined, or NULL if the class uses the default
+//               constructor.
+////////////////////////////////////////////////////////////////////
+DCField *DCClass::
+get_constructor() const {
+  return _constructor;
+}
 
 ////////////////////////////////////////////////////////////////////
 //     Function: DCClass::get_num_fields
@@ -473,38 +501,49 @@ pack_required_field(DCPacker &packer, PyObject *distobj,
   // good, robust way to get this; presently, we just mangle the
   // "setFoo()" name of the required field into "getFoo()" and call
   // that.
-  string set_name = atom->get_name();
+  string setter_name = atom->get_name();
+
+  if (setter_name.empty()) {
+    ostringstream strm;
+    strm << "Required field is unnamed!";
+    nassert_raise(strm.str());
+    return false;
+  }
 
   if (atom->get_num_elements() == 0) {
     // It sure doesn't make sense to have a required field with no
     // parameters.  What data, exactly, is required?
     ostringstream strm;
-    strm << "Required field " << set_name << " has no parameters!";
+    strm << "Required field " << setter_name << " has no parameters!";
     nassert_raise(strm.str());
     return false;
   }
   
-  if (set_name.substr(0, 3) != string("set")) {
-    // This is required to suit our set/get mangling convention.
-    ostringstream strm;
-    strm << "Required field " << set_name << " does not begin with 'set'";
-    nassert_raise(strm.str());
-    return false;
+  string getter_name = setter_name;
+  if (setter_name.substr(0, 3) == "set") {
+    // If the original method started with "set", we mangle this
+    // directly to "get".
+    getter_name[0] = 'g';
+
+  } else {
+    // Otherwise, we add a "get" prefix, and capitalize the next
+    // letter.
+    getter_name = "get" + setter_name;
+    getter_name[3] = toupper(getter_name[3]);
   }
-  string get_name = set_name;
-  get_name[0] = 'g';
   
   // Now we have to look up the getter on the distributed object
   // and call it.
-  if (!PyObject_HasAttrString(distobj, (char *)get_name.c_str())) {
+  if (!PyObject_HasAttrString(distobj, (char *)getter_name.c_str())) {
     ostringstream strm;
-    strm << "Required field " << set_name
-         << " doesn't have matching field named " << get_name;
+    strm << "Distributed class " << get_name()
+         << " doesn't have getter named " << getter_name
+         << " to match required field " << setter_name;
     nassert_raise(strm.str());
     return false;
   }
   PyObject *func = 
-    PyObject_GetAttrString(distobj, (char *)get_name.c_str());
+    PyObject_GetAttrString(distobj, (char *)getter_name.c_str());
   nassertr(func != (PyObject *)NULL, false);
   
   PyObject *empty_args = PyTuple_New(0);
@@ -514,7 +553,7 @@ pack_required_field(DCPacker &packer, PyObject *distobj,
   if (result == (PyObject *)NULL) {
     // We don't set this as an exception, since presumably the Python
     // method itself has already triggered a Python exception.
-    cerr << "Error when calling " << get_name << "\n";
+    cerr << "Error when calling " << getter_name << "\n";
     return false;
   }
   
@@ -695,6 +734,10 @@ write(ostream &out, bool brief, int indent_level) const {
   }
   out << "\n";
 
+  if (_constructor != (DCField *)NULL) {
+    _constructor->write(out, brief, indent_level + 2);
+  }
+
   Fields::const_iterator fi;
   for (fi = _fields.begin(); fi != _fields.end(); ++fi) {
     (*fi)->write(out, brief, indent_level + 2);
@@ -733,6 +776,11 @@ output_instance(ostream &out, bool brief, const string &prename,
 
   out << " {";
 
+  if (_constructor != (DCField *)NULL) {
+    _constructor->output(out, brief);
+    out << "; ";
+  }
+
   Fields::const_iterator fi;
   for (fi = _fields.begin(); fi != _fields.end(); ++fi) {
     (*fi)->output(out, brief);
@@ -761,6 +809,10 @@ generate_hash(HashGenerator &hashgen) const {
     hashgen.add_int((*pi)->get_number());
   }
 
+  if (_constructor != (DCField *)NULL) {
+    _constructor->generate_hash(hashgen);
+  }
+
   hashgen.add_int(_fields.size());
   Fields::const_iterator fi;
   for (fi = _fields.begin(); fi != _fields.end(); ++fi) {
@@ -779,6 +831,22 @@ generate_hash(HashGenerator &hashgen) const {
 ////////////////////////////////////////////////////////////////////
 bool DCClass::
 add_field(DCField *field) {
+  if (!_name.empty() && field->get_name() == _name) {
+    // This field is a constructor.
+    if (_constructor != (DCField *)NULL) {
+      // We already have a constructor.
+      return false;
+    }
+    if (field->as_atomic_field() == (DCAtomicField *)NULL) {
+      // The constructor must be an atomic field.
+      return false;
+    }
+    _constructor = field;
+    _fields_by_name.insert
+      (FieldsByName::value_type(field->get_name(), field));
+    return true;
+  }
+
   bool inserted = _fields_by_name.insert
     (FieldsByName::value_type(field->get_name(), field)).second;
 

+ 5 - 0
direct/src/dcparser/dcClass.h

@@ -45,6 +45,9 @@ PUBLISHED:
 
   bool has_parent() const;
   DCClass *get_parent() const;
+  
+  bool has_constructor() const;
+  DCField *get_constructor() const;
 
   int get_num_fields() const;
   DCField *get_field(int n) const;
@@ -102,6 +105,8 @@ private:
   typedef pvector<DCClass *> Parents;
   Parents _parents;
 
+  DCField *_constructor;
+
   typedef pvector<DCField *> Fields;
   Fields _fields;
 

+ 17 - 2
direct/src/dcparser/dcClassParameter.cxx

@@ -31,10 +31,23 @@ DCClassParameter(DCClass *dclass) :
 {
   set_name(dclass->get_name());
 
+  int num_fields = _dclass->get_num_inherited_fields();
+
   _has_nested_fields = true;
-  _num_nested_fields = _dclass->get_num_inherited_fields();
+  _num_nested_fields = num_fields;
+  if (_dclass->has_constructor()) {
+    _num_nested_fields++;
+  }
   _pack_type = PT_class;
 
+  _nested_fields.reserve(_num_nested_fields);
+  if (_dclass->has_constructor()) {
+    _nested_fields.push_back(_dclass->get_constructor());
+  }
+  for (int i = 0 ; i < num_fields; i++) {
+    _nested_fields.push_back(_dclass->get_inherited_field(i));
+  }
+
   // If all of the nested fields have a fixed byte size, then so does
   // the class (and its byte size is the sum of all of the nested
   // fields).
@@ -57,6 +70,7 @@ DCClassParameter(DCClass *dclass) :
 DCClassParameter::
 DCClassParameter(const DCClassParameter &copy) :
   DCParameter(copy),
+  _nested_fields(copy._nested_fields),
   _dclass(copy._dclass)
 {
 }
@@ -113,7 +127,8 @@ get_class() const {
 ////////////////////////////////////////////////////////////////////
 DCPackerInterface *DCClassParameter::
 get_nested_field(int n) const {
-  return _dclass->get_inherited_field(n);
+  nassertr(n >= 0 && n < (int)_nested_fields.size(), NULL);
+  return _nested_fields[n];
 }
 
 ////////////////////////////////////////////////////////////////////

+ 3 - 0
direct/src/dcparser/dcClassParameter.h

@@ -50,6 +50,9 @@ public:
   virtual void generate_hash(HashGenerator &hashgen) const;
 
 private:
+  typedef pvector<DCPackerInterface *> Fields;
+  Fields _nested_fields;
+
   DCClass *_dclass;
 };
 

+ 43 - 12
direct/src/dcparser/dcPacker.cxx

@@ -1126,17 +1126,36 @@ unpack_class_object(DCClass *dclass) {
   PyObject *class_def = dclass->get_class_def();
   nassertr(class_def != (PyObject *)NULL, NULL);
 
-  PyObject *object = PyObject_CallObject(class_def, NULL);
-  if (object == (PyObject *)NULL) {
-    return NULL;
+  PyObject *object = NULL;
+
+  if (!dclass->has_constructor()) {
+    // If the class uses a default constructor, go ahead and create
+    // the Python object for it now.
+    object = PyObject_CallObject(class_def, NULL);
+    if (object == (PyObject *)NULL) {
+      return NULL;
+    }
   }
 
   push();
+  if (object == (PyObject *)NULL && more_nested_fields()) {
+    // The first nested field will be the constructor.
+    const DCField *field = ((DCPackerInterface *)get_current_field())->as_field();
+    nassertr(field != (DCField *)NULL, object);
+    nassertr(field == dclass->get_constructor(), object);
+
+    set_class_element(class_def, object, field);
+
+    // By now, the object should have been constructed.
+    if (object == (PyObject *)NULL) {
+      return NULL;
+    }
+  }
   while (more_nested_fields()) {
     const DCField *field = ((DCPackerInterface *)get_current_field())->as_field();
     nassertr(field != (DCField *)NULL, object);
 
-    set_class_element(object, field);
+    set_class_element(class_def, object, field);
   }
   pop();
 
@@ -1150,10 +1169,11 @@ unpack_class_object(DCClass *dclass) {
 //     Function: DCPacker::set_class_element
 //       Access: Private
 //  Description: Unpacks the current element and stuffs it on the
-//               Python class object in whatever way is appopriate.
+//               Python class object in whatever way is appropriate.
 ////////////////////////////////////////////////////////////////////
 void DCPacker::
-set_class_element(PyObject *object, const DCField *field) {
+set_class_element(PyObject *class_def, PyObject *&object, 
+                  const DCField *field) {
   string field_name = field->get_name();
   DCPackType pack_type = get_pack_type();
 
@@ -1168,7 +1188,8 @@ set_class_element(PyObject *object, const DCField *field) {
       while (more_nested_fields()) {
         const DCField *field = ((DCPackerInterface *)get_current_field())->as_field();
         nassertv(field != (DCField *)NULL);
-        set_class_element(object, field);
+        nassertv(object != (PyObject *)NULL);
+        set_class_element(class_def, object, field);
       }
       pop();
       break;
@@ -1186,14 +1207,24 @@ set_class_element(PyObject *object, const DCField *field) {
     PyObject *element = unpack_object();
 
     if (pack_type == PT_field) {
-      PyObject *func = PyObject_GetAttrString(object, (char *)field_name.c_str());
-      if (func != (PyObject *)NULL) {
-        PyObject *result = PyObject_CallObject(func, element);
-        Py_XDECREF(result);
+      if (object == (PyObject *)NULL) {
+        // If the object hasn't been constructed yet, assume this is
+        // the constructor.
+        object = PyObject_CallObject(class_def, element);
+
+      } else {
+        if (PyObject_HasAttrString(object, (char *)field_name.c_str())) {
+          PyObject *func = PyObject_GetAttrString(object, (char *)field_name.c_str());
+          if (func != (PyObject *)NULL) {
+            PyObject *result = PyObject_CallObject(func, element);
+            Py_XDECREF(result);
+            Py_DECREF(func);
+          }
+        }
       }
-      Py_DECREF(func);
       
     } else {
+      nassertv(object != (PyObject *)NULL);
       PyObject_SetAttrString(object, (char *)field_name.c_str(), element);
     }
 

+ 3 - 2
direct/src/dcparser/dcPacker.h

@@ -27,7 +27,7 @@
 #include "dcPython.h"
 
 class DCClass;
-class DCSwitchParametera;
+class DCSwitchParameter;
 
 ////////////////////////////////////////////////////////////////////
 //       Class : DCPacker
@@ -180,7 +180,8 @@ private:
 #ifdef HAVE_PYTHON
   void pack_class_object(DCClass *dclass, PyObject *object);
   PyObject *unpack_class_object(DCClass *dclass);
-  void set_class_element(PyObject *object, const DCField *field);
+  void set_class_element(PyObject *class_def, PyObject *&object, 
+                         const DCField *field);
 #endif
 
 private: