From f02662d4e033e72a58338fee0a0e6d8177461cf2 Mon Sep 17 00:00:00 2001 From: rdb Date: Tue, 22 Jul 2014 20:06:59 +0000 Subject: [PATCH] Improve interrogate code readability, use METH_STATIC for static methods, support anonymous enums --- dtool/src/interrogate/functionRemap.cxx | 18 +- dtool/src/interrogate/functionRemap.h | 5 +- dtool/src/interrogate/interfaceMaker.cxx | 17 +- dtool/src/interrogate/interfaceMaker.h | 23 ++ .../interfaceMakerPythonNative.cxx | 332 +++++++++--------- .../interrogate/interfaceMakerPythonNative.h | 16 +- dtool/src/interrogate/interrogateBuilder.cxx | 85 +++-- 7 files changed, 265 insertions(+), 231 deletions(-) diff --git a/dtool/src/interrogate/functionRemap.cxx b/dtool/src/interrogate/functionRemap.cxx index b3eefd602f..eae6299a03 100644 --- a/dtool/src/interrogate/functionRemap.cxx +++ b/dtool/src/interrogate/functionRemap.cxx @@ -50,6 +50,7 @@ FunctionRemap(const InterrogateType &itype, const InterrogateFunction &ifunc, _num_default_parameters = num_default_parameters; _type = T_normal; _flags = 0; + _args_type = 0; _wrapper_index = 0; _return_value_needs_management = false; @@ -648,11 +649,11 @@ setup_properties(const InterrogateFunction &ifunc, InterfaceMaker *interface_mak } if (_parameters.size() == first_param) { - _flags |= F_no_args; + _args_type = InterfaceMaker::AT_no_args; } else if (_parameters.size() == first_param + 1) { - _flags |= F_single_arg; + _args_type = InterfaceMaker::AT_single_arg; } else { - _flags |= F_varargs; + _args_type = InterfaceMaker::AT_varargs; } if (_type == T_normal) { @@ -670,7 +671,8 @@ setup_properties(const InterrogateFunction &ifunc, InterfaceMaker *interface_mak if (_has_this && _parameters.size() > 2) { if (TypeManager::is_integer(_parameters[1]._remap->get_new_type())) { // Its first parameter is an int parameter, presumably an index. - _flags |= (F_setitem_int | F_varargs); + _flags |= F_setitem_int; + _args_type = InterfaceMaker::AT_varargs; } } @@ -720,16 +722,16 @@ setup_properties(const InterrogateFunction &ifunc, InterfaceMaker *interface_mak } else if (fname == "operator ()" || fname == "__call__") { // Call operators always take keyword arguments. - _flags |= (F_varargs | F_keyword_args); + _args_type = InterfaceMaker::AT_keyword_args; } else if (fname == "__setattr__" || fname == "__getattr__") { // Just to prevent these from getting keyword arguments. } else { - if (_flags & F_varargs) { + if (_args_type == InterfaceMaker::AT_varargs) { // Every other method can take keyword arguments, if they // take more than one argument. - _flags |= F_keyword_args; + _args_type = InterfaceMaker::AT_keyword_args; } } @@ -743,7 +745,7 @@ setup_properties(const InterrogateFunction &ifunc, InterfaceMaker *interface_mak } // Constructors always take varargs and keyword args. - _flags |= (F_varargs | F_keyword_args); + _args_type = InterfaceMaker::AT_keyword_args; } return true; diff --git a/dtool/src/interrogate/functionRemap.h b/dtool/src/interrogate/functionRemap.h index f1812009d7..ed0ef49507 100644 --- a/dtool/src/interrogate/functionRemap.h +++ b/dtool/src/interrogate/functionRemap.h @@ -91,10 +91,6 @@ public: F_getbuffer = 0x0200, F_releasebuffer = 0x0400, F_compare_to = 0x0800, - F_no_args = 0x1000, - F_single_arg = 0x2000, - F_varargs = 0x4000, - F_keyword_args = 0x8000, }; typedef vector Parameters; @@ -111,6 +107,7 @@ public: int _num_default_parameters; Type _type; int _flags; + int _args_type; string _expression; string _function_signature; string _hash; diff --git a/dtool/src/interrogate/interfaceMaker.cxx b/dtool/src/interrogate/interfaceMaker.cxx index e5ca8ce25e..b980f6b8b0 100644 --- a/dtool/src/interrogate/interfaceMaker.cxx +++ b/dtool/src/interrogate/interfaceMaker.cxx @@ -57,6 +57,7 @@ Function(const string &name, { _has_this = false; _flags = 0; + _args_type = AT_unknown; } //////////////////////////////////////////////////////////////////// @@ -632,13 +633,13 @@ record_function(const InterrogateType &itype, FunctionIndex func_index) { CPPInstance *cppfunc = (*ii).second; CPPFunctionType *ftype = cppfunc->_type->as_function_type(); int max_default_parameters = 0; - + if (separate_overloading()) { // Count up the number of default parameters this function might // take. CPPParameterList *parameters = ftype->_parameters; CPPParameterList::Parameters::reverse_iterator pi; - for (pi = parameters->_parameters.rbegin(); + for (pi = parameters->_parameters.rbegin(); pi != parameters->_parameters.rend(); ++pi) { CPPInstance *param = (*pi); @@ -651,7 +652,7 @@ record_function(const InterrogateType &itype, FunctionIndex func_index) { } } } - + // Now make a different wrapper for each combination of default // parameters. This will happen only if separate_overloading(), // tested above, returned true; otherwise, max_default_parameters @@ -672,9 +673,10 @@ record_function(const InterrogateType &itype, FunctionIndex func_index) { } func->_flags |= remap->_flags; + func->_args_type = (ArgsType)((int)func->_args_type | (int)remap->_args_type); // Make a wrapper for the function. - FunctionWrapperIndex wrapper_index = + FunctionWrapperIndex wrapper_index = remap->make_wrapper_entry(func_index); if (wrapper_index != 0) { InterrogateFunction &mod_ifunc = idb->update_function(func_index); @@ -685,13 +687,6 @@ record_function(const InterrogateType &itype, FunctionIndex func_index) { } } - // If there's a remap taking no args and a remap - // taking only a single arg, assume we need varargs. - if ((func->_flags & FunctionRemap::F_no_args) && - (func->_flags & FunctionRemap::F_single_arg)) { - func->_flags |= FunctionRemap::F_varargs; - } - return func; } diff --git a/dtool/src/interrogate/interfaceMaker.h b/dtool/src/interrogate/interfaceMaker.h index 789adef439..2b5fbc4e83 100644 --- a/dtool/src/interrogate/interfaceMaker.h +++ b/dtool/src/interrogate/interfaceMaker.h @@ -69,6 +69,28 @@ public: static ostream &indent(ostream &out, int indent_level); public: + // This contains information about the number + // of arguments that the wrapping function should take. + enum ArgsType { + // This is deliberately engineered such that these + // values can be OR'ed together to produce another + // valid enum value. + AT_unknown = 0x00, + + // The method or function takes no arguments. + AT_no_args = 0x01, + + // There is only a single argument. + AT_single_arg = 0x02, + + // The method takes a variable number of arguments. + AT_varargs = 0x03, + + // The method may take keyword arguments, if appropriate + // in the scripting language. Implies AT_varargs. + AT_keyword_args = 0x07, + }; + class Function { public: Function(const string &name, @@ -83,6 +105,7 @@ public: Remaps _remaps; bool _has_this; int _flags; + ArgsType _args_type; }; typedef vector Functions; Functions _functions; diff --git a/dtool/src/interrogate/interfaceMakerPythonNative.cxx b/dtool/src/interrogate/interfaceMakerPythonNative.cxx index bde6164881..006e23325c 100755 --- a/dtool/src/interrogate/interfaceMakerPythonNative.cxx +++ b/dtool/src/interrogate/interfaceMakerPythonNative.cxx @@ -821,7 +821,7 @@ write_functions(ostream &out) { for (fi = _functions.begin(); fi != _functions.end(); ++fi) { Function *func = (*fi); if (!func->_itype.is_global() && is_function_legal(func)) { - write_function_for_top(out, NULL, func, ""); + write_function_for_top(out, NULL, func); } } @@ -872,29 +872,7 @@ write_class_details(ostream &out, Object *obj) { for (fi = obj->_methods.begin(); fi != obj->_methods.end(); ++fi) { Function *func = (*fi); if (func) { - SlottedFunctionDef def; - get_slotted_function_def(obj, func, def); - - ostringstream GetThis; - GetThis << " " << cClassName << " *local_this = NULL;\n"; - GetThis << " DTOOL_Call_ExtractThisPointerForType(self, &Dtool_" << ClassName << ", (void **)&local_this);\n"; - GetThis << " if (local_this == NULL) {\n"; - if (def._wrapper_type == WT_numeric_operator || def._wrapper_type == WT_ternary_operator) { - // WT_numeric_operator means we must return NotImplemented, instead - // of raising an exception, if the this pointer doesn't - // match. This is for things like __sub__, which Python - // likes to call on the wrong-type objects. - GetThis << " Py_INCREF(Py_NotImplemented);\n"; - GetThis << " return Py_NotImplemented;\n"; - - } else { - // Other functions should raise an exception if the this - // pointer isn't set or is the wrong type. - GetThis << " PyErr_SetString(PyExc_AttributeError, \"C++ object is not yet constructed, or already destructed.\");\n"; - GetThis << " return NULL;\n"; - } - GetThis << " }\n"; - write_function_for_top(out, obj, func, GetThis.str()); + write_function_for_top(out, obj, func); } } @@ -915,7 +893,7 @@ write_class_details(ostream &out, Object *obj) { Function *func = (*fi); std::string fname = "int Dtool_Init_" + ClassName + "(PyObject *self, PyObject *args, PyObject *kwds)"; - write_function_for_name(out, obj, func, fname, "", true, coercion_attempted, false, true, true, true); + write_function_for_name(out, obj, func, fname, true, coercion_attempted, AT_keyword_args, true); } if (coercion_attempted) { // If a coercion attempt was written into the above constructor, @@ -925,7 +903,7 @@ write_class_details(ostream &out, Object *obj) { Function *func = (*fi); std::string fname = "int Dtool_InitNoCoerce_" + ClassName + "(PyObject *self, PyObject *args)"; - write_function_for_name(out, obj, func, fname, "", false, coercion_attempted, false, true, false, true); + write_function_for_name(out, obj, func, fname, false, coercion_attempted, AT_varargs, true); } } else { // Otherwise, since the above constructor didn't involve any @@ -1143,17 +1121,28 @@ write_module_support(ostream &out, ostream *out_h, InterrogateModuleDef *def) { string name1 = methodNameFromCppName(func, "", false); string name2 = methodNameFromCppName(func, "", true); - const char *flags; - if (func->_flags & FunctionRemap::F_keyword_args) { + string flags; + switch (func->_args_type) { + case AT_keyword_args: flags = "METH_VARARGS | METH_KEYWORDS"; - } else if (func->_flags & FunctionRemap::F_varargs) { + break; + + case AT_varargs: flags = "METH_VARARGS"; - } else if (func->_flags & FunctionRemap::F_single_arg) { + break; + + case AT_single_arg: flags = "METH_O"; - } else { + break; + + default: flags = "METH_NOARGS"; + break; } + // Note: we shouldn't add METH_STATIC here, since both METH_STATIC + // and METH_CLASS are illegal for module-level functions. + out << " { \"" << name1 << "\", (PyCFunction) &" << func->_name << ", " << flags << ", (const char *)" << func->_name << "_comment},\n"; if (name1 != name2) { @@ -1261,13 +1250,12 @@ write_module_class(ostream &out, Object *obj) { out << "//********************************************************************\n"; out << "PyMethodDef Dtool_Methods_" << ClassName << "[] = {\n"; - std::map static_functions; std::map slotted_functions; // function Table bool got_copy = false; bool got_deepcopy = false; - int x = 0; + //int x = 0; for (fi = obj->_methods.begin(); fi != obj->_methods.end(); ++fi) { Function *func = (*fi); if (func->_name == "__copy__") { @@ -1276,33 +1264,39 @@ write_module_class(ostream &out, Object *obj) { got_deepcopy = true; } - if (!isFunctionWithThis(func)) { - // Save a reference to this static method, so we can add it - // directly to the class. - static_functions[x] = func; - } - string name1 = methodNameFromCppName(func, export_class_name, false); string name2 = methodNameFromCppName(func, export_class_name, true); - const char *flags; - if (func->_flags & FunctionRemap::F_keyword_args) { + string flags; + switch (func->_args_type) { + case AT_keyword_args: flags = "METH_VARARGS | METH_KEYWORDS"; - } else if (func->_flags & FunctionRemap::F_varargs) { + break; + + case AT_varargs: flags = "METH_VARARGS"; - } else if (func->_flags & FunctionRemap::F_single_arg) { + break; + + case AT_single_arg: flags = "METH_O"; - } else { + break; + + default: flags = "METH_NOARGS"; + break; + } + + if (!func->_has_this) { + flags += " | METH_STATIC"; } out << " { \"" << name1 << "\", (PyCFunction) &" << func->_name << ", " << flags << ", (char *) " << func->_name << "_comment},\n"; - ++x; + //++x; if (name1 != name2) { out << " { \"" << name2 << "\", (PyCFunction) &" << func->_name << ", " << flags << ", (char *) " << func->_name << "_comment},\n"; - ++x; + //++x; } SlottedFunctionDef slotted_def; @@ -1384,17 +1378,21 @@ write_module_class(ostream &out, Object *obj) { bool func_varargs = true; string call_func; - if (func->_flags & FunctionRemap::F_keyword_args) { + switch (func->_args_type) { + case AT_keyword_args: call_func = func->_name + "(self, args, NULL)"; + break; - } else if (func->_flags & FunctionRemap::F_varargs) { + case AT_varargs: call_func = func->_name + "(self, args)"; + break; - } else if (func->_flags & FunctionRemap::F_single_arg) { + case AT_single_arg: func_varargs = false; call_func = func->_name + "(self, arg)"; + break; - } else { + default: func_varargs = false; call_func = func->_name + "(self)"; } @@ -1536,7 +1534,8 @@ write_module_class(ostream &out, Object *obj) { string expected_params; bool coercion_attempted = false; - write_function_forset(out, obj, func, remaps, expected_params, 2, false, false, coercion_attempted, false, false, false, false, "index"); + write_function_forset(out, obj, func, remaps, expected_params, 2, false, false, + coercion_attempted, AT_no_args, false, "index"); out << " return NULL;\n"; out << "}\n\n"; @@ -1578,7 +1577,8 @@ write_module_class(ostream &out, Object *obj) { // very often, it's probably best to disable it for performance. string expected_params; bool coercion_attempted = false; - write_function_forset(out, obj, func, remaps, expected_params, 2, false, false, coercion_attempted, true, false, false, true, "index"); + write_function_forset(out, obj, func, remaps, expected_params, 2, false, false, + coercion_attempted, AT_single_arg, true, "index"); out << " if (!PyErr_Occurred()) {\n"; out << " PyErr_SetString(PyExc_TypeError,\n"; @@ -1998,7 +1998,7 @@ write_module_class(ostream &out, Object *obj) { Function::Remaps::const_iterator ri; for (ri = func->_remaps.begin(); ri != func->_remaps.end(); ++ri) { FunctionRemap *remap = (*ri); - if (is_remap_legal(remap) && remap->_has_this && (remap->_flags & FunctionRemap::F_single_arg)) { + if (is_remap_legal(remap) && remap->_has_this && (remap->_args_type == AT_single_arg)) { remaps.insert(remap); } } @@ -2024,7 +2024,8 @@ write_module_class(ostream &out, Object *obj) { string expected_params; bool coercion_attempted = false; - write_function_forset(out, obj, func, remaps, expected_params, 4, false, true, coercion_attempted, true, false, false, false); + write_function_forset(out, obj, func, remaps, expected_params, 4, false, true, + coercion_attempted, AT_single_arg, false); out << " if (PyErr_Occurred() && PyErr_ExceptionMatches(PyExc_TypeError)) {\n"; out << " PyErr_Clear();\n"; @@ -2213,7 +2214,14 @@ write_module_class(ostream &out, Object *obj) { int enum_count = nested_obj->_itype.number_of_enum_values(); for (int xx = 0; xx < enum_count; xx++) { string name1 = classNameFromCppName(nested_obj->_itype.get_enum_value_name(xx), false); - string name2 = classNameFromCppName(nested_obj->_itype.get_enum_value_name(xx), true); + string name2; + if (nested_obj->_itype.has_true_name()) { + name2 = classNameFromCppName(nested_obj->_itype.get_enum_value_name(xx), true); + } else { + // Don't generate the alternative syntax for anonymous enums, since we added support + // for those after we started deprecating the alternative syntax. + name2 = name1; + } int enum_value = nested_obj->_itype.get_enum_value(xx); out << "#if PY_MAJOR_VERSION >= 3\n"; out << " PyDict_SetItemString(Dtool_" << ClassName << ".As_PyTypeObject().tp_dict, \"" << name1 << "\", PyLong_FromLong(" << enum_value << "));\n"; @@ -2242,25 +2250,6 @@ write_module_class(ostream &out, Object *obj) { // Why make the class a member of itself? //out << " PyDict_SetItemString(Dtool_" <::iterator sfi; - for (sfi = static_functions.begin(); sfi != static_functions.end(); sfi++) { - out << " // Static Method " << methodNameFromCppName(sfi->second, export_class_name, false) << "\n"; - string name1 = methodNameFromCppName(sfi->second, export_class_name, false); - string name2 = methodNameFromCppName(sfi->second, export_class_name, true); - out << " PyDict_SetItemString(Dtool_" << ClassName - << ".As_PyTypeObject().tp_dict, \"" << name1 - << "\", PyCFunction_New(&Dtool_Methods_" << ClassName << "[" << sfi->first - << "], &Dtool_" << ClassName << ".As_PyObject()));\n"; - if (name1 != name2) { - out << " PyDict_SetItemString(Dtool_" << ClassName - << ".As_PyTypeObject().tp_dict, \"" << name2 - << "\", PyCFunction_New(&Dtool_Methods_" << ClassName << "[" << sfi->first - << "], &Dtool_" << ClassName << ".As_PyObject()));\n"; - } - } - bool is_runtime_typed = IsPandaTypedObject(obj->_itype._cpptype->as_struct_type()); if (HasAGetClassTypeFunction(obj->_itype)) { is_runtime_typed = true; @@ -2379,43 +2368,37 @@ write_prototype_for_name(ostream &out, InterfaceMaker::Function *func, const std // the indicated C++ function or method. //////////////////////////////////////////////////////////////////// void InterfaceMakerPythonNative:: -write_function_for_top(ostream &out, InterfaceMaker::Object *obj, InterfaceMaker::Function *func, const std::string &PreProcess) { +write_function_for_top(ostream &out, InterfaceMaker::Object *obj, InterfaceMaker::Function *func) { std::string fname; - bool single_arg = false; - bool have_varargs = false; - bool have_kwargs = false; - - if ((func->_flags & FunctionRemap::F_keyword_args) != 0) { - // Function takes both an args tuple and a kwargs dictionary. - have_kwargs = true; - have_varargs = true; - } else if ((func->_flags & FunctionRemap::F_varargs) != 0) { - // Function takes only an args tuple. - have_varargs = true; - } else if ((func->_flags & FunctionRemap::F_single_arg) != 0) { - // Function takes a single PyObject* argument. - single_arg = true; - } - if (func->_ifunc.is_unary_op()) { - assert(!single_arg); - assert(!have_varargs); - assert(!have_kwargs); + assert(func->_args_type == AT_no_args); } - if (have_kwargs) { - fname = "static PyObject *" + func->_name + "(PyObject *self, PyObject *args, PyObject *kwds)"; - } else if (have_varargs) { - fname = "static PyObject *" + func->_name + "(PyObject *self, PyObject *args)"; - } else if (single_arg) { - fname = "static PyObject *" + func->_name + "(PyObject *self, PyObject *arg)"; - } else { - fname = "static PyObject *" + func->_name + "(PyObject *self)"; + fname = "static PyObject *" + func->_name + "(PyObject *"; + + // This will be NULL for static funcs, so prevent code from using it. + if (func->_has_this) { + fname += "self"; } + switch (func->_args_type) { + case AT_keyword_args: + fname += ", PyObject *args, PyObject *kwds"; + break; + + case AT_varargs: + fname += ", PyObject *args"; + break; + + case AT_single_arg: + fname += ", PyObject *arg"; + break; + } + fname += ")"; + bool coercion_attempted = false; - write_function_for_name(out, obj, func, fname, PreProcess, true, coercion_attempted, single_arg, have_varargs, have_kwargs, false); + write_function_for_name(out, obj, func, fname, true, coercion_attempted, func->_args_type, false); } //////////////////////////////////////////////////////////////////// @@ -2424,10 +2407,10 @@ write_function_for_top(ostream &out, InterfaceMaker::Object *obj, InterfaceMaker // Wrap a complete name override function for Py..... //////////////////////////////////////////////////////////////////// void InterfaceMakerPythonNative:: -write_function_for_name(ostream &out1, InterfaceMaker::Object *obj, InterfaceMaker::Function *func, - const std::string &function_name, const std::string &PreProcess, - bool coercion_allowed, bool &coercion_attempted, bool single_arg, - bool have_varargs, bool have_kwargs, bool return_int) { +write_function_for_name(ostream &out1, InterfaceMaker::Object *obj, InterfaceMaker::Function *func, + const std::string &function_name, + bool coercion_allowed, bool &coercion_attempted, + ArgsType args_type, bool return_int) { ostringstream out; std::map > MapSets; @@ -2458,8 +2441,33 @@ write_function_for_name(ostream &out1, InterfaceMaker::Object *obj, InterfaceMak out1 << " *******************************************************************/\n"; out << function_name << " {\n"; - if (isFunctionWithThis(func)) { - out << PreProcess; + + if (func->_has_this) { + // Extract pointer from 'self' parameter. + std::string ClassName = make_safe_name(obj->_itype.get_scoped_name()); + std::string cClassName = obj->_itype.get_true_name(); + + SlottedFunctionDef def; + get_slotted_function_def(obj, func, def); + + out << " " << cClassName << " *local_this = NULL;\n"; + out << " DTOOL_Call_ExtractThisPointerForType(self, &Dtool_" << ClassName << ", (void **)&local_this);\n"; + out << " if (local_this == NULL) {\n"; + if (def._wrapper_type == WT_numeric_operator || def._wrapper_type == WT_ternary_operator) { + // WT_numeric_operator means we must return NotImplemented, instead + // of raising an exception, if the this pointer doesn't + // match. This is for things like __sub__, which Python + // likes to call on the wrong-type objects. + out << " Py_INCREF(Py_NotImplemented);\n"; + out << " return Py_NotImplemented;\n"; + + } else { + // Other functions should raise an exception if the this + // pointer isn't set or is the wrong type. + out << " PyErr_SetString(PyExc_AttributeError, \"C++ object is not yet constructed, or already destructed.\");\n"; + out << " return NULL;\n"; + } + out << " }\n"; } bool is_inplace = isInplaceFunction(func); @@ -2477,27 +2485,37 @@ write_function_for_name(ostream &out1, InterfaceMaker::Object *obj, InterfaceMak if (MapSets.size() > 1) { string expected_params; - if (have_varargs) { + switch (args_type) { + case AT_keyword_args: indent(out, 2) << "int parameter_count = PyTuple_Size(args);\n"; - if (have_kwargs) { + if (args_type == AT_keyword_args) { indent(out, 2) << "if (kwds != NULL && PyDict_Check(kwds)) {\n"; indent(out, 2) << " parameter_count += PyDict_Size(kwds);\n"; indent(out, 2) << "}\n"; } + break; - } else if (single_arg) { + case AT_varargs: + indent(out, 2) << "int parameter_count = PyTuple_Size(args);\n"; + break; + + case AT_single_arg: + // It shouldń't get here, but we'll handle these cases nonetheless. indent(out, 2) << "const int parameter_count = 1;\n"; + break; - } else { + default: indent(out, 2) << "const int parameter_count = 0;\n"; + break; } indent(out, 2) << "switch (parameter_count) {\n"; for (mii = MapSets.begin(); mii != MapSets.end(); mii ++) { indent(out, 2) << "case " << mii->first << ": {\n"; - write_function_forset(out, obj, func, mii->second, expected_params, 4, is_inplace, coercion_allowed, coercion_attempted, single_arg, have_varargs, have_kwargs, return_int); + write_function_forset(out, obj, func, mii->second, expected_params, 4, is_inplace, + coercion_allowed, coercion_attempted, args_type, return_int); indent(out, 4) << "break;\n"; indent(out, 2) << "}\n"; @@ -2561,7 +2579,8 @@ write_function_for_name(ostream &out1, InterfaceMaker::Object *obj, InterfaceMak } else { string expected_params = ""; for (mii = MapSets.begin(); mii != MapSets.end(); mii++) { - write_function_forset(out, obj, func, mii->second, expected_params, 2, is_inplace, coercion_allowed, coercion_attempted, single_arg, have_varargs, have_kwargs, return_int); + write_function_forset(out, obj, func, mii->second, expected_params, 2, is_inplace, + coercion_allowed, coercion_attempted, args_type, return_int); } out << " if (!PyErr_Occurred()) {\n"; @@ -2712,10 +2731,9 @@ write_function_forset(ostream &out, InterfaceMaker::Object *obj, std::set &remapsin, string &expected_params, int indent_level, bool is_inplace, bool coercion_allowed, - bool &coercion_attempted, bool single_arg, - bool have_varargs, bool have_kwargs, - bool return_int, - const string &first_pexpr) { + bool &coercion_attempted, + ArgsType args_type, + bool return_int, const string &first_pexpr) { // Do we accept any parameters that are class objects? If so, we // might need to check for parameter coercion. bool coercion_possible = false; @@ -2788,7 +2806,8 @@ write_function_forset(ostream &out, InterfaceMaker::Object *obj, indent(out, indent_level) << "// -2 "; remap->write_orig_prototype(out, 0); out << "\n"; - write_function_instance(out, obj, func, remap, expected_params, indent_level + 2, is_inplace, coercion_possible, coercion_attempted, single_arg, have_varargs, have_kwargs, return_int, first_pexpr); + write_function_instance(out, obj, func, remap, expected_params, indent_level + 2, is_inplace, + coercion_possible, coercion_attempted, args_type, return_int, first_pexpr); indent(out, indent_level + 2) << "PyErr_Clear();\n"; indent(out, indent_level) << "}\n\n"; @@ -2811,7 +2830,8 @@ write_function_forset(ostream &out, InterfaceMaker::Object *obj, << "// 1-" ; remap->write_orig_prototype(out, 0); out << "\n" ; - write_function_instance(out, obj, func, remap, expected_params, indent_level, is_inplace, coercion_possible, coercion_attempted, single_arg, have_varargs, have_kwargs, return_int, first_pexpr); + write_function_instance(out, obj, func, remap, expected_params, indent_level, is_inplace, + coercion_possible, coercion_attempted, args_type, return_int, first_pexpr); if (remap->_has_this && !remap->_const_method) { indent(out, indent_level - 2) @@ -2894,8 +2914,7 @@ write_function_instance(ostream &out, InterfaceMaker::Object *obj, FunctionRemap *remap, string &expected_params, int indent_level, bool is_inplace, bool coercion_possible, bool &coercion_attempted, - bool single_arg, bool have_varargs, - bool have_kwargs, bool return_int, + ArgsType args_type, bool return_int, const string &first_pexpr) { string format_specifiers; std::string keyword_list; @@ -3034,7 +3053,7 @@ write_function_instance(ostream &out, InterfaceMaker::Object *obj, } else { indent(out, indent_level) << "char *" << param_name << "_str;\n"; indent(out, indent_level) << "Py_ssize_t " << param_name << "_len;\n"; - if (single_arg) { + if (args_type == AT_single_arg) { out << "#if PY_MAJOR_VERSION >= 3\n"; indent(out, indent_level) << param_name << "_str = PyUnicode_AsUTF8AndSize(arg, &" @@ -3068,7 +3087,7 @@ write_function_instance(ostream &out, InterfaceMaker::Object *obj, only_pyobjects = false; } else if (TypeManager::is_bool(type)) { - if (single_arg) { + if (args_type == AT_single_arg) { param_name = "arg"; } else { indent(out, indent_level) << "PyObject *" << param_name << ";\n"; @@ -3080,7 +3099,7 @@ write_function_instance(ostream &out, InterfaceMaker::Object *obj, ++num_params; } else if (TypeManager::is_unsigned_longlong(type)) { - if (single_arg) { + if (args_type == AT_single_arg) { param_name = "arg"; } else { indent(out, indent_level) << "PyObject *" << param_name << ";\n"; @@ -3095,7 +3114,7 @@ write_function_instance(ostream &out, InterfaceMaker::Object *obj, ++num_params; } else if (TypeManager::is_longlong(type)) { - if (single_arg) { + if (args_type == AT_single_arg) { param_name = "arg"; } else { indent(out, indent_level) << "PyObject *" << param_name << ";\n"; @@ -3110,7 +3129,7 @@ write_function_instance(ostream &out, InterfaceMaker::Object *obj, ++num_params; } else if (TypeManager::is_unsigned_integer(type)) { - if (single_arg) { + if (args_type == AT_single_arg) { param_name = "arg"; } else { indent(out, indent_level) << "PyObject *" << param_name << ";\n"; @@ -3126,7 +3145,7 @@ write_function_instance(ostream &out, InterfaceMaker::Object *obj, ++num_params; } else if (TypeManager::is_integer(type)) { - if (single_arg) { + if (args_type == AT_single_arg) { pexpr_string = "(" + type->get_local_name(&parser) + ")PyInt_AS_LONG(arg)"; extra_param_check += " && PyInt_Check(arg)"; } else { @@ -3139,7 +3158,7 @@ write_function_instance(ostream &out, InterfaceMaker::Object *obj, ++num_params; } else if (TypeManager::is_double(type)) { - if (single_arg) { + if (args_type == AT_single_arg) { pexpr_string = "PyFloat_AsDouble(arg)"; extra_param_check += " && PyNumber_Check(arg)"; } else { @@ -3152,7 +3171,7 @@ write_function_instance(ostream &out, InterfaceMaker::Object *obj, ++num_params; } else if (TypeManager::is_float(type)) { - if (single_arg) { + if (args_type == AT_single_arg) { pexpr_string = "(float) PyFloat_AsDouble(arg)"; extra_param_check += " && PyNumber_Check(arg)"; } else { @@ -3173,7 +3192,7 @@ write_function_instance(ostream &out, InterfaceMaker::Object *obj, ++num_params; } else if (TypeManager::is_pointer_to_PyObject(type)) { - if (single_arg) { + if (args_type == AT_single_arg) { // This is a single-arg function, so there's no need // to convert anything. param_name = "arg"; @@ -3191,7 +3210,7 @@ write_function_instance(ostream &out, InterfaceMaker::Object *obj, check_exceptions = true; } else if (TypeManager::is_pointer_to_Py_buffer(type)) { - if (single_arg) { + if (args_type == AT_single_arg) { param_name = "arg"; } else { indent(out, indent_level) << "PyObject *" << param_name << ";\n"; @@ -3218,7 +3237,7 @@ write_function_instance(ostream &out, InterfaceMaker::Object *obj, expected_params += classNameFromCppName(obj_type->get_simple_name(), false); if (!remap->_has_this || pn != 0) { - if (single_arg) { + if (args_type == AT_single_arg) { param_name = "arg"; } else { indent(out, indent_level) << "PyObject *" << param_name << ";\n"; @@ -3278,7 +3297,7 @@ write_function_instance(ostream &out, InterfaceMaker::Object *obj, } else { // Ignore a parameter. - if (single_arg) { + if (args_type == AT_single_arg) { param_name = "arg"; } else { indent(out, indent_level) << "PyObject *" << param_name << ";\n"; @@ -3318,7 +3337,9 @@ write_function_instance(ostream &out, InterfaceMaker::Object *obj, std::string format_specifiers1 = format_specifiers + ":" + methodNameFromCppName(func, "", false); - if (have_kwargs) { + switch (args_type) { + case AT_keyword_args: + // Wrapper takes a varargs tuple and a keyword args dict. indent(out, indent_level) << "static char *keyword_list[] = {" << keyword_list << "NULL};\n"; indent(out, indent_level) @@ -3328,8 +3349,10 @@ write_function_instance(ostream &out, InterfaceMaker::Object *obj, ++open_scopes; indent_level += 2; + break; - } else if (have_varargs) { + case AT_varargs: + // Wrapper takes a varargs tuple. if (only_pyobjects) { // All parameters are PyObject*, so we can use the slightly // more efficient PyArg_UnpackTuple function instead. @@ -3347,16 +3370,18 @@ write_function_instance(ostream &out, InterfaceMaker::Object *obj, } ++open_scopes; indent_level += 2; + break; - } else if (single_arg && !only_pyobjects && - format_specifiers != "" && - format_specifiers != "O") { - indent(out, indent_level) - << "if (PyArg_Parse(arg, \"" << format_specifiers << "\"" - << parameter_list << ")) {\n"; + case AT_single_arg: + // Wrapper takes a single PyObject* argument. + if (!only_pyobjects && format_specifiers != "O") { + indent(out, indent_level) + << "if (PyArg_Parse(arg, \"" << format_specifiers << "\"" + << parameter_list << ")) {\n"; - ++open_scopes; - indent_level += 2; + ++open_scopes; + indent_level += 2; + } } } @@ -4155,23 +4180,6 @@ is_function_legal(Function *func) { return false; } -//////////////////////////////////////////////////////////////////////// -// Function : isFunctionWithThis -// -// If any rempas have a this .. this function has a this..( of self) to python.. -//////////////////////////////////////////////////////////////////////// -bool InterfaceMakerPythonNative:: -isFunctionWithThis(Function *func) { - Function::Remaps::const_iterator ri; - for (ri = func->_remaps.begin(); ri != func->_remaps.end(); ++ri) { - FunctionRemap *remap = (*ri); - if (remap->_has_this) { - return true; - } - } - return false; -} - //////////////////////////////////////////////////////// // Function : IsRunTimeTyped /////////////////////////////////////////////////////// diff --git a/dtool/src/interrogate/interfaceMakerPythonNative.h b/dtool/src/interrogate/interfaceMakerPythonNative.h index e8f0e15352..a7c20598db 100755 --- a/dtool/src/interrogate/interfaceMakerPythonNative.h +++ b/dtool/src/interrogate/interfaceMakerPythonNative.h @@ -27,8 +27,7 @@ class FunctionRemap; // Description : An InterfaceMaker for generating complex Python // function wrappers around C++ code. //////////////////////////////////////////////////////////////////// -class InterfaceMakerPythonNative : public InterfaceMakerPython -{ +class InterfaceMakerPythonNative : public InterfaceMakerPython { public: InterfaceMakerPythonNative(InterrogateModuleDef *def); virtual ~InterfaceMakerPythonNative(); @@ -92,27 +91,24 @@ private: void write_prototype_for_name(ostream &out, Function *func, const std::string &name); void write_prototype_for(ostream &out, Function *func); - void write_function_for_top(ostream &out, Object *obj, Function *func, const std::string &PreProcess); + void write_function_for_top(ostream &out, Object *obj, Function *func); void write_function_for_name(ostream &out, Object *obj, Function *func, - const std::string &name, const std::string &PreProcess, + const std::string &name, bool coercion_allowed, bool &coercion_attempted, - bool single_arg, bool have_varargs, - bool have_kwargs, bool return_int); + ArgsType args_type, bool return_int); void write_function_forset(ostream &out, Object *obj, Function *func, std::set &remaps, string &expected_params, int indent_level, bool inplace, bool coercion_allowed, bool &coercion_attempted, - bool single_arg, bool have_varargs, - bool have_kwargs, bool return_int, + ArgsType args_type, bool return_int, const string &first_expr = string()); void write_function_instance(ostream &out, Object *obj, Function *func, FunctionRemap *remap, string &expected_params, int indent_level, bool is_inplace, bool coercion_allowed, bool &coercion_attempted, - bool single_arg, bool have_varargs, - bool have_kwargs, bool return_int, + ArgsType args_type, bool return_int, const string &first_pexpr = string()); void pack_return_value(ostream &out, int indent_level, FunctionRemap *remap, diff --git a/dtool/src/interrogate/interrogateBuilder.cxx b/dtool/src/interrogate/interrogateBuilder.cxx index 3624de5352..1d1a3e868d 100644 --- a/dtool/src/interrogate/interrogateBuilder.cxx +++ b/dtool/src/interrogate/interrogateBuilder.cxx @@ -1863,26 +1863,34 @@ get_type(CPPType *type, bool global) { // First, check to see if it's already there. string true_name = type->get_local_name(&parser); - TypesByName::const_iterator tni = _types_by_name.find(true_name); - if (tni != _types_by_name.end()) { - // It's already here, so update the global flag. - index = (*tni).second; - if (index == 0) { - // This is an invalid type; we don't know anything about it. - return 0; - } - InterrogateType &itype = InterrogateDatabase::get_ptr()->update_type(index); - if (global) { - itype._flags |= InterrogateType::F_global; - } + if (true_name.empty()) { + // Whoops, it's an anonymous type. That's okay, because we'll + // usually only encounter them once anyway, so let's go ahead and + // define it without checking in _types_by_name. - if ((itype._flags & InterrogateType::F_fully_defined) != 0) { - return index; - } + } else { + TypesByName::const_iterator tni = _types_by_name.find(true_name); + if (tni != _types_by_name.end()) { + // It's already here, so update the global flag. + index = (*tni).second; + if (index == 0) { + // This is an invalid type; we don't know anything about it. + return 0; + } - // But wait--it's not fully defined yet! We'll go ahead and - // define it now. + InterrogateType &itype = InterrogateDatabase::get_ptr()->update_type(index); + if (global) { + itype._flags |= InterrogateType::F_global; + } + + if ((itype._flags & InterrogateType::F_fully_defined) != 0) { + return index; + } + + // But wait--it's not fully defined yet! We'll go ahead and + // define it now. + } } bool forced = in_forcetype(true_name); @@ -1890,7 +1898,9 @@ get_type(CPPType *type, bool global) { if (index == 0) { // It isn't already there, so we have to define it. index = InterrogateDatabase::get_ptr()->get_next_index(); - _types_by_name[true_name] = index; + if (!true_name.empty()) { + _types_by_name[true_name] = index; + } InterrogateType itype; if (global) { @@ -1904,7 +1914,7 @@ get_type(CPPType *type, bool global) { InterrogateDatabase::get_ptr()->update_type(index); itype._name = get_preferred_name(type); - + int num_alt_names = type->get_num_alt_names(); if (num_alt_names != 0) { itype._alt_names.clear(); @@ -1954,23 +1964,19 @@ get_type(CPPType *type, bool global) { } } - if (forced || !in_ignoretype(true_name)) - { + if (forced || !in_ignoretype(true_name)) { itype._flags |= InterrogateType::F_fully_defined; - if (type->as_simple_type() != (CPPSimpleType *)NULL) - { + if (type->as_simple_type() != (CPPSimpleType *)NULL) { define_atomic_type(itype, type->as_simple_type()); - } else if (type->as_pointer_type() != (CPPPointerType *)NULL) - { + } else if (type->as_pointer_type() != (CPPPointerType *)NULL) { define_wrapped_type(itype, type->as_pointer_type()); } else if (type->as_const_type() != (CPPConstType *)NULL) { define_wrapped_type(itype, type->as_const_type()); - } else if (type->as_struct_type() != (CPPStructType *)NULL) - { + } else if (type->as_struct_type() != (CPPStructType *)NULL) { define_struct_type(itype, type->as_struct_type(), index, forced); } else if (type->as_enum_type() != (CPPEnumType *)NULL) { @@ -1984,7 +1990,9 @@ get_type(CPPType *type, bool global) { // Remove the type from the database. InterrogateDatabase::get_ptr()->remove_type(index); - _types_by_name[true_name] = 0; + if (!true_name.empty()) { + _types_by_name[true_name] = 0; + } index = 0; } } @@ -2229,6 +2237,7 @@ define_struct_type(InterrogateType &itype, CPPStructType *cpptype, for (di = scope->_declarations.begin(); di != scope->_declarations.end(); ++di) { + if ((*di)->get_subtype() == CPPDeclaration::ST_instance) { CPPInstance *inst = (*di)->as_instance(); if (inst->_type->get_subtype() == CPPDeclaration::ST_function) { @@ -2243,15 +2252,12 @@ define_struct_type(InterrogateType &itype, CPPStructType *cpptype, } } - } else if ((*di)->get_subtype() == CPPDeclaration::ST_type_declaration) - { + } else if ((*di)->get_subtype() == CPPDeclaration::ST_type_declaration) { CPPType *type = (*di)->as_type_declaration()->_type; - if ((*di)->_vis <= min_vis || in_forcetype(type->get_local_name(&parser))) - { + if ((*di)->_vis <= min_vis || in_forcetype(type->get_local_name(&parser))) { if (type->as_struct_type() != (CPPStructType *)NULL || - type->as_enum_type() != (CPPEnumType *)NULL) - { + type->as_enum_type() != (CPPEnumType *)NULL) { // Here's a nested class or enum definition. type->_vis = (*di)->_vis; @@ -2259,13 +2265,20 @@ define_struct_type(InterrogateType &itype, CPPStructType *cpptype, assert(nested_type != (CPPExtensionType *)NULL); // Only try to export named types. - if (nested_type->_ident != (CPPIdentifier *)NULL) - { + if (nested_type->_ident != (CPPIdentifier *)NULL) { TypeIndex nested_index = get_type(nested_type, false); itype._nested_types.push_back(nested_index); } } } + } else if ((*di)->get_subtype() == CPPDeclaration::ST_enum) { + CPPType *type = (*di)->as_enum_type(); + + // An anonymous enum type. + if ((*di)->_vis <= min_vis) { + TypeIndex nested_index = get_type(type, false); + itype._nested_types.push_back(nested_index); + } } }