diff --git a/dtool/src/interrogate/functionRemap.cxx b/dtool/src/interrogate/functionRemap.cxx index 54a74f730e..2fc33fc21a 100644 --- a/dtool/src/interrogate/functionRemap.cxx +++ b/dtool/src/interrogate/functionRemap.cxx @@ -307,7 +307,18 @@ make_wrapper_entry(FunctionIndex function_index) { _parameters.erase(_parameters.begin() + 1); _flags |= F_explicit_self; } + + } else if (_type == T_constructor) { + // We also allow "self" to be passed in to a constructor, even + // though the constructor doesn't normally accept a this pointer. + // But this makes sense to Python programmers. + if (_parameters.size() >= 1 && _parameters[0]._name == "self" && + TypeManager::is_pointer_to_PyObject(_parameters[0]._remap->get_orig_type())) { + _parameters.erase(_parameters.begin() + 0); + _flags |= F_explicit_self; + } } + if (!_void_return) { iwrapper._flags |= InterrogateFunctionWrapper::F_has_return; @@ -391,24 +402,21 @@ get_call_str(const string &container, const vector_string &pexprs) const { call << _cppfunc->get_local_name(&parser); } + const char *separator = ""; + 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 << ", "; - } + call << separator << "self"; + separator = ", "; } - int pn = _first_true_parameter; - if (pn < (int)_parameters.size()) { + for (int pn = _first_true_parameter; + pn < (int)_parameters.size(); + ++pn) { + call << separator; _parameters[pn]._remap->pass_parameter(call, get_parameter_expr(pn, pexprs)); - pn++; - while (pn < (int)_parameters.size()) { - call << ", "; - _parameters[pn]._remap->pass_parameter(call, get_parameter_expr(pn, pexprs)); - pn++; - } + separator = ", "; } call << ")"; } @@ -670,3 +678,26 @@ setup_properties(const InterrogateFunction &ifunc, InterfaceMaker *interface_mak return true; } + + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +std::string make_safe_name(const std::string & name) +{ + return InterrogateBuilder::clean_identifier(name); + + /* + static const char safe_chars2[] = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_"; + std::string result = name; + + size_t pos = result.find_first_not_of(safe_chars2); + while (pos != std::string::npos) + { + result[pos] = '_'; + pos = result.find_first_not_of(safe_chars2); + } + + return result; + */ +} diff --git a/dtool/src/interrogate/functionRemap.h b/dtool/src/interrogate/functionRemap.h index 069863fd7c..931bb8634c 100644 --- a/dtool/src/interrogate/functionRemap.h +++ b/dtool/src/interrogate/functionRemap.h @@ -126,4 +126,7 @@ private: bool setup_properties(const InterrogateFunction &ifunc, InterfaceMaker *interface_maker); }; +std::string make_safe_name(const std::string & name); + + #endif diff --git a/dtool/src/interrogate/interfaceMakerPythonNative.cxx b/dtool/src/interrogate/interfaceMakerPythonNative.cxx index 6e08431c01..431b427c12 100755 --- a/dtool/src/interrogate/interfaceMakerPythonNative.cxx +++ b/dtool/src/interrogate/interfaceMakerPythonNative.cxx @@ -335,28 +335,6 @@ std::string methodNameFromCppName(InterfaceMaker::Function *func, const std::st /////////////////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////////////// -/////////////////////////////////////////////////////////////////////////////// -/////////////////////////////////////////////////////////////////////////////// -/////////////////////////////////////////////////////////////////////////////// -std::string make_safe_name(const std::string & name) -{ - return InterrogateBuilder::clean_identifier(name); - - /* - static const char safe_chars2[] = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_"; - std::string result = name; - - size_t pos = result.find_first_not_of(safe_chars2); - while (pos != std::string::npos) - { - result[pos] = '_'; - pos = result.find_first_not_of(safe_chars2); - } - - return result; - */ -} - bool isInplaceFunction(InterfaceMaker::Function *func) { std::string wname = methodNameFromCppName(func,""); @@ -2368,6 +2346,24 @@ write_function_instance(ostream &out, InterfaceMaker::Object *obj, if (remap->_type == FunctionRemap::T_constructor) { is_constructor = true; } + + if (is_constructor && (remap->_flags & FunctionRemap::F_explicit_self) != 0) { + // If we'll be passing "self" to the constructor, we need to + // pre-initialize it here. Unfortunately, we can't pre-load the + // "this" pointer, but the constructor itself can do this. + + indent(out, indent_level) + << "// Pre-initialize self for the constructor\n"; + + CPPType *orig_type = remap->_return_type->get_orig_type(); + TypeIndex type_index = builder.get_type(TypeManager::unwrap(TypeManager::resolve_type(orig_type)),false); + InterrogateDatabase *idb = InterrogateDatabase::get_ptr(); + const InterrogateType &itype = idb->get_type(type_index); + indent(out, indent_level) + << "DTool_PyInit_Finalize(self, NULL, &" + << CLASS_PREFEX << make_safe_name(itype.get_scoped_name()) + << ", false, false);\n"; + } // Make one pass through the parameter list. We will output a // one-line temporary variable definition for each parameter, while @@ -2790,6 +2786,11 @@ write_function_instance(ostream &out, InterfaceMaker::Object *obj, } } + // Close the extra brace opened within do_assert_init(). + extra_indent_level -= 2; + indent(out, extra_indent_level) + << "}\n"; + if (!extra_param_check.empty()) { extra_indent_level-=4; indent(out,extra_indent_level)<< "}\n"; @@ -3382,7 +3383,20 @@ bool InterfaceMakerPythonNative::isFunctionWithThis( Function *func) // failed while the C++ code was executing, and report // this failure back to Python. //////////////////////////////////////////////////////////////////// -void InterfaceMakerPythonNative::do_assert_init(ostream &out, int indent_level, bool constructor) const { +void InterfaceMakerPythonNative::do_assert_init(ostream &out, int &indent_level, bool constructor) const { + // If a method raises TypeError, continue. + indent(out, indent_level) + << "if (PyErr_Occurred() && PyErr_ExceptionMatches(PyExc_TypeError)) {\n"; + indent(out, indent_level + 2) + << "// TypeError raised; continue to next overload type.\n"; + if (constructor) { + indent(out, indent_level + 2) + << "delete return_value;\n"; + } + indent(out, indent_level) + << "} else {\n"; + indent_level += 2; + if (watch_asserts) { out << "#ifndef NDEBUG\n"; indent(out, indent_level) @@ -3393,19 +3407,25 @@ void InterfaceMakerPythonNative::do_assert_init(ostream &out, int indent_level, << "PyErr_SetString(PyExc_AssertionError, notify->get_assert_error_message().c_str());\n"; indent(out, indent_level + 2) << "notify->clear_assert_failed();\n"; - if(constructor) - indent(out, indent_level + 2) << "return -1;\n"; - else - indent(out, indent_level + 2) << "return (PyObject *)NULL;\n"; + if (constructor) { + indent(out, indent_level + 2) << "delete return_value;\n"; + indent(out, indent_level + 2) << "return -1;\n"; + } else { + indent(out, indent_level + 2) << "return (PyObject *)NULL;\n"; + } + indent(out, indent_level) << "}\n"; out << "#endif\n"; indent(out, indent_level) << "if (PyErr_Occurred()) {\n"; - if(constructor) - indent(out, indent_level + 2) << "return -1;\n"; - else - indent(out, indent_level + 2) << "return (PyObject *)NULL;\n"; + if (constructor) { + indent(out, indent_level + 2) << "delete return_value;\n"; + indent(out, indent_level + 2) << "return -1;\n"; + } else { + indent(out, indent_level + 2) << "return (PyObject *)NULL;\n"; + } + indent(out, indent_level) << "}\n"; } diff --git a/dtool/src/interrogate/interfaceMakerPythonNative.h b/dtool/src/interrogate/interfaceMakerPythonNative.h index 43dd7dfaba..ab011f73c8 100755 --- a/dtool/src/interrogate/interfaceMakerPythonNative.h +++ b/dtool/src/interrogate/interfaceMakerPythonNative.h @@ -107,7 +107,7 @@ private: void write_ClasseDeclarations(ostream &out , ostream *out_h,Object * obj); void write_ClasseDetails(ostream &out, Object * obj); - void do_assert_init(ostream &out, int indent_level, bool constructor) const; + void do_assert_init(ostream &out, int &indent_level, bool constructor) const; public: bool isRemapLegal( FunctionRemap &remap); bool isFunctionLegal( Function *func); diff --git a/dtool/src/pystub/pystub.cxx b/dtool/src/pystub/pystub.cxx index 799177e2b2..d9b2e23a32 100644 --- a/dtool/src/pystub/pystub.cxx +++ b/dtool/src/pystub/pystub.cxx @@ -99,6 +99,7 @@ extern "C" { EXPCL_DTOOLCONFIG int PyString_AsStringAndSize(...); EXPCL_DTOOLCONFIG int PyString_FromString(...); EXPCL_DTOOLCONFIG int PyString_FromStringAndSize(...); + EXPCL_DTOOLCONFIG int PyString_Size(...); EXPCL_DTOOLCONFIG int PyString_Type(...); EXPCL_DTOOLCONFIG int PySys_GetObject(...); EXPCL_DTOOLCONFIG int PyThreadState_Clear(...); @@ -231,6 +232,7 @@ int PyString_AsString(...) { return 0; } int PyString_AsStringAndSize(...) { return 0; } int PyString_FromString(...) { return 0; } int PyString_FromStringAndSize(...) { return 0; } +int PyString_Size(...) { return 0; } int PyString_Type(...) { return 0; } int PySys_GetObject(...) { return 0; } int PyThreadState_Clear(...) { return 0; } diff --git a/panda/src/express/pointerToArray.I b/panda/src/express/pointerToArray.I index 34dc492b42..e9e91176a6 100644 --- a/panda/src/express/pointerToArray.I +++ b/panda/src/express/pointerToArray.I @@ -77,6 +77,77 @@ PointerToArray(const PointerToArray ©) : { } +#ifdef HAVE_PYTHON +//////////////////////////////////////////////////////////////////// +// Function: PointerToArray::Constructor +// Access: Published +// Description: This special constructor accepts a Python list of +// elements, or a Python string. +//////////////////////////////////////////////////////////////////// +template +PointerToArray:: +PointerToArray(PyObject *self, PyObject *sequence) : + PointerToArrayBase((ReferenceCountedVector *)NULL), + _type_handle(get_type_handle(Element)) +{ + // We have to pre-initialize self's "this" pointer when we receive + // self in the constructor--the caller can't initialize this for us. + ((Dtool_PyInstDef *)self)->_ptr_to_object = this; + + if (!PySequence_Check(sequence)) { + // If passed with a non-sequence, this isn't the right constructor. + PyErr_SetString(PyExc_TypeError, "PointerToArray constructor requires a sequence"); + return; + } + + if (PyString_CheckExact(sequence)) { + // If we were passed a Python string, then instead of storing it + // character-at-a-time, just load the whole string as a data + // buffer. + int size = PyString_Size(sequence); + if (size % sizeof(Element) != 0) { + ostringstream stream; + stream << "Buffer not a multiple of " << sizeof(Element) << " bytes"; + string str = stream.str(); + PyErr_SetString(PyExc_ValueError, str.c_str()); + return; + } + + int num_elements = size / sizeof(Element); + insert(begin(), num_elements, Element()); + + // Hope there aren't any constructors or destructors involved + // here. + if (size != 0) { + const char *data = PyString_AsString(sequence); + memcpy(p(), data, size); + } + return; + } + + // Now construct the internal list by copying the elements + // one-at-a-time from Python. + int size = PySequence_Size(sequence); + for (int i = 0; i < size; ++i) { + PyObject *item = PySequence_GetItem(sequence, i); + if (item == NULL) { + return; + } + PyObject *result = PyObject_CallMethod(self, (char *)"pushBack", (char *)"O", item); + Py_DECREF(item); + if (result == NULL) { + // Unable to add item--probably it wasn't of the appropriate type. + ostringstream stream; + stream << "Element " << i << " in sequence passed to PointerToArray constructor could not be added"; + string str = stream.str(); + PyErr_SetString(PyExc_TypeError, str.c_str()); + return; + } + Py_DECREF(result); + } +} +#endif // HAVE_PYTHON + //////////////////////////////////////////////////////////////////// // Function: PointerToArray::begin // Access: Public diff --git a/panda/src/express/pointerToArray.h b/panda/src/express/pointerToArray.h index dea3ea73c9..65235edb17 100644 --- a/panda/src/express/pointerToArray.h +++ b/panda/src/express/pointerToArray.h @@ -67,6 +67,9 @@ #include "pandabase.h" #include "pointerToArrayBase.h" +#ifdef HAVE_PYTHON +#include "py_panda.h" +#endif #if defined(WIN32_VC) && !defined(__INTEL_COMPILER) // disable mysterious MSVC warning for static inline PTA::empty_array method @@ -108,6 +111,11 @@ PUBLISHED: INLINE PointerToArray(TypeHandle type_handle = get_type_handle(Element)); INLINE static PointerToArray empty_array(size_type n, TypeHandle type_handle = get_type_handle(Element)); INLINE PointerToArray(const PointerToArray ©); + +#ifdef HAVE_PYTHON + PointerToArray(PyObject *self, PyObject *sequence); +#endif + INLINE size_type size() const; INLINE void push_back(const Element &x); INLINE void pop_back(); @@ -141,6 +149,10 @@ public: INLINE PointerToArray(size_type n, const Element &value, TypeHandle type_handle = get_type_handle(Element)); INLINE PointerToArray(const PointerToArray ©); +#ifdef HAVE_PYTHON + PointerToArray(PyObject *self, PyObject *sequence); +#endif + public: // Duplicating the interface of vector. The following member // functions are all const, because they do not reassign the @@ -254,6 +266,7 @@ public: PUBLISHED: INLINE ConstPointerToArray(const PointerToArray ©); INLINE ConstPointerToArray(const ConstPointerToArray ©); + typedef TYPENAME pvector::size_type size_type; INLINE size_type size() const; INLINE const Element &get_element(size_type n) const; diff --git a/panda/src/gobj/textureCollection.cxx b/panda/src/gobj/textureCollection.cxx index 4463f039e8..9fd40bc4b4 100644 --- a/panda/src/gobj/textureCollection.cxx +++ b/panda/src/gobj/textureCollection.cxx @@ -13,15 +13,11 @@ //////////////////////////////////////////////////////////////////// #include "textureCollection.h" - #include "indent.h" #ifdef HAVE_PYTHON -#include "py_panda.h" -#ifndef CPPPARSER -extern EXPCL_PANDA_PUTIL Dtool_PyTypedObject Dtool_Texture; -#endif // CPPPARSER -#endif // HAVE_PYTHON +#include "py_panda.h" +#endif //////////////////////////////////////////////////////////////////// // Function: TextureCollection::Constructor @@ -63,7 +59,11 @@ operator = (const TextureCollection ©) { // in the class record. //////////////////////////////////////////////////////////////////// TextureCollection:: -TextureCollection(PyObject *sequence) { +TextureCollection(PyObject *self, PyObject *sequence) { + // We have to pre-initialize self's "this" pointer when we receive + // self in the constructor--the caller can't initialize this for us. + ((Dtool_PyInstDef *)self)->_ptr_to_object = this; + if (!PySequence_Check(sequence)) { // If passed with a non-sequence, this isn't the right constructor. PyErr_SetString(PyExc_TypeError, "TextureCollection constructor requires a sequence"); @@ -76,20 +76,17 @@ TextureCollection(PyObject *sequence) { if (item == NULL) { return; } - Texture *tex = NULL; - DTOOL_Call_ExtractThisPointerForType(item, &Dtool_Texture, (void **)&tex); + PyObject *result = PyObject_CallMethod(self, (char *)"addTexture", (char *)"O", item); Py_DECREF(item); - if (tex == NULL) { - // If one of the items in the sequence is not a Texture, can't - // use this constructor. + if (result == NULL) { + // Unable to add item--probably it wasn't of the appropriate type. ostringstream stream; - stream << "Element " << i << " in sequence passed to TextureCollection constructor is not a Texture"; + stream << "Element " << i << " in sequence passed to TextureCollection constructor could not be added"; string str = stream.str(); PyErr_SetString(PyExc_TypeError, str.c_str()); return; } - - add_texture(tex); + Py_DECREF(result); } } #endif // HAVE_PYTHON diff --git a/panda/src/gobj/textureCollection.h b/panda/src/gobj/textureCollection.h index f9d9119da0..38d3dc6faa 100644 --- a/panda/src/gobj/textureCollection.h +++ b/panda/src/gobj/textureCollection.h @@ -32,7 +32,7 @@ PUBLISHED: INLINE ~TextureCollection(); #ifdef HAVE_PYTHON - TextureCollection(PyObject *sequence); + TextureCollection(PyObject *self, PyObject *sequence); PyObject *__reduce__(PyObject *self) const; #endif diff --git a/panda/src/pgraph/nodePathCollection.cxx b/panda/src/pgraph/nodePathCollection.cxx index 50d9079658..e7dcb49c47 100644 --- a/panda/src/pgraph/nodePathCollection.cxx +++ b/panda/src/pgraph/nodePathCollection.cxx @@ -18,15 +18,11 @@ #include "textureAttrib.h" #include "colorScaleAttrib.h" #include "colorAttrib.h" - #include "indent.h" #ifdef HAVE_PYTHON -#include "py_panda.h" -#ifndef CPPPARSER -extern EXPCL_PANDA_PUTIL Dtool_PyTypedObject Dtool_NodePath; -#endif // CPPPARSER -#endif // HAVE_PYTHON +#include "py_panda.h" +#endif //////////////////////////////////////////////////////////////////// // Function: NodePathCollection::Constructor @@ -68,7 +64,11 @@ operator = (const NodePathCollection ©) { // in the class record. //////////////////////////////////////////////////////////////////// NodePathCollection:: -NodePathCollection(PyObject *sequence) { +NodePathCollection(PyObject *self, PyObject *sequence) { + // We have to pre-initialize self's "this" pointer when we receive + // self in the constructor--the caller can't initialize this for us. + ((Dtool_PyInstDef *)self)->_ptr_to_object = this; + if (!PySequence_Check(sequence)) { // If passed with a non-sequence, this isn't the right constructor. PyErr_SetString(PyExc_TypeError, "NodePathCollection constructor requires a sequence"); @@ -81,20 +81,17 @@ NodePathCollection(PyObject *sequence) { if (item == NULL) { return; } - NodePath *np = NULL; - DTOOL_Call_ExtractThisPointerForType(item, &Dtool_NodePath, (void **)&np); + PyObject *result = PyObject_CallMethod(self, (char *)"addPath", (char *)"O", item); Py_DECREF(item); - if (np == NULL) { - // If one of the items in the sequence is not a NodePath, can't - // use this constructor. + if (result == NULL) { + // Unable to add item--probably it wasn't of the appropriate type. ostringstream stream; - stream << "Element " << i << " in sequence passed to NodePathCollection constructor is not a NodePath"; + stream << "Element " << i << " in sequence passed to NodePathCollection constructor could not be added"; string str = stream.str(); PyErr_SetString(PyExc_TypeError, str.c_str()); return; } - - add_path(*np); + Py_DECREF(result); } } #endif // HAVE_PYTHON diff --git a/panda/src/pgraph/nodePathCollection.h b/panda/src/pgraph/nodePathCollection.h index 70bb69df80..ba04e9df6a 100644 --- a/panda/src/pgraph/nodePathCollection.h +++ b/panda/src/pgraph/nodePathCollection.h @@ -34,7 +34,7 @@ PUBLISHED: INLINE ~NodePathCollection(); #ifdef HAVE_PYTHON - NodePathCollection(PyObject *sequence); + NodePathCollection(PyObject *self, PyObject *sequence); PyObject *__reduce__(PyObject *self) const; #endif