diff --git a/dtool/src/dtoolutil/filename.cxx b/dtool/src/dtoolutil/filename.cxx index 428d4e7136..04c94b0f54 100644 --- a/dtool/src/dtoolutil/filename.cxx +++ b/dtool/src/dtoolutil/filename.cxx @@ -302,6 +302,30 @@ Filename(const Filename &dirname, const Filename &basename) { } } +#ifdef HAVE_PYTHON +//////////////////////////////////////////////////////////////////// +// Function: Filename::__reduce__ +// Access: Published +// Description: This special Python method is implement to provide +// support for the pickle module. +//////////////////////////////////////////////////////////////////// +PyObject *Filename:: +__reduce__(PyObject *self) const { + // We should return at least a 2-tuple, (Class, (args)): the + // necessary class object whose constructor we should call + // (e.g. this), and the arguments necessary to reconstruct this + // object. + PyObject *this_class = PyObject_Type(self); + if (this_class == NULL) { + return NULL; + } + + PyObject *result = Py_BuildValue("(O(s))", this_class, c_str()); + Py_DECREF(this_class); + return result; +} +#endif // HAVE_PYTHON + //////////////////////////////////////////////////////////////////// // Function: Filename::from_os_specific // Access: Published, Static diff --git a/dtool/src/dtoolutil/filename.h b/dtool/src/dtoolutil/filename.h index 6b0b37fac4..8c64d56d46 100644 --- a/dtool/src/dtoolutil/filename.h +++ b/dtool/src/dtoolutil/filename.h @@ -66,6 +66,10 @@ PUBLISHED: Filename(const Filename &dirname, const Filename &basename); INLINE ~Filename(); +#ifdef HAVE_PYTHON + PyObject *__reduce__(PyObject *self) const; +#endif + // Static constructors to explicitly create a filename that refers // to a text or binary file. This is in lieu of calling set_text() // or set_binary() or set_type(). diff --git a/dtool/src/interrogate/functionRemap.cxx b/dtool/src/interrogate/functionRemap.cxx index dc66a308d9..54a74f730e 100644 --- a/dtool/src/interrogate/functionRemap.cxx +++ b/dtool/src/interrogate/functionRemap.cxx @@ -297,6 +297,16 @@ make_wrapper_entry(FunctionIndex function_index) { assert(!iwrapper._parameters.empty()); iwrapper._parameters.front()._parameter_flags |= InterrogateFunctionWrapper::PF_is_this; + + if (_parameters.size() >= 2 && _parameters[1]._name == "self" && + TypeManager::is_pointer_to_PyObject(_parameters[1]._remap->get_orig_type())) { + // Here's a special case. If the first parameter of a nonstatic + // method is a PyObject * called "self", then we will + // automatically fill it in from the this pointer, and remove it + // from the generated parameter list. + _parameters.erase(_parameters.begin() + 1); + _flags |= F_explicit_self; + } } if (!_void_return) { @@ -382,6 +392,14 @@ get_call_str(const string &container, const vector_string &pexprs) const { } call << "("; + if (_flags & F_explicit_self) { + // Pass on the PyObject * that we stripped off above. + call << "self"; + if (_first_true_parameter < (int)_parameters.size()) { + call << ", "; + } + } + int pn = _first_true_parameter; if (pn < (int)_parameters.size()) { _parameters[pn]._remap->pass_parameter(call, get_parameter_expr(pn, pexprs)); @@ -630,6 +648,23 @@ setup_properties(const InterrogateFunction &ifunc, InterfaceMaker *interface_mak // It receives no parameters, and returns an integer. _flags |= F_size; } + + } else if (fname == "make_copy" ) { + if (_has_this && _parameters.size() == 1 && + TypeManager::is_pointer(_return_type->get_new_type())) { + // It receives no parameters, and returns a pointer. + _flags |= F_make_copy; + } + } + + } else if (_type == T_constructor) { + if (!_has_this && _parameters.size() == 1) { + if (TypeManager::unwrap(_parameters[0]._remap->get_orig_type()) == + TypeManager::unwrap(_return_type->get_orig_type())) { + // If this is the only parameter, and it's the same as the + // "this" type, this is a copy constructor. + _flags |= F_copy_constructor; + } } } diff --git a/dtool/src/interrogate/functionRemap.h b/dtool/src/interrogate/functionRemap.h index 44e08f96c1..069863fd7c 100644 --- a/dtool/src/interrogate/functionRemap.h +++ b/dtool/src/interrogate/functionRemap.h @@ -79,11 +79,14 @@ public: }; enum Flags { - F_getitem = 0x0001, - F_getitem_int = 0x0002, - F_size = 0x0004, - F_setitem = 0x0008, - F_setitem_int = 0x0010, + F_getitem = 0x0001, + F_getitem_int = 0x0002, + F_size = 0x0004, + F_setitem = 0x0008, + F_setitem_int = 0x0010, + F_make_copy = 0x0020, + F_copy_constructor = 0x0040, + F_explicit_self = 0x0080, }; typedef vector Parameters; diff --git a/dtool/src/interrogate/interfaceMaker.cxx b/dtool/src/interrogate/interfaceMaker.cxx index 3b211c88bb..2fb060dfa7 100644 --- a/dtool/src/interrogate/interfaceMaker.cxx +++ b/dtool/src/interrogate/interfaceMaker.cxx @@ -123,6 +123,10 @@ check_protocols() { int flags = 0; Functions::const_iterator fi; + for (fi = _constructors.begin(); fi != _constructors.end(); ++fi) { + Function *func = (*fi); + flags |= func->_flags; + } for (fi = _methods.begin(); fi != _methods.end(); ++fi) { Function *func = (*fi); flags |= func->_flags; @@ -140,6 +144,16 @@ check_protocols() { _protocol_types |= PT_mapping; } + if (flags & FunctionRemap::F_make_copy) { + // It's not exactly a protocol, but if we have a make_copy() + // method, we can use it to synthesize a __copy__ and __deepcopy__ + // Python method to support the copy module. + _protocol_types |= PT_make_copy; + } else if (flags & FunctionRemap::F_copy_constructor) { + // Ditto for the copy constructor. + _protocol_types |= PT_copy_constructor; + } + // Now are there any make_seq requests within this class? CPPStructType *stype = _itype._cpptype->as_struct_type(); if (stype != (CPPStructType *)NULL) { diff --git a/dtool/src/interrogate/interfaceMaker.h b/dtool/src/interrogate/interfaceMaker.h index 680d6f97c6..2eb0f2131d 100644 --- a/dtool/src/interrogate/interfaceMaker.h +++ b/dtool/src/interrogate/interfaceMaker.h @@ -110,8 +110,10 @@ public: MakeSeqs _make_seqs; enum ProtocolTypes { - PT_sequence = 0x0001, - PT_mapping = 0x0002, + PT_sequence = 0x0001, + PT_mapping = 0x0002, + PT_make_copy = 0x0004, + PT_copy_constructor = 0x0008, }; int _protocol_types; }; diff --git a/dtool/src/interrogate/interfaceMakerPythonNative.cxx b/dtool/src/interrogate/interfaceMakerPythonNative.cxx index 600dd7c1a9..df9b7bb0b4 100755 --- a/dtool/src/interrogate/interfaceMakerPythonNative.cxx +++ b/dtool/src/interrogate/interfaceMakerPythonNative.cxx @@ -97,7 +97,10 @@ RenameSet methodRenameDictionary[] = { { "operator <<=" , "__ilshift__", 1 }, { "operator >>=" , "__irshift__", 1 }, { "operator typecast bool", "__nonzero__", 0 }, - { "__nonzero__" , "__nonzero__", 0 }, + { "__nonzero__" , "__nonzero__", 0 }, + { "__reduce__" , "__reduce__", 0 }, + { "__copy__" , "__copy__", 0 }, + { "__deepcopy__" , "__deepcopy__", 0 }, { "print" , "Cprint", 0 }, { "CInterval.set_t", "_priv__cSetT", 0 }, { NULL, NULL, -1 } @@ -159,34 +162,36 @@ RenameSet classRenameDictionary[] = { /////////////////////////////////////////////////////////////////////////////////////// const char * pythonKeywords[] = { "and", - "del", - "for", - "is", - "raise", - "assert", - "elif", - "from", - "lambda", - "return", - "break", - "else", - "global", - "not", - "try", - "class", - "except", - "if", - "or", - "while", - "continue", - "exec", - "import", - "pass", - "def", - "finally", - "in", - "print", - NULL}; + "del", + "for", + "is", + "raise", + "assert", + "elif", + "from", + "lambda", + "return", + "break", + "else", + "global", + "not", + "try", + "class", + "except", + "if", + "or", + "while", + "continue", + "exec", + "import", + "pass", + "def", + "finally", + "in", + "print", + NULL +}; + /////////////////////////////////////////////////////////////////////////////////////// std::string checkKeyword(std::string & cppName) { @@ -813,69 +818,69 @@ void InterfaceMakerPythonNative::write_prototypes_class(ostream &out_code,ostrea void InterfaceMakerPythonNative::write_functions(ostream &out) { inside_python_native = true; - - out << "//********************************************************************\n"; - out << "//*** Functions for .. Global \n" ; - out << "//********************************************************************\n"; - Functions::iterator fi; - for (fi = _functions.begin(); fi != _functions.end(); ++fi) - { - Function *func = (*fi); - if(!func->_itype.is_global() && isFunctionLegal(func)) - write_function_for_top(out, NULL, func,""); - } - - Objects::iterator oi; - for (oi = _objects.begin(); oi != _objects.end(); ++oi) - { - Object *object = (*oi).second; - if(object->_itype.is_class() ||object->_itype.is_struct()) - { - if(isCppTypeLegal(object->_itype._cpptype)) - if(isExportThisRun(object->_itype._cpptype)) - write_ClasseDetails(out,object); - } - } - -// Objects::iterator oi; - for (oi = _objects.begin(); oi != _objects.end(); ++oi) + + out << "//********************************************************************\n"; + out << "//*** Functions for .. Global \n" ; + out << "//********************************************************************\n"; + Functions::iterator fi; + for (fi = _functions.begin(); fi != _functions.end(); ++fi) { - Object *object = (*oi).second; - if(!object->_itype.get_outer_class()) + Function *func = (*fi); + if(!func->_itype.is_global() && isFunctionLegal(func)) + write_function_for_top(out, NULL, func,""); + } + + Objects::iterator oi; + for (oi = _objects.begin(); oi != _objects.end(); ++oi) + { + Object *object = (*oi).second; + if(object->_itype.is_class() ||object->_itype.is_struct()) { - if(object->_itype.is_class() ||object->_itype.is_struct()) - if(isCppTypeLegal(object->_itype._cpptype)) - if(isExportThisRun(object->_itype._cpptype)) - write_module_class(out,object); + if(isCppTypeLegal(object->_itype._cpptype)) + if(isExportThisRun(object->_itype._cpptype)) + write_ClasseDetails(out,object); + } + } + + // Objects::iterator oi; + for (oi = _objects.begin(); oi != _objects.end(); ++oi) + { + Object *object = (*oi).second; + if(!object->_itype.get_outer_class()) + { + if(object->_itype.is_class() ||object->_itype.is_struct()) + if(isCppTypeLegal(object->_itype._cpptype)) + if(isExportThisRun(object->_itype._cpptype)) + write_module_class(out,object); } } inside_python_native = true; - } + //////////////////////////////////////////////////////////// // Function : write_ClasseDetails //////////////////////////////////////////////////////////// void InterfaceMakerPythonNative::write_ClasseDetails(ostream &out, Object * obj) { - Functions::iterator fi; - - //std::string cClassName = obj->_itype.get_scoped_name(); - std::string ClassName = make_safe_name(obj->_itype.get_scoped_name()); - std::string cClassName = obj->_itype.get_true_name(); - std::string export_class_name = classNameFromCppName(obj->_itype.get_name()); - - out << "//********************************************************************\n"; - out << "//*** Functions for .. "<< cClassName <<" \n" ; - out << "//********************************************************************\n"; - - for (fi = obj->_methods.begin(); fi != obj->_methods.end(); ++fi) + Functions::iterator fi; + + //std::string cClassName = obj->_itype.get_scoped_name(); + std::string ClassName = make_safe_name(obj->_itype.get_scoped_name()); + std::string cClassName = obj->_itype.get_true_name(); + std::string export_class_name = classNameFromCppName(obj->_itype.get_name()); + + out << "//********************************************************************\n"; + out << "//*** Functions for .. "<< cClassName <<" \n" ; + out << "//********************************************************************\n"; + + for (fi = obj->_methods.begin(); fi != obj->_methods.end(); ++fi) { - Function *func = (*fi); - if( (func)) + Function *func = (*fi); + if( (func)) { SlottedFunctionDef def; get_slotted_function_def(obj, func, def); - + ostringstream GetThis; GetThis << " "<_constructors.size() == 0) + + // bool AnyLeganConstructors; + + if(obj->_constructors.size() == 0) { - std::string fname = "int Dtool_Init_"+ClassName+"(PyObject *self, PyObject *args, PyObject *kwds)"; - out << fname << "\n"; - out << "{\n"; - out << " PyErr_SetString(PyExc_TypeError, \"Error Can Not Init Constant Class (" << cClassName << ")\");\n"; - out << " return -1;\n" ; - out << "}\n"; - + std::string fname = "int Dtool_Init_"+ClassName+"(PyObject *self, PyObject *args, PyObject *kwds)"; + out << fname << "\n"; + out << "{\n"; + out << " PyErr_SetString(PyExc_TypeError, \"Error Can Not Init Constant Class (" << cClassName << ")\");\n"; + out << " return -1;\n" ; + out << "}\n"; + } - else + else { - for (fi = obj->_constructors.begin(); fi != obj->_constructors.end(); ++fi) + for (fi = obj->_constructors.begin(); fi != obj->_constructors.end(); ++fi) { - Function *func = (*fi); - std::string fname = "int Dtool_Init_"+ClassName+"(PyObject *self, PyObject *args, PyObject *kwds) "; - - write_function_for_name(out, obj, func,fname,"",ClassName); + Function *func = (*fi); + std::string fname = "int Dtool_Init_"+ClassName+"(PyObject *self, PyObject *args, PyObject *kwds) "; + + write_function_for_name(out, obj, func,fname,"",ClassName); } } - - MakeSeqs::iterator msi; - for (msi = obj->_make_seqs.begin(); msi != obj->_make_seqs.end(); ++msi) { - write_make_seq(out, obj, ClassName, *msi); + + MakeSeqs::iterator msi; + for (msi = obj->_make_seqs.begin(); msi != obj->_make_seqs.end(); ++msi) { + write_make_seq(out, obj, ClassName, *msi); + } + + CPPType *cpptype = TypeManager::resolve_type(obj->_itype._cpptype); + std::map< string ,CastDetails > details; + std::map< string ,CastDetails >::iterator di; + builder.get_type(TypeManager::unwrap(cpptype),false); + GetValideChildClasses(details, cpptype->as_struct_type()); + for(di = details.begin(); di != details.end(); di++) + { + //InterrogateType ptype =idb->get_type(di->first); + if(di->second._is_legal_py_class && !isExportThisRun(di->second._structType)) + _external_imports.insert(make_safe_name(di->second._to_class_name)); + //out << "IMPORT_THIS struct Dtool_PyTypedObject Dtool_" << make_safe_name(di->second._to_class_name) <<";\n"; } + + + + { // the Cast Converter + + out << "inline void * Dtool_UpcastInterface_"<< ClassName << "(PyObject *self, Dtool_PyTypedObject *requested_type)\n"; + out << "{\n"; + out << " Dtool_PyTypedObject *SelfType = ((Dtool_PyInstDef *)self)->_My_Type;\n"; + out << " if(SelfType != &Dtool_" << ClassName <<")\n"; + out << " {\n"; + out << " printf(\""<_My_Type->_name,requested_type->_name);fflush(NULL);\n";; + out << " return NULL;\n"; + out << " }\n"; + out << " \n"; + out << " "<_ptr_to_object;\n"; + out << " if(requested_type == &Dtool_"<_itype._cpptype); - std::map< string ,CastDetails > details; - std::map< string ,CastDetails >::iterator di; - builder.get_type(TypeManager::unwrap(cpptype),false); - GetValideChildClasses(details, cpptype->as_struct_type()); for(di = details.begin(); di != details.end(); di++) { - //InterrogateType ptype =idb->get_type(di->first); - if(di->second._is_legal_py_class && !isExportThisRun(di->second._structType)) - _external_imports.insert(make_safe_name(di->second._to_class_name)); - //out << "IMPORT_THIS struct Dtool_PyTypedObject Dtool_" << make_safe_name(di->second._to_class_name) <<";\n"; + if(di->second._is_legal_py_class) + { + out << " if(requested_type == &Dtool_"<second._to_class_name)<<")\n"; + out << " return "<< di->second._up_cast_string << " local_this;\n"; + } } - + out << " return NULL;\n"; + out << "}\n"; - { // the Cast Converter - - out << "inline void * Dtool_UpcastInterface_"<< ClassName << "(PyObject *self, Dtool_PyTypedObject *requested_type)\n"; - out << "{\n"; - out << " Dtool_PyTypedObject *SelfType = ((Dtool_PyInstDef *)self)->_My_Type;\n"; - out << " if(SelfType != &Dtool_" << ClassName <<")\n"; - out << " {\n"; - out << " printf(\""<_My_Type->_name,requested_type->_name);fflush(NULL);\n";; - out << " return NULL;\n"; - out << " }\n"; - out << " \n"; - out << " "<_ptr_to_object;\n"; - out << " if(requested_type == &Dtool_"<second._is_legal_py_class) - { - out << " if(requested_type == &Dtool_"<second._to_class_name)<<")\n"; - out << " return "<< di->second._up_cast_string << " local_this;\n"; - } - } - - out << " return NULL;\n"; - out << "}\n"; - - out << "inline void * Dtool_DowncastInterface_"<< ClassName << "(void *from_this, Dtool_PyTypedObject *from_type)\n"; - out << "{\n"; - out << " if(from_this == NULL || from_type == NULL)\n"; - out << " return NULL;\n"; - out << " if(from_type == &Dtool_" << ClassName<<")\n"; - out << " return from_this;\n"; - for(di = details.begin(); di != details.end(); di++) - { - if(di->second._can_downcast && di->second._is_legal_py_class) - { - out << " if(from_type == &Dtool_"<second._to_class_name)<<")\n"; - out << " {\n"; - out << " "<< di->second._to_class_name << "* other_this = ("<< di->second._to_class_name << "*)from_this;\n" ; - out << " return ("<< cClassName << "*)other_this;\n"; - out << " }\n"; - } - } - out << " return (void *) NULL;\n"; - out << "}\n"; - - } + out << "inline void * Dtool_DowncastInterface_"<< ClassName << "(void *from_this, Dtool_PyTypedObject *from_type)\n"; + out << "{\n"; + out << " if(from_this == NULL || from_type == NULL)\n"; + out << " return NULL;\n"; + out << " if(from_type == &Dtool_" << ClassName<<")\n"; + out << " return from_this;\n"; + for(di = details.begin(); di != details.end(); di++) + { + if(di->second._can_downcast && di->second._is_legal_py_class) + { + out << " if(from_type == &Dtool_"<second._to_class_name)<<")\n"; + out << " {\n"; + out << " "<< di->second._to_class_name << "* other_this = ("<< di->second._to_class_name << "*)from_this;\n" ; + out << " return ("<< cClassName << "*)other_this;\n"; + out << " }\n"; + } + } + out << " return (void *) NULL;\n"; + out << "}\n"; + } } + //////////////////////////////////////////////////////////// /// Function : write_ClasseDeclarations // @@ -1215,9 +1220,18 @@ write_module_class(ostream &out, Object *obj) { std::map normal_Operator_functions; std::map wraped_Operator_functions; // function Table + bool got_copy = false; + bool got_deepcopy = false; + int x; for (x = 0, fi = obj->_methods.begin(); fi != obj->_methods.end(); ++fi,x++) { Function *func = (*fi); + if (func->_name == "__copy__") { + got_copy = true; + } else if (func->_name == "__deepcopy__") { + got_deepcopy = true; + } + SlottedFunctionDef slotted_def; if (!get_slotted_function_def(obj, func, slotted_def)) { out << " { \"" << methodNameFromCppName(func,export_calss_name) << "\",(PyCFunction ) &" @@ -1248,6 +1262,22 @@ write_module_class(ostream &out, Object *obj) { } } + if (obj->_protocol_types & Object::PT_make_copy) { + if (!got_copy) { + out << " { \"__copy__\", (PyCFunction)©_from_make_copy, METH_NOARGS, NULL},\n"; + got_copy = true; + } + } else if (obj->_protocol_types & Object::PT_copy_constructor) { + if (!got_copy) { + out << " { \"__copy__\", (PyCFunction)©_from_copy_constructor, METH_NOARGS, NULL},\n"; + got_copy = true; + } + } + + if (got_copy && !got_deepcopy) { + out << " { \"__deepcopy__\", (PyCFunction)&map_deepcopy_to_copy, METH_VARARGS, NULL},\n"; + } + MakeSeqs::iterator msi; for (msi = obj->_make_seqs.begin(); msi != obj->_make_seqs.end(); ++msi) { out << " { \"" diff --git a/dtool/src/interrogatedb/py_panda.cxx b/dtool/src/interrogatedb/py_panda.cxx index 09bd767e8c..377a3517fb 100644 --- a/dtool/src/interrogatedb/py_panda.cxx +++ b/dtool/src/interrogatedb/py_panda.cxx @@ -708,6 +708,42 @@ PyObject *make_list_for_item(PyObject *self, const char *num_name, return list; } +//////////////////////////////////////////////////////////////////// +// Function: copy_from_make_copy +// Description: This is a support function for a synthesized +// __copy__() method from a C++ make_copy() method. +//////////////////////////////////////////////////////////////////// +PyObject *copy_from_make_copy(PyObject *self) { + return PyObject_CallMethod(self, (char *)"makeCopy", (char *)"()"); +} + +//////////////////////////////////////////////////////////////////// +// Function: copy_from_make_copy +// Description: This is a support function for a synthesized +// __copy__() method from a C++ copy constructor. +//////////////////////////////////////////////////////////////////// +PyObject *copy_from_copy_constructor(PyObject *self) { + PyObject *this_class = PyObject_Type(self); + if (this_class == NULL) { + return NULL; + } + + PyObject *result = PyObject_CallFunction(this_class, (char *)"(O)", self); + Py_DECREF(this_class); + return result; +} + +//////////////////////////////////////////////////////////////////// +// Function: copy_from_make_copy +// Description: This is a support function for a synthesized +// __deepcopy__() method for any class that has a +// __copy__() method. The sythethic method simply +// invokes __copy__(). +//////////////////////////////////////////////////////////////////// +PyObject *map_deepcopy_to_copy(PyObject *self, PyObject *args) { + return PyObject_CallMethod(self, (char *)"__copy__", (char *)"()"); +} + //////////////////////////////////////////////////////////////////// // Function: PyLongOrInt_FromUnsignedLong // Description: Similar to PyLong_FromUnsignedLong(), but returns diff --git a/dtool/src/interrogatedb/py_panda.h b/dtool/src/interrogatedb/py_panda.h index e05a0aea23..e571b29d8e 100755 --- a/dtool/src/interrogatedb/py_panda.h +++ b/dtool/src/interrogatedb/py_panda.h @@ -174,114 +174,118 @@ struct Dtool_PyTypedObject { //////////////////////////////////////////////////////////////////////// -// Macro's from Hell.. May want to Just Add this to the Code generator.. +// Macros from Hell.. May want to just add this to the code generator.. //////////////////////////////////////////////////////////////////////// -#define Define_Dtool_Class(MODULE_NAME, CLASS_NAME, PUBLIC_NAME) \ -static PyNumberMethods Dtool_PyNumberMethods_##CLASS_NAME ={\ - 0,/*binaryfunc nb_add*/\ - 0,/*binaryfunc nb_subtract*/\ - 0,/*binaryfunc nb_multiply*/\ - 0,/*binaryfunc nb_divide*/\ - 0,/*binaryfunc nb_remainder*/\ - 0,/*binaryfunc nb_divmod*/\ - 0,/*ternaryfunc nb_power*/\ - 0,/*unaryfunc nb_negative*/\ - 0,/*unaryfunc nb_positive*/\ - 0,/*unaryfunc nb_absolute*/\ - 0,/*inquiry nb_nonzero*/\ - 0,/*unaryfunc nb_invert*/\ - 0,/*binaryfunc nb_lshift*/\ - 0,/*binaryfunc nb_rshift*/\ - 0,/*binaryfunc nb_and*/\ - 0,/*binaryfunc nb_xor*/\ - 0,/*binaryfunc nb_or*/\ - 0,/*coercion nb_coerce*/\ - 0,/*unaryfunc nb_int*/\ - 0,/*unaryfunc nb_long*/\ - 0,/*unaryfunc nb_float*/\ - 0,/*unaryfunc nb_oct*/\ - 0,/*unaryfunc nb_hex*/\ - 0,/*binaryfunc nb_inplace_add*/\ - 0,/*binaryfunc nb_inplace_subtract*/\ - 0,/*binaryfunc nb_inplace_multiply*/\ - 0,/*binaryfunc nb_inplace_divide*/\ - 0,/*binaryfunc nb_inplace_remainder*/\ - 0,/*ternaryfunc nb_inplace_power*/\ - 0,/*binaryfunc nb_inplace_lshift*/\ - 0,/*binaryfunc nb_inplace_rshift*/\ - 0,/*binaryfunc nb_inplace_and*/\ - 0,/*binaryfunc nb_inplace_xor*/\ - 0,/*binaryfunc nb_inplace_or*/\ - 0,/*binaryfunc nb_floor_divide*/\ - 0,/*binaryfunc nb_true_divide*/\ - 0,/*binaryfunc nb_inplace_floor_divide*/\ - 0,/*binaryfunc nb_inplace_true_divide*/\ - };\ -static PySequenceMethods Dtool_PySequenceMethods_##CLASS_NAME ={\ - 0,/*lenfunc sq_length */\ - 0,/*binaryfunc sq_concat */\ - 0,/*ssizeargfunc sq_repeat */\ - 0,/*ssizeargfunc sq_item */\ - 0,/*ssizeargfunc sq_ass_item */\ - 0,/*objobjproc sq_contains */\ - 0,/*binaryfunc sq_inplace_concat */\ - 0,/*ssizeargfunc sq_inplace_repeat */\ -};\ -static PyMappingMethods Dtool_PyMappingMethods_##CLASS_NAME ={\ - 0,/*inquiry mp_length */\ - 0,/*binaryfunc mp_subscript */\ - 0,/*objobjargproc mp_ass_subscript */\ -};\ -EXPORT_THIS Dtool_PyTypedObject Dtool_##CLASS_NAME = {\ -{\ - PyObject_HEAD_INIT(NULL)\ - 0,\ - "lib" #MODULE_NAME "." #PUBLIC_NAME, /*type name with module */ \ - sizeof(Dtool_PyInstDef), /* tp_basicsize*/ \ - 0, /*tp_itemsize*/ \ - &Dtool_Deallocate_General, /*Dtool_Deallocate_##CLASS_NAME,*/ /*tp_dealloc*/\ - 0, /*tp_print*/\ - 0, /*tp_getattr*/\ - 0, /*tp_setattr*/\ - 0, /*tp_compare*/\ - 0, /*tp_repr*/\ - &Dtool_PyNumberMethods_##CLASS_NAME, /*tp_as_number*/\ - &Dtool_PySequenceMethods_##CLASS_NAME, /*tp_as_sequence*/\ - &Dtool_PyMappingMethods_##CLASS_NAME, /*tp_as_mapping*/\ - 0, /*tp_hash */\ - 0, /* tp_call */\ - 0, /* tp_str */\ - PyObject_GenericGetAttr, /* tp_getattro */\ - PyObject_GenericSetAttr, /* tp_setattro */\ - 0, /* tp_as_buffer */\ - (Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_CHECKTYPES), /* tp_flags */\ - 0, /* tp_doc */\ - 0, /* tp_traverse */\ - 0, /* tp_clear */\ - 0, /* tp_richcompare */\ - 0, /* tp_weaklistoffset */\ - 0, /* tp_iter */\ - 0, /* tp_iternext */\ - Dtool_Methods_##CLASS_NAME, /* tp_methods */\ - standard_type_members, /* tp_members */\ - 0, /* tp_getset */\ - 0, /* tp_base */\ - 0, /* tp_dict */\ - 0, /* tp_descr_get */\ - 0, /* tp_descr_set */\ - 0, /* tp_dictoffset */\ - Dtool_Init_##CLASS_NAME, /* tp_init */\ - PyType_GenericAlloc, /* tp_alloc */\ - Dtool_new_##CLASS_NAME, /* tp_new */\ - _PyObject_Del, /* tp_free */\ -},\ - #CLASS_NAME, \ - false,\ - Dtool_UpcastInterface_##CLASS_NAME, \ - Dtool_DowncastInterface_##CLASS_NAME, \ - Dtool_FreeInstance_##CLASS_NAME, \ - Dtool_PyModuleClassInit_##CLASS_NAME\ -}; +#define Define_Dtool_Class(MODULE_NAME, CLASS_NAME, PUBLIC_NAME) \ + static PyNumberMethods Dtool_PyNumberMethods_##CLASS_NAME = \ + { \ + 0,/*binaryfunc nb_add*/ \ + 0,/*binaryfunc nb_subtract*/ \ + 0,/*binaryfunc nb_multiply*/ \ + 0,/*binaryfunc nb_divide*/ \ + 0,/*binaryfunc nb_remainder*/ \ + 0,/*binaryfunc nb_divmod*/ \ + 0,/*ternaryfunc nb_power*/ \ + 0,/*unaryfunc nb_negative*/ \ + 0,/*unaryfunc nb_positive*/ \ + 0,/*unaryfunc nb_absolute*/ \ + 0,/*inquiry nb_nonzero*/ \ + 0,/*unaryfunc nb_invert*/ \ + 0,/*binaryfunc nb_lshift*/ \ + 0,/*binaryfunc nb_rshift*/ \ + 0,/*binaryfunc nb_and*/ \ + 0,/*binaryfunc nb_xor*/ \ + 0,/*binaryfunc nb_or*/ \ + 0,/*coercion nb_coerce*/ \ + 0,/*unaryfunc nb_int*/ \ + 0,/*unaryfunc nb_long*/ \ + 0,/*unaryfunc nb_float*/ \ + 0,/*unaryfunc nb_oct*/ \ + 0,/*unaryfunc nb_hex*/ \ + 0,/*binaryfunc nb_inplace_add*/ \ + 0,/*binaryfunc nb_inplace_subtract*/ \ + 0,/*binaryfunc nb_inplace_multiply*/ \ + 0,/*binaryfunc nb_inplace_divide*/ \ + 0,/*binaryfunc nb_inplace_remainder*/ \ + 0,/*ternaryfunc nb_inplace_power*/ \ + 0,/*binaryfunc nb_inplace_lshift*/ \ + 0,/*binaryfunc nb_inplace_rshift*/ \ + 0,/*binaryfunc nb_inplace_and*/ \ + 0,/*binaryfunc nb_inplace_xor*/ \ + 0,/*binaryfunc nb_inplace_or*/ \ + 0,/*binaryfunc nb_floor_divide*/ \ + 0,/*binaryfunc nb_true_divide*/ \ + 0,/*binaryfunc nb_inplace_floor_divide*/ \ + 0,/*binaryfunc nb_inplace_true_divide*/ \ + }; \ + static PySequenceMethods Dtool_PySequenceMethods_##CLASS_NAME = \ + { \ + 0,/*lenfunc sq_length */ \ + 0,/*binaryfunc sq_concat */ \ + 0,/*ssizeargfunc sq_repeat */ \ + 0,/*ssizeargfunc sq_item */ \ + 0,/*ssizeargfunc sq_ass_item */ \ + 0,/*objobjproc sq_contains */ \ + 0,/*binaryfunc sq_inplace_concat */ \ + 0,/*ssizeargfunc sq_inplace_repeat */ \ + }; \ + static PyMappingMethods Dtool_PyMappingMethods_##CLASS_NAME = \ + { \ + 0,/*inquiry mp_length */ \ + 0,/*binaryfunc mp_subscript */ \ + 0,/*objobjargproc mp_ass_subscript */ \ + }; \ + EXPORT_THIS Dtool_PyTypedObject Dtool_##CLASS_NAME = \ + { \ + { \ + PyObject_HEAD_INIT(NULL) \ + 0, \ + "lib" #MODULE_NAME "." #PUBLIC_NAME, /*type name with module */ \ + sizeof(Dtool_PyInstDef), /* tp_basicsize */ \ + 0, /* tp_itemsize */ \ + &Dtool_Deallocate_General, /* tp_dealloc */ \ + 0, /* tp_print */ \ + 0, /* tp_getattr */ \ + 0, /* tp_setattr */ \ + 0, /* tp_compare */ \ + 0, /* tp_repr */ \ + &Dtool_PyNumberMethods_##CLASS_NAME, /* tp_as_number */ \ + &Dtool_PySequenceMethods_##CLASS_NAME, /* tp_as_sequence */ \ + &Dtool_PyMappingMethods_##CLASS_NAME, /* tp_as_mapping */ \ + 0, /* tp_hash */ \ + 0, /* tp_call */ \ + 0, /* tp_str */ \ + PyObject_GenericGetAttr, /* tp_getattro */ \ + PyObject_GenericSetAttr, /* tp_setattro */ \ + 0, /* tp_as_buffer */ \ + (Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_CHECKTYPES), /* tp_flags */ \ + 0, /* tp_doc */ \ + 0, /* tp_traverse */ \ + 0, /* tp_clear */ \ + 0, /* tp_richcompare */ \ + 0, /* tp_weaklistoffset */ \ + 0, /* tp_iter */ \ + 0, /* tp_iternext */ \ + Dtool_Methods_##CLASS_NAME, /* tp_methods */ \ + standard_type_members, /* tp_members */ \ + 0, /* tp_getset */ \ + 0, /* tp_base */ \ + 0, /* tp_dict */ \ + 0, /* tp_descr_get */ \ + 0, /* tp_descr_set */ \ + 0, /* tp_dictoffset */ \ + Dtool_Init_##CLASS_NAME, /* tp_init */ \ + PyType_GenericAlloc, /* tp_alloc */ \ + Dtool_new_##CLASS_NAME, /* tp_new */ \ + _PyObject_Del, /* tp_free */ \ + }, \ + #CLASS_NAME, \ + false, \ + Dtool_UpcastInterface_##CLASS_NAME, \ + Dtool_DowncastInterface_##CLASS_NAME, \ + Dtool_FreeInstance_##CLASS_NAME, \ + Dtool_PyModuleClassInit_##CLASS_NAME \ + }; //////////////////////////////////////////////////////////////////////// @@ -538,6 +542,16 @@ EXPCL_DTOOLCONFIG int DTOOL_PyObject_Compare(PyObject *v1, PyObject *v2); EXPCL_DTOOLCONFIG PyObject * make_list_for_item(PyObject *self, const char *num_name, const char *element_name); + +EXPCL_DTOOLCONFIG PyObject * +copy_from_make_copy(PyObject *self); + +EXPCL_DTOOLCONFIG PyObject * +copy_from_copy_constructor(PyObject *self); + +EXPCL_DTOOLCONFIG PyObject * +map_deepcopy_to_copy(PyObject *self, PyObject *args); + EXPCL_DTOOLCONFIG PyObject * PyLongOrInt_FromUnsignedLong(unsigned long value); diff --git a/dtool/src/pystub/pystub.cxx b/dtool/src/pystub/pystub.cxx index 6c61c67990..799177e2b2 100644 --- a/dtool/src/pystub/pystub.cxx +++ b/dtool/src/pystub/pystub.cxx @@ -51,6 +51,7 @@ extern "C" { EXPCL_DTOOLCONFIG int PyGen_Type(...); EXPCL_DTOOLCONFIG int PyGILState_Ensure(...); EXPCL_DTOOLCONFIG int PyGILState_Release(...); + EXPCL_DTOOLCONFIG int PyImport_GetModuleDict(...); EXPCL_DTOOLCONFIG int PyInt_AsLong(...); EXPCL_DTOOLCONFIG int PyInt_AsSsize_t(...); EXPCL_DTOOLCONFIG int PyInt_FromLong(...); @@ -74,6 +75,7 @@ extern "C" { EXPCL_DTOOLCONFIG int PyModule_AddObject(...); EXPCL_DTOOLCONFIG int PyNumber_Long(...); EXPCL_DTOOLCONFIG int PyObject_Call(...); + EXPCL_DTOOLCONFIG int PyObject_CallFunction(...); EXPCL_DTOOLCONFIG int PyObject_CallMethod(...); EXPCL_DTOOLCONFIG int PyObject_CallMethodObjArgs(...); EXPCL_DTOOLCONFIG int PyObject_CallObject(...); @@ -88,6 +90,7 @@ extern "C" { EXPCL_DTOOLCONFIG int PyObject_Repr(...); EXPCL_DTOOLCONFIG int PyObject_SetAttrString(...); EXPCL_DTOOLCONFIG int PyObject_Str(...); + EXPCL_DTOOLCONFIG int PyObject_Type(...); EXPCL_DTOOLCONFIG int PySequence_Check(...); EXPCL_DTOOLCONFIG int PySequence_GetItem(...); EXPCL_DTOOLCONFIG int PySequence_Size(...); @@ -180,6 +183,7 @@ int PyGen_Check(...) { return 0; } int PyGen_Type(...) { return 0; } int PyGILState_Ensure(...) { return 0; } int PyGILState_Release(...) { return 0; } +int PyImport_GetModuleDict(...) { return 0; } int PyInt_AsLong(...) { return 0; } int PyInt_AsSsize_t(...) { return 0; } int PyInt_FromLong(...) { return 0; } @@ -203,6 +207,7 @@ int PyModule_AddIntConstant(...) { return 0; }; int PyModule_AddObject(...) { return 0; }; int PyNumber_Long(...) { return 0; } int PyObject_Call(...) { return 0; } +int PyObject_CallFunction(...) { return 0; } int PyObject_CallMethod(...) { return 0; } int PyObject_CallMethodObjArgs(...) { return 0; } int PyObject_CallObject(...) { return 0; } @@ -217,6 +222,7 @@ int PyObject_IsTrue(...) { return 0; } int PyObject_Repr(...) { return 0; } int PyObject_SetAttrString(...) { return 0; } int PyObject_Str(...) { return 0; } +int PyObject_Type(...) { return 0; } int PySequence_Check(...) { return 0; } int PySequence_GetItem(...) { return 0; } int PySequence_Size(...) { return 0; } diff --git a/panda/src/linmath/lmatrix3_src.cxx b/panda/src/linmath/lmatrix3_src.cxx index eb8a3bc113..2c79a80442 100644 --- a/panda/src/linmath/lmatrix3_src.cxx +++ b/panda/src/linmath/lmatrix3_src.cxx @@ -46,6 +46,33 @@ const FLOATNAME(LMatrix3) FLOATNAME(LMatrix3)::_ly_to_rz_mat = FLOATNAME(LMatrix3)::_flip_z_mat * FLOATNAME(LMatrix3)::_y_to_z_up_mat; +#ifdef HAVE_PYTHON +//////////////////////////////////////////////////////////////////// +// Function: LMatrix3::__reduce__ +// Access: Published +// Description: This special Python method is implement to provide +// support for the pickle module. +//////////////////////////////////////////////////////////////////// +PyObject *FLOATNAME(LMatrix3):: +__reduce__(PyObject *self) const { + // We should return at least a 2-tuple, (Class, (args)): the + // necessary class object whose constructor we should call + // (e.g. this), and the arguments necessary to reconstruct this + // object. + PyObject *this_class = PyObject_Type(self); + if (this_class == NULL) { + return NULL; + } + + PyObject *result = Py_BuildValue("(O(fffffffff))", this_class, + _m.m._00, _m.m._01, _m.m._02, + _m.m._10, _m.m._11, _m.m._12, + _m.m._20, _m.m._21, _m.m._22); + Py_DECREF(this_class); + return result; +} +#endif // HAVE_PYTHON + //////////////////////////////////////////////////////////////////// // Function: LMatrix::set_scale_shear_mat // Access: Public diff --git a/panda/src/linmath/lmatrix3_src.h b/panda/src/linmath/lmatrix3_src.h index 600cba69d1..b596764f14 100644 --- a/panda/src/linmath/lmatrix3_src.h +++ b/panda/src/linmath/lmatrix3_src.h @@ -65,6 +65,10 @@ PUBLISHED: FLOATTYPE e20, FLOATTYPE e21, FLOATTYPE e22); ALLOC_DELETED_CHAIN(FLOATNAME(LMatrix3)); +#ifdef HAVE_PYTHON + PyObject *__reduce__(PyObject *self) const; +#endif + void fill(FLOATTYPE fill_value); INLINE_LINMATH void set( FLOATTYPE e00, FLOATTYPE e01, FLOATTYPE e02, diff --git a/panda/src/linmath/lmatrix4_src.cxx b/panda/src/linmath/lmatrix4_src.cxx index 335685e522..83280aa0ba 100644 --- a/panda/src/linmath/lmatrix4_src.cxx +++ b/panda/src/linmath/lmatrix4_src.cxx @@ -62,6 +62,35 @@ const FLOATNAME(LMatrix4) FLOATNAME(LMatrix4)::_lz_to_ry_mat = const FLOATNAME(LMatrix4) FLOATNAME(LMatrix4)::_ly_to_rz_mat = FLOATNAME(LMatrix4)::_flip_z_mat * FLOATNAME(LMatrix4)::_y_to_z_up_mat; + +#ifdef HAVE_PYTHON +//////////////////////////////////////////////////////////////////// +// Function: LMatrix4::__reduce__ +// Access: Published +// Description: This special Python method is implement to provide +// support for the pickle module. +//////////////////////////////////////////////////////////////////// +PyObject *FLOATNAME(LMatrix4):: +__reduce__(PyObject *self) const { + // We should return at least a 2-tuple, (Class, (args)): the + // necessary class object whose constructor we should call + // (e.g. this), and the arguments necessary to reconstruct this + // object. + PyObject *this_class = PyObject_Type(self); + if (this_class == NULL) { + return NULL; + } + + PyObject *result = Py_BuildValue("(O(ffffffffffffffff))", this_class, + _m.m._00, _m.m._01, _m.m._02, _m.m._03, + _m.m._10, _m.m._11, _m.m._12, _m.m._13, + _m.m._20, _m.m._21, _m.m._22, _m.m._23, + _m.m._30, _m.m._31, _m.m._32, _m.m._33); + Py_DECREF(this_class); + return result; +} +#endif // HAVE_PYTHON + //////////////////////////////////////////////////////////////////// // Function: LMatrix::convert_mat // Access: Public, Static diff --git a/panda/src/linmath/lmatrix4_src.h b/panda/src/linmath/lmatrix4_src.h index e27730c0c1..3d954f5ac0 100644 --- a/panda/src/linmath/lmatrix4_src.h +++ b/panda/src/linmath/lmatrix4_src.h @@ -60,6 +60,10 @@ PUBLISHED: FLOATTYPE e30, FLOATTYPE e31, FLOATTYPE e32, FLOATTYPE e33); ALLOC_DELETED_CHAIN(FLOATNAME(LMatrix4)); +#ifdef HAVE_PYTHON + PyObject *__reduce__(PyObject *self) const; +#endif + // Construct a 4x4 matrix given a 3x3 rotation matrix and an optional // translation component. INLINE_LINMATH FLOATNAME(LMatrix4)(const FLOATNAME(LMatrix3) &upper3); diff --git a/panda/src/linmath/lvecBase2_src.cxx b/panda/src/linmath/lvecBase2_src.cxx index a4b591564d..829ae354c1 100644 --- a/panda/src/linmath/lvecBase2_src.cxx +++ b/panda/src/linmath/lvecBase2_src.cxx @@ -21,6 +21,31 @@ const FLOATNAME(LVecBase2) FLOATNAME(LVecBase2)::_unit_x = const FLOATNAME(LVecBase2) FLOATNAME(LVecBase2)::_unit_y = FLOATNAME(LVecBase2)(0.0f, 1.0f); +#ifdef HAVE_PYTHON +//////////////////////////////////////////////////////////////////// +// Function: LVecBase2::__reduce__ +// Access: Published +// Description: This special Python method is implement to provide +// support for the pickle module. +//////////////////////////////////////////////////////////////////// +PyObject *FLOATNAME(LVecBase2):: +__reduce__(PyObject *self) const { + // We should return at least a 2-tuple, (Class, (args)): the + // necessary class object whose constructor we should call + // (e.g. this), and the arguments necessary to reconstruct this + // object. + PyObject *this_class = PyObject_Type(self); + if (this_class == NULL) { + return NULL; + } + + PyObject *result = Py_BuildValue("(O(ff))", this_class, + (*this)[0], (*this)[1]); + Py_DECREF(this_class); + return result; +} +#endif // HAVE_PYTHON + //////////////////////////////////////////////////////////////////// // Function: LVecBase2::init_type // Access: Public, Static diff --git a/panda/src/linmath/lvecBase2_src.h b/panda/src/linmath/lvecBase2_src.h index 3eace11fb6..60a0bff455 100644 --- a/panda/src/linmath/lvecBase2_src.h +++ b/panda/src/linmath/lvecBase2_src.h @@ -39,6 +39,10 @@ PUBLISHED: INLINE_LINMATH ~FLOATNAME(LVecBase2)(); +#ifdef HAVE_PYTHON + PyObject *__reduce__(PyObject *self) const; +#endif + INLINE_LINMATH FLOATTYPE operator [](int i) const; INLINE_LINMATH FLOATTYPE &operator [](int i); #ifdef HAVE_PYTHON diff --git a/panda/src/linmath/lvecBase3_src.cxx b/panda/src/linmath/lvecBase3_src.cxx index dc3d9d38c7..299df19470 100644 --- a/panda/src/linmath/lvecBase3_src.cxx +++ b/panda/src/linmath/lvecBase3_src.cxx @@ -12,6 +12,7 @@ // //////////////////////////////////////////////////////////////////// + TypeHandle FLOATNAME(LVecBase3)::_type_handle; const FLOATNAME(LVecBase3) FLOATNAME(LVecBase3)::_zero = @@ -23,6 +24,32 @@ const FLOATNAME(LVecBase3) FLOATNAME(LVecBase3)::_unit_y = const FLOATNAME(LVecBase3) FLOATNAME(LVecBase3)::_unit_z = FLOATNAME(LVecBase3)(0.0f, 0.0f, 1.0f); + +#ifdef HAVE_PYTHON +//////////////////////////////////////////////////////////////////// +// Function: LVecBase3::__reduce__ +// Access: Published +// Description: This special Python method is implement to provide +// support for the pickle module. +//////////////////////////////////////////////////////////////////// +PyObject *FLOATNAME(LVecBase3):: +__reduce__(PyObject *self) const { + // We should return at least a 2-tuple, (Class, (args)): the + // necessary class object whose constructor we should call + // (e.g. this), and the arguments necessary to reconstruct this + // object. + PyObject *this_class = PyObject_Type(self); + if (this_class == NULL) { + return NULL; + } + + PyObject *result = Py_BuildValue("(O(fff))", this_class, + (*this)[0], (*this)[1], (*this)[2]); + Py_DECREF(this_class); + return result; +} +#endif // HAVE_PYTHON + //////////////////////////////////////////////////////////////////// // Function: LVecBase3::init_type // Access: Public, Static diff --git a/panda/src/linmath/lvecBase3_src.h b/panda/src/linmath/lvecBase3_src.h index d3aaed6d3b..cb8963ccd6 100644 --- a/panda/src/linmath/lvecBase3_src.h +++ b/panda/src/linmath/lvecBase3_src.h @@ -37,6 +37,10 @@ PUBLISHED: INLINE_LINMATH ~FLOATNAME(LVecBase3)(); +#ifdef HAVE_PYTHON + PyObject *__reduce__(PyObject *self) const; +#endif + INLINE_LINMATH FLOATTYPE operator [](int i) const; INLINE_LINMATH FLOATTYPE &operator [](int i); #ifdef HAVE_PYTHON diff --git a/panda/src/linmath/lvecBase4_src.cxx b/panda/src/linmath/lvecBase4_src.cxx index d71d817323..6b34200b88 100644 --- a/panda/src/linmath/lvecBase4_src.cxx +++ b/panda/src/linmath/lvecBase4_src.cxx @@ -25,6 +25,31 @@ const FLOATNAME(LVecBase4) FLOATNAME(LVecBase4)::_unit_z = const FLOATNAME(LVecBase4) FLOATNAME(LVecBase4)::_unit_w = FLOATNAME(LVecBase4)(0.0f, 0.0f, 0.0f, 1.0f); +#ifdef HAVE_PYTHON +//////////////////////////////////////////////////////////////////// +// Function: LVecBase4::__reduce__ +// Access: Published +// Description: This special Python method is implement to provide +// support for the pickle module. +//////////////////////////////////////////////////////////////////// +PyObject *FLOATNAME(LVecBase4):: +__reduce__(PyObject *self) const { + // We should return at least a 2-tuple, (Class, (args)): the + // necessary class object whose constructor we should call + // (e.g. this), and the arguments necessary to reconstruct this + // object. + PyObject *this_class = PyObject_Type(self); + if (this_class == NULL) { + return NULL; + } + + PyObject *result = Py_BuildValue("(O(ffff))", this_class, + (*this)[0], (*this)[1], (*this)[2], (*this)[3]); + Py_DECREF(this_class); + return result; +} +#endif // HAVE_PYTHON + //////////////////////////////////////////////////////////////////// // Function: LVecBase4::init_type // Access: Public, Static diff --git a/panda/src/linmath/lvecBase4_src.h b/panda/src/linmath/lvecBase4_src.h index fcec5484f3..db9a6f1ca2 100644 --- a/panda/src/linmath/lvecBase4_src.h +++ b/panda/src/linmath/lvecBase4_src.h @@ -38,6 +38,10 @@ PUBLISHED: INLINE_LINMATH ~FLOATNAME(LVecBase4)(); +#ifdef HAVE_PYTHON + PyObject *__reduce__(PyObject *self) const; +#endif + INLINE_LINMATH FLOATTYPE operator [](int i) const; INLINE_LINMATH FLOATTYPE &operator [](int i); #ifdef HAVE_PYTHON diff --git a/panda/src/pgraph/nodePath.cxx b/panda/src/pgraph/nodePath.cxx index 518fca962d..b5be321d01 100644 --- a/panda/src/pgraph/nodePath.cxx +++ b/panda/src/pgraph/nodePath.cxx @@ -66,6 +66,7 @@ #include "pStatCollector.h" #include "pStatTimer.h" #include "modelNode.h" +#include "py_panda.h" // stack seems to overflow on Intel C++ at 7000. If we need more than // 7000, need to increase stack size. @@ -127,6 +128,128 @@ static ConfigVariableEnum empty_node_path // ***End temporary transition code for operator bool +#ifdef HAVE_PYTHON +//////////////////////////////////////////////////////////////////// +// Function: NodePath::__copy__ +// Access: Published +// Description: A special Python method that is invoked by +// copy.copy(node). Unlike the NodePath copy +// constructor, this makes a duplicate copy of the +// underlying PandaNode (but shares children, instead of +// copying them or omitting them). +//////////////////////////////////////////////////////////////////// +NodePath NodePath:: +__copy__() const { + if (is_empty()) { + // Invoke the copy constructor if we have no node. + return *this; + } + + // If we do have a node, duplicate it, and wrap it in a new + // NodePath. + return NodePath(node()->__copy__()); +} +#endif // HAVE_PYTHON + +#ifdef HAVE_PYTHON +//////////////////////////////////////////////////////////////////// +// Function: NodePath::__deepcopy__ +// Access: Published +// Description: A special Python method that is invoked by +// copy.deepcopy(np). This calls copy_to() unless the +// NodePath is already present in the provided +// dictionary. +//////////////////////////////////////////////////////////////////// +PyObject *NodePath:: +__deepcopy__(PyObject *self, PyObject *memo) const { + IMPORT_THIS struct Dtool_PyTypedObject Dtool_NodePath; + + // Borrowed reference. + PyObject *dupe = PyDict_GetItem(memo, self); + if (dupe != NULL) { + // Already in the memo dictionary. + Py_INCREF(dupe); + return dupe; + } + + NodePath *np_dupe; + if (is_empty()) { + np_dupe = new NodePath(*this); + } else { + np_dupe = new NodePath(copy_to(NodePath())); + } + + dupe = DTool_CreatePyInstance((void *)np_dupe, Dtool_NodePath, + true, false); + if (PyDict_SetItem(memo, self, dupe) != 0) { + Py_DECREF(dupe); + return NULL; + } + + return dupe; +} +#endif // HAVE_PYTHON + +#ifdef HAVE_PYTHON +//////////////////////////////////////////////////////////////////// +// Function: NodePath::__reduce__ +// Access: Published +// Description: This special Python method is implement to provide +// support for the pickle module. +//////////////////////////////////////////////////////////////////// +PyObject *NodePath:: +__reduce__(PyObject *self) const { + // We should return at least a 2-tuple, (Class, (args)): the + // necessary class object whose constructor we should call + // (e.g. this), and the arguments necessary to reconstruct this + // object. + + if (is_empty()) { + // Reconstruct an empty NodePath. Not a 100% reconstruction, + // because we lose the specific error status, but I don't think + // that matters much. + PyObject *this_class = PyObject_Type(self); + if (this_class == NULL) { + return NULL; + } + + PyObject *result = Py_BuildValue("(O())", this_class); + Py_DECREF(this_class); + return result; + } + + // We have a non-empty NodePath. We need to streamify the + // underlying node. + + string bam_stream; + if (!node()->encode_to_bam_stream(bam_stream)) { + ostringstream stream; + stream << "Could not bamify object of type " << node()->get_type() << "\n"; + string message = stream.str(); + PyErr_SetString(PyExc_TypeError, message.c_str()); + return NULL; + } + + // Start by getting this class object. + PyObject *this_class = PyObject_Type(self); + if (this_class == NULL) { + return NULL; + } + + PyObject *func = TypedWritable::find_global_decode(this_class, "pyDecodeNodePathFromBamStream"); + if (func == NULL) { + PyErr_SetString(PyExc_TypeError, "Couldn't find pyDecodeNodePathFromBamStream()"); + Py_DECREF(this_class); + return NULL; + } + Py_DECREF(this_class); + + PyObject *result = Py_BuildValue("(O(s#))", func, bam_stream.data(), bam_stream.size()); + Py_DECREF(func); + return result; +} +#endif // HAVE_PYTHON + //////////////////////////////////////////////////////////////////// // Function: NodePath::operator bool // Access: Published @@ -7292,3 +7415,23 @@ r_find_all_materials(PandaNode *node, const RenderState *state, r_find_all_materials(child, next_state, materials); } } + +#ifdef HAVE_PYTHON +//////////////////////////////////////////////////////////////////// +// Function: py_decode_NodePath_from_bam_stream +// Access: Published +// Description: This wrapper is defined as a global function to suit +// pickle's needs. +//////////////////////////////////////////////////////////////////// +NodePath +py_decode_NodePath_from_bam_stream(const string &data) { + PT(PandaNode) node = PandaNode::decode_from_bam_stream(data); + if (node == (PandaNode *)NULL) { + PyErr_SetString(PyExc_ValueError, "Could not unpack bam stream"); + return NodePath(); + } + + return NodePath(node); +} +#endif // HAVE_PYTHON + diff --git a/panda/src/pgraph/nodePath.h b/panda/src/pgraph/nodePath.h index 7471c7752a..34b4926619 100644 --- a/panda/src/pgraph/nodePath.h +++ b/panda/src/pgraph/nodePath.h @@ -168,6 +168,12 @@ PUBLISHED: INLINE NodePath(const NodePath ©); INLINE void operator = (const NodePath ©); +#ifdef HAVE_PYTHON + NodePath __copy__() const; + PyObject *__deepcopy__(PyObject *self, PyObject *memo) const; + PyObject *__reduce__(PyObject *self) const; +#endif + INLINE static NodePath not_found(); INLINE static NodePath removed(); INLINE static NodePath fail(); @@ -949,6 +955,12 @@ private: INLINE ostream &operator << (ostream &out, const NodePath &node_path); +#ifdef HAVE_PYTHON +BEGIN_PUBLISH +NodePath py_decode_NodePath_from_bam_stream(const string &data); +END_PUBLISH +#endif + #include "nodePath.I" #endif diff --git a/panda/src/pgraph/pandaNode.cxx b/panda/src/pgraph/pandaNode.cxx index be8cbac5ba..00a2e5816e 100644 --- a/panda/src/pgraph/pandaNode.cxx +++ b/panda/src/pgraph/pandaNode.cxx @@ -28,6 +28,7 @@ #include "config_mathutil.h" #include "lightReMutexHolder.h" #include "graphicsStateGuardianBase.h" +#include "py_panda.h" // This category is just temporary for debugging convenience. NotifyCategoryDecl(drawmask, EXPCL_PANDA_PGRAPH, EXPTP_PANDA_PGRAPH); @@ -211,20 +212,6 @@ as_reference_count() { return this; } -//////////////////////////////////////////////////////////////////// -// Function: PandaNode::make_copy -// Access: Public, Virtual -// Description: Returns a newly-allocated PandaNode that is a shallow -// copy of this one. It will be a different pointer, -// but its internal data may or may not be shared with -// that of the original PandaNode. No children will be -// copied. -//////////////////////////////////////////////////////////////////// -PandaNode *PandaNode:: -make_copy() const { - return new PandaNode(*this); -} - //////////////////////////////////////////////////////////////////// // Function: PandaNode::dupe_for_flatten // Access: Public, Virtual @@ -608,6 +595,20 @@ void PandaNode:: add_for_draw(CullTraverser *, CullTraverserData &) { } +//////////////////////////////////////////////////////////////////// +// Function: PandaNode::make_copy +// Access: Published, Virtual +// Description: Returns a newly-allocated PandaNode that is a shallow +// copy of this one. It will be a different pointer, +// but its internal data may or may not be shared with +// that of the original PandaNode. No children will be +// copied. +//////////////////////////////////////////////////////////////////// +PandaNode *PandaNode:: +make_copy() const { + return new PandaNode(*this); +} + //////////////////////////////////////////////////////////////////// // Function: PandaNode::copy_subgraph // Access: Published @@ -618,12 +619,79 @@ add_for_draw(CullTraverser *, CullTraverserData &) { // will impede normal use of the PandaNode. //////////////////////////////////////////////////////////////////// PT(PandaNode) PandaNode:: -copy_subgraph(Thread *current_thread) const -{ +copy_subgraph(Thread *current_thread) const { InstanceMap inst_map; return r_copy_subgraph(inst_map, current_thread); } +#ifdef HAVE_PYTHON +//////////////////////////////////////////////////////////////////// +// Function: PandaNode::__copy__ +// Access: Published +// Description: A special Python method that is invoked by +// copy.copy(node). Unlike the PandaNode copy +// constructor, which creates a new node without +// children, this shares child pointers (essentially +// making every child an instance). This is intended to +// simulate the behavior of copy.copy() for other +// objects. +//////////////////////////////////////////////////////////////////// +PT(PandaNode) PandaNode:: +__copy__() const { + Thread *current_thread = Thread::get_current_thread(); + + PT(PandaNode) node_dupe = make_copy(); + + Children children = get_children(current_thread); + int num_children = children.get_num_children(); + + for (int i = 0; i < num_children; ++i) { + PandaNode *child = children.get_child(i); + node_dupe->add_child(children.get_child(i), children.get_child_sort(i)); + } + + return node_dupe; +} +#endif // HAVE_PYTHON + +#ifdef HAVE_PYTHON +//////////////////////////////////////////////////////////////////// +// Function: PandaNode::__deepcopy__ +// Access: Published +// Description: A special Python method that is invoked by +// copy.deepcopy(node). This calls copy_subgraph() +// unless the node is already present in the provided +// dictionary. +//////////////////////////////////////////////////////////////////// +PyObject *PandaNode:: +__deepcopy__(PyObject *self, PyObject *memo) const { + IMPORT_THIS struct Dtool_PyTypedObject Dtool_PandaNode; + + // Borrowed reference. + PyObject *dupe = PyDict_GetItem(memo, self); + if (dupe != NULL) { + // Already in the memo dictionary. + Py_INCREF(dupe); + return dupe; + } + + PT(PandaNode) node_dupe = copy_subgraph(); + + // DTool_CreatePyInstanceTyped() steals a C++ reference. + node_dupe->ref(); + dupe = DTool_CreatePyInstanceTyped + ((void *)node_dupe.p(), Dtool_PandaNode, true, false, + node_dupe->get_type_index()); + + if (PyDict_SetItem(memo, self, dupe) != 0) { + Py_DECREF(dupe); + return NULL; + } + + return dupe; +} +#endif // HAVE_PYTHON + //////////////////////////////////////////////////////////////////// // Function: PandaNode::count_num_descendants // Access: Published @@ -2597,6 +2665,32 @@ is_ambient_light() const { return false; } +//////////////////////////////////////////////////////////////////// +// Function: PandaNode::decode_from_bam_stream +// Access: Published, Static +// Description: Reads the string created by a previous call to +// encode_to_bam_stream(), and extracts and returns the +// single object on that string. Returns NULL on error. +// +// This method is intended to replace +// decode_raw_from_bam_stream() when you know the stream +// in question returns an object of type PandaNode, +// allowing for easier reference count management. Note +// that the caller is still responsible for maintaining +// the reference count on the return value. +//////////////////////////////////////////////////////////////////// +PT(PandaNode) PandaNode:: +decode_from_bam_stream(const string &data) { + TypedWritable *object; + ReferenceCount *ref_ptr; + + if (!TypedWritable::decode_raw_from_bam_stream(object, ref_ptr, data)) { + return NULL; + } + + return DCAST(PandaNode, object); +} + //////////////////////////////////////////////////////////////////// // Function: PandaNode::get_internal_bounds // Access: Protected diff --git a/panda/src/pgraph/pandaNode.h b/panda/src/pgraph/pandaNode.h index c6d3f4c86f..4038768b50 100644 --- a/panda/src/pgraph/pandaNode.h +++ b/panda/src/pgraph/pandaNode.h @@ -84,7 +84,6 @@ private: public: virtual ReferenceCount *as_reference_count(); - virtual PandaNode *make_copy() const; virtual PandaNode *dupe_for_flatten() const; virtual bool safe_to_flatten() const; @@ -116,8 +115,14 @@ public: virtual void add_for_draw(CullTraverser *trav, CullTraverserData &data); PUBLISHED: + virtual PandaNode *make_copy() const; PT(PandaNode) copy_subgraph(Thread *current_thread = Thread::get_current_thread()) const; +#ifdef HAVE_PYTHON + PT(PandaNode) __copy__() const; + PyObject *__deepcopy__(PyObject *self, PyObject *memo) const; +#endif + INLINE int get_num_parents(Thread *current_thread = Thread::get_current_thread()) const; INLINE PandaNode *get_parent(int n, Thread *current_thread = Thread::get_current_thread()) const; INLINE int find_parent(PandaNode *node, Thread *current_thread = Thread::get_current_thread()) const; @@ -300,6 +305,10 @@ PUBLISHED: }; INLINE int get_fancy_bits(Thread *current_thread = Thread::get_current_thread()) const; + +PUBLISHED: + static PT(PandaNode) decode_from_bam_stream(const string &data); + protected: class BoundsData; diff --git a/panda/src/putil/typedWritable.I b/panda/src/putil/typedWritable.I index 87ba030d34..9baa7721f2 100644 --- a/panda/src/putil/typedWritable.I +++ b/panda/src/putil/typedWritable.I @@ -66,3 +66,28 @@ INLINE UpdateSeq TypedWritable:: get_bam_modified() const { return _bam_modified; } + + +//////////////////////////////////////////////////////////////////// +// Function: TypedWritable::encode_to_bam_stream +// Access: Published +// Description: Converts the TypedWritable object into a single +// stream of data using a BamWriter, and returns that +// data as a string string. Returns empty string on +// failure. +// +// This is a convenience method particularly useful for +// cases when you are only serializing a single object. +// If you have many objects to process, it is more +// efficient to use the same BamWriter to serialize all +// of them together. +//////////////////////////////////////////////////////////////////// +string TypedWritable:: +encode_to_bam_stream() const { + string data; + if (!encode_to_bam_stream(data)) { + return string(); + } + return data; +} + diff --git a/panda/src/putil/typedWritable.cxx b/panda/src/putil/typedWritable.cxx index 73891ad8b8..034917b3ba 100644 --- a/panda/src/putil/typedWritable.cxx +++ b/panda/src/putil/typedWritable.cxx @@ -14,7 +14,11 @@ #include "typedWritable.h" #include "bamWriter.h" +#include "bamReader.h" +#include "datagramOutputFile.h" +#include "datagramInputFile.h" #include "lightMutexHolder.h" +#include "bam.h" LightMutex TypedWritable::_bam_writers_lock; @@ -144,3 +148,276 @@ ReferenceCount *TypedWritable:: as_reference_count() { return NULL; } + +#ifdef HAVE_PYTHON +//////////////////////////////////////////////////////////////////// +// Function: TypedWritable::__reduce__ +// Access: Published +// Description: This special Python method is implement to provide +// support for the pickle module. +//////////////////////////////////////////////////////////////////// +PyObject *TypedWritable:: +__reduce__(PyObject *self) const { + // We should return at least a 2-tuple, (Class, (args)): the + // necessary class object whose constructor we should call + // (e.g. this), and the arguments necessary to reconstruct this + // object. + + // Check that we have a decodeFromBamStream python method. If not, + // we can't use this interface. + PyObject *method = PyObject_GetAttrString(self, "decodeFromBamStream"); + if (method == NULL) { + ostringstream stream; + stream << "Cannot pickle objects of type " << get_type() << "\n"; + string message = stream.str(); + PyErr_SetString(PyExc_TypeError, message.c_str()); + return NULL; + } + Py_DECREF(method); + + // First, streamify the object, if possible. + string bam_stream; + if (!encode_to_bam_stream(bam_stream)) { + ostringstream stream; + stream << "Could not bamify object of type " << get_type() << "\n"; + string message = stream.str(); + PyErr_SetString(PyExc_TypeError, message.c_str()); + return NULL; + } + + // Start by getting this class object. + PyObject *this_class = PyObject_Type(self); + if (this_class == NULL) { + return NULL; + } + + PyObject *func = find_global_decode(this_class, "pyDecodeTypedWritableFromBamStream"); + if (func == NULL) { + PyErr_SetString(PyExc_TypeError, "Couldn't find pyDecodeTypedWritableFromBamStream()"); + Py_DECREF(this_class); + return NULL; + } + + PyObject *result = Py_BuildValue("(O(Os#))", func, this_class, bam_stream.data(), bam_stream.size()); + Py_DECREF(func); + Py_DECREF(this_class); + return result; +} +#endif // HAVE_PYTHON + +//////////////////////////////////////////////////////////////////// +// Function: TypedWritable::encode_to_bam_stream +// Access: Published +// Description: Converts the TypedWritable object into a single +// stream of data using a BamWriter, and stores that +// data in the indicated string. Returns true on +// success, false on failure. +// +// This is a convenience method particularly useful for +// cases when you are only serializing a single object. +// If you have many objects to process, it is more +// efficient to use the same BamWriter to serialize all +// of them together. +//////////////////////////////////////////////////////////////////// +bool TypedWritable:: +encode_to_bam_stream(string &data) const { + data.clear(); + ostringstream stream; + + // We use nested scoping to ensure the destructors get called in the + // right order. + { + DatagramOutputFile dout; + if (!dout.open(stream)) { + return false; + } + + if (!dout.write_header(_bam_header)) { + return false; + } + + { + BamWriter writer(&dout, "bam_stream"); + if (!writer.init()) { + return false; + } + + if (!writer.write_object(this)) { + return false; + } + } + } + + data = stream.str(); + return true; +} + +//////////////////////////////////////////////////////////////////// +// Function: TypedWritable::decode_raw_from_bam_stream +// Access: Published, Static +// Description: Reads the string created by a previous call to +// encode_to_bam_stream(), and extracts the single +// object on that string. Returns true on success, +// false on on error. +// +// This variant sets the TypedWritable and +// ReferenceCount pointers separately; both are pointers +// to the same object. The reference count is not +// incremented; it is the caller's responsibility to +// manage the reference count. +// +// Note that this method cannot be used to retrieve +// objects that do not inherit from ReferenceCount, +// because these objects cannot persist beyond the +// lifetime of the BamReader that reads them. To +// retrieve these objects from a bam stream, you must +// construct a BamReader directly. +// +// If you happen to know that the particular object in +// question inherits from TypedWritableReferenceCount or +// PandaNode, consider calling the variant of +// decode_from_bam_stream() defined for those methods, +// which presents a simpler interface. +//////////////////////////////////////////////////////////////////// +bool TypedWritable:: +decode_raw_from_bam_stream(TypedWritable *&ptr, ReferenceCount *&ref_ptr, + const string &data) { + istringstream stream(data); + + DatagramInputFile din; + if (!din.open(stream)) { + return false; + } + + string head; + if (!din.read_header(head, _bam_header.size())) { + return false; + } + + if (head != _bam_header) { + return false; + } + + // We scope this so we can control when the BamReader destructs. + { + BamReader reader(&din, "bam_stream"); + if (!reader.init()) { + return false; + } + + if (!reader.read_object(ptr, ref_ptr)) { + return false; + } + + if (!reader.resolve()) { + return false; + } + + if (ref_ptr == NULL) { + // Can't support non-reference-counted objects. + return false; + } + + // Protect the pointer from accidental deletion when the BamReader + // goes away. + ref_ptr->ref(); + } + + // Now decrement the ref count, without deleting the object. This + // may reduce the reference count to zero, but that's OK--we trust + // the caller to manage the reference count from this point on. + ref_ptr->unref(); + return true; +} + +#ifdef HAVE_PYTHON +//////////////////////////////////////////////////////////////////// +// Function: TypedWritable::find_global_decode +// Access: Public, Static +// Description: This is a support function for __reduce__(). It +// searches for the global function +// pyDecodeTypedWritableFromBamStream() in this class's +// module, or in the module for any base class. (It's +// really looking for the libpanda module, but we can't +// be sure what name that module was loaded under, so we +// search upwards this way.) +// +// Returns: new reference on success, or NULL on failure. +//////////////////////////////////////////////////////////////////// +PyObject *TypedWritable:: +find_global_decode(PyObject *this_class, const char *func_name) { + PyObject *module_name = PyObject_GetAttrString(this_class, "__module__"); + if (module_name != NULL) { + // borrowed reference + PyObject *sys_modules = PyImport_GetModuleDict(); + if (sys_modules != NULL) { + // borrowed reference + PyObject *module = PyDict_GetItem(sys_modules, module_name); + if (module != NULL){ + PyObject *func = PyObject_GetAttrString(module, func_name); + if (func != NULL) { + Py_DECREF(module_name); + return func; + } + } + } + } + Py_DECREF(module_name); + + PyObject *bases = PyObject_GetAttrString(this_class, "__bases__"); + if (bases != NULL) { + if (PySequence_Check(bases)) { + Py_ssize_t size = PySequence_Size(bases); + for (Py_ssize_t i = 0; i < size; ++i) { + PyObject *base = PySequence_GetItem(bases, i); + if (base != NULL) { + PyObject *func = find_global_decode(base, func_name); + Py_DECREF(base); + if (func != NULL) { + Py_DECREF(bases); + return func; + } + } + } + } + Py_DECREF(bases); + } + + return NULL; +} +#endif // HAVE_PYTHON + +#ifdef HAVE_PYTHON +//////////////////////////////////////////////////////////////////// +// Function: py_decode_TypedWritable_from_bam_stream +// Access: Published +// Description: This wrapper is defined as a global function to suit +// pickle's needs. +//////////////////////////////////////////////////////////////////// +PyObject * +py_decode_TypedWritable_from_bam_stream(PyObject *this_class, const string &data) { + // We need the function TypedWritable::decode_from_bam_stream, which + // invokes the BamReader to reconstruct this object. Since we use + // the specific object's class as the pointer, we get the particular + // instance of decode_from_bam_stream appropriate to this class. + + PyObject *func = PyObject_GetAttrString(this_class, "decodeFromBamStream"); + if (func == NULL) { + return NULL; + } + + PyObject *result = PyObject_CallFunction(func, (char *)"(s#)", data.data(), data.size()); + if (result == NULL) { + return NULL; + } + + if (result == Py_None) { + Py_DECREF(result); + PyErr_SetString(PyExc_ValueError, "Could not unpack bam stream"); + return NULL; + } + + return result; +} +#endif // HAVE_PYTHON + diff --git a/panda/src/putil/typedWritable.h b/panda/src/putil/typedWritable.h index 48ef0e9067..81fd4c788c 100644 --- a/panda/src/putil/typedWritable.h +++ b/panda/src/putil/typedWritable.h @@ -60,6 +60,21 @@ PUBLISHED: INLINE void mark_bam_modified(); INLINE UpdateSeq get_bam_modified() const; +#ifdef HAVE_PYTHON + PyObject *__reduce__(PyObject *self) const; +#endif + + INLINE string encode_to_bam_stream() const; + bool encode_to_bam_stream(string &data) const; + static bool decode_raw_from_bam_stream(TypedWritable *&ptr, + ReferenceCount *&ref_ptr, + const string &data); + +public: +#ifdef HAVE_PYTHON + static PyObject *find_global_decode(PyObject *this_class, const char *func_name); +#endif + private: // We may need to store a list of the BamWriter(s) that have a // reference to this object, so that we can remove the object from @@ -70,12 +85,10 @@ private: UpdateSeq _bam_modified; -PUBLISHED: +public: static TypeHandle get_class_type() { return _type_handle; } - -public: static void init_type() { TypedObject::init_type(); register_type(_type_handle, "TypedWritable", @@ -96,6 +109,12 @@ private: friend class BamWriter; }; +#ifdef HAVE_PYTHON +BEGIN_PUBLISH +PyObject *py_decode_TypedWritable_from_bam_stream(PyObject *this_class, const string &data); +END_PUBLISH +#endif + #include "typedWritable.I" #endif diff --git a/panda/src/putil/typedWritableReferenceCount.cxx b/panda/src/putil/typedWritableReferenceCount.cxx index c5ee8ddd67..62751f373f 100644 --- a/panda/src/putil/typedWritableReferenceCount.cxx +++ b/panda/src/putil/typedWritableReferenceCount.cxx @@ -26,3 +26,30 @@ ReferenceCount *TypedWritableReferenceCount:: as_reference_count() { return this; } + +//////////////////////////////////////////////////////////////////// +// Function: TypedWritableReferenceCount::decode_from_bam_stream +// Access: Published, Static +// Description: Reads the string created by a previous call to +// encode_to_bam_stream(), and extracts and returns the +// single object on that string. Returns NULL on error. +// +// This method is intended to replace +// decode_raw_from_bam_stream() when you know the stream +// in question returns an object of type +// TypedWritableReferenceCount, allowing for easier +// reference count management. Note that the caller is +// still responsible for maintaining the reference count +// on the return value. +//////////////////////////////////////////////////////////////////// +PT(TypedWritableReferenceCount) TypedWritableReferenceCount:: +decode_from_bam_stream(const string &data) { + TypedWritable *object; + ReferenceCount *ref_ptr; + + if (!TypedWritable::decode_raw_from_bam_stream(object, ref_ptr, data)) { + return NULL; + } + + return DCAST(TypedWritableReferenceCount, object); +} diff --git a/panda/src/putil/typedWritableReferenceCount.h b/panda/src/putil/typedWritableReferenceCount.h index 0498cdc0e3..b6e44d5d29 100644 --- a/panda/src/putil/typedWritableReferenceCount.h +++ b/panda/src/putil/typedWritableReferenceCount.h @@ -40,18 +40,17 @@ public: virtual ReferenceCount *as_reference_count(); +PUBLISHED: + static PT(TypedWritableReferenceCount) decode_from_bam_stream(const string &data); + public: virtual TypeHandle get_type() const { return get_class_type(); } virtual TypeHandle force_init_type() {init_type(); return get_class_type();} - -PUBLISHED: static TypeHandle get_class_type() { return _type_handle; } - -public: static void init_type() { TypedWritable::init_type(); ReferenceCount::init_type();