From b2c04a8c7acde07c76fad045db75a7eb975c12d2 Mon Sep 17 00:00:00 2001 From: rdb Date: Sat, 30 Jun 2018 17:33:09 +0200 Subject: [PATCH] interrogate: support scoped enum args and return values --- .../interfaceMakerPythonNative.cxx | 86 +++++++++++++++++-- dtool/src/interrogate/typeManager.cxx | 20 +++++ dtool/src/interrogate/typeManager.h | 1 + dtool/src/interrogatedb/py_panda.I | 14 +++ dtool/src/interrogatedb/py_panda.cxx | 5 +- dtool/src/interrogatedb/py_panda.h | 6 +- 6 files changed, 121 insertions(+), 11 deletions(-) diff --git a/dtool/src/interrogate/interfaceMakerPythonNative.cxx b/dtool/src/interrogate/interfaceMakerPythonNative.cxx index 7961286874..6f300eed03 100644 --- a/dtool/src/interrogate/interfaceMakerPythonNative.cxx +++ b/dtool/src/interrogate/interfaceMakerPythonNative.cxx @@ -815,6 +815,11 @@ write_prototypes(ostream &out_code, ostream *out_h) { // _external_imports.insert(object->_itype._cpptype); } } + } else if (object->_itype.is_scoped_enum() && isExportThisRun(object->_itype._cpptype)) { + // Forward declare where we will put the scoped enum type. + string class_name = object->_itype._cpptype->get_local_name(&parser); + string safe_name = make_safe_name(class_name); + out_code << "static PyTypeObject *Dtool_Ptr_" << safe_name << " = nullptr;\n"; } } @@ -1301,7 +1306,10 @@ write_module_support(ostream &out, ostream *out_h, InterrogateModuleDef *def) { int enum_count = object->_itype.number_of_enum_values(); if (object->_itype.is_scoped_enum()) { - // Convert as Python 3.4 enum. + // Convert as Python 3.4-style enum. + string class_name = object->_itype._cpptype->get_local_name(&parser); + string safe_name = make_safe_name(class_name); + CPPType *underlying_type = TypeManager::unwrap_const(object->_itype._cpptype->as_enum_type()->get_underlying_type()); string cast_to = underlying_type->get_local_name(&parser); out << "#if PY_VERSION_HEX >= 0x03040000\n\n"; @@ -1318,9 +1326,11 @@ write_module_support(ostream &out, ostream *out_h, InterrogateModuleDef *def) { << object->_itype.get_enum_value_name(xx) << "));\n" " PyTuple_SET_ITEM(members, " << xx << ", member);\n"; } + out << " Dtool_Ptr_" << safe_name << " = Dtool_EnumType_Create(\"" + << object->_itype.get_name() << "\", members, \"" + << _def->module_name << "\");\n"; out << " PyModule_AddObject(module, \"" << object->_itype.get_name() - << "\", Dtool_EnumType_Create(\"" << object->_itype.get_name() - << "\", members, \"" << _def->module_name << "\"));\n"; + << "\", (PyObject *)Dtool_Ptr_" << safe_name << ");\n"; out << " }\n"; out << "#endif\n"; } else { @@ -3112,7 +3122,10 @@ write_module_class(ostream &out, Object *obj) { // support recently. } else if (nested_obj->_itype.is_scoped_enum()) { - // Convert enum class as Python 3.4 enum. + // Convert enum class as Python 3.4-style enum. + string class_name = nested_obj->_itype._cpptype->get_local_name(&parser); + string safe_name = make_safe_name(class_name); + int enum_count = nested_obj->_itype.number_of_enum_values(); CPPType *underlying_type = TypeManager::unwrap_const(nested_obj->_itype._cpptype->as_enum_type()->get_underlying_type()); string cast_to = underlying_type->get_local_name(&parser); @@ -3130,9 +3143,11 @@ write_module_class(ostream &out, Object *obj) { << nested_obj->_itype.get_enum_value_name(xx) << "));\n" " PyTuple_SET_ITEM(members, " << xx << ", member);\n"; } + out << " Dtool_Ptr_" << safe_name << " = Dtool_EnumType_Create(\"" + << nested_obj->_itype.get_name() << "\", members, \"" + << _def->module_name << "\");\n"; out << " PyDict_SetItemString(dict, \"" << nested_obj->_itype.get_name() - << "\", Dtool_EnumType_Create(\"" << nested_obj->_itype.get_name() - << "\", members, \"" << _def->module_name << "\"));\n"; + << "\", (PyObject *)Dtool_Ptr_" << safe_name << ");\n"; out << " }\n"; out << "#endif\n"; @@ -4821,6 +4836,46 @@ write_function_instance(ostream &out, FunctionRemap *remap, clear_error = true; only_pyobjects = false; + } else if (TypeManager::is_scoped_enum(type)) { + if (args_type == AT_single_arg) { + param_name = "arg"; + } else { + indent(out, indent_level) << "PyObject *" << param_name; + if (default_value != nullptr) { + out << " = nullptr"; + } + out << ";\n"; + format_specifiers += "O"; + parameter_list += ", &" + param_name; + } + + CPPEnumType *enum_type = (CPPEnumType *)TypeManager::unwrap(type); + CPPType *underlying_type = enum_type->get_underlying_type(); + underlying_type = TypeManager::unwrap_const(underlying_type); + + //indent(out, indent_level); + //underlying_type->output_instance(out, param_name + "_val", &parser); + //out << default_expr << ";\n"; + extra_convert << "long " << param_name << "_val"; + + if (default_value != nullptr) { + extra_convert << " = (long)"; + default_value->output(extra_convert, 0, &parser, false); + extra_convert << + ";\nif (" << param_name << " != nullptr) {\n" + " " << param_name << "_val = Dtool_EnumValue_AsLong(" + param_name + ");\n" + "}"; + } else { + extra_convert + << ";\n" + << param_name << "_val = Dtool_EnumValue_AsLong(" + param_name + ");\n"; + } + + pexpr_string = "(" + enum_type->get_local_name(&parser) + ")" + param_name + "_val"; + expected_params += classNameFromCppName(enum_type->get_simple_name(), false); + extra_param_check << " && " << param_name << "_val != -1"; + clear_error = true; + } else if (TypeManager::is_bool(type)) { if (args_type == AT_single_arg) { param_name = "arg"; @@ -6220,7 +6275,24 @@ pack_return_value(ostream &out, int indent_level, FunctionRemap *remap, CPPType *orig_type = return_type->get_orig_type(); CPPType *type = return_type->get_new_type(); - if (return_type->new_type_is_atomic_string() || + if (TypeManager::is_scoped_enum(type)) { + InterrogateDatabase *idb = InterrogateDatabase::get_ptr(); + TypeIndex type_index = builder.get_type(TypeManager::unwrap(TypeManager::resolve_type(orig_type)), false); + const InterrogateType &itype = idb->get_type(type_index); + string safe_name = make_safe_name(itype.get_scoped_name()); + + indent(out, indent_level) + << "return PyObject_CallFunction((PyObject *)Dtool_Ptr_" << safe_name; + + CPPType *underlying_type = ((CPPEnumType *)itype._cpptype)->get_underlying_type(); + if (TypeManager::is_unsigned_integer(underlying_type)) { + out << ", \"k\", (unsigned long)"; + } else { + out << ", \"l\", (long)"; + } + out << "(" << return_expr << "));\n"; + + } else if (return_type->new_type_is_atomic_string() || TypeManager::is_simple(type) || TypeManager::is_char_pointer(type) || TypeManager::is_wchar_pointer(type) || diff --git a/dtool/src/interrogate/typeManager.cxx b/dtool/src/interrogate/typeManager.cxx index b7b744aa86..f12541111d 100644 --- a/dtool/src/interrogate/typeManager.cxx +++ b/dtool/src/interrogate/typeManager.cxx @@ -310,6 +310,26 @@ is_struct(CPPType *type) { } } +/** + * Returns true if the indicated type is an enum class, const or otherwise. + */ +bool TypeManager:: +is_scoped_enum(CPPType *type) { + switch (type->get_subtype()) { + case CPPDeclaration::ST_enum: + return ((CPPEnumType *)type)->is_scoped(); + + case CPPDeclaration::ST_const: + return is_scoped_enum(type->as_const_type()->_wrapped_around); + + case CPPDeclaration::ST_typedef: + return is_scoped_enum(type->as_typedef_type()->_type); + + default: + return false; + } +} + /** * Returns true if the indicated type is some kind of enumerated type, const * or otherwise. diff --git a/dtool/src/interrogate/typeManager.h b/dtool/src/interrogate/typeManager.h index d507e947ef..db831cdafc 100644 --- a/dtool/src/interrogate/typeManager.h +++ b/dtool/src/interrogate/typeManager.h @@ -52,6 +52,7 @@ public: static bool is_pointer(CPPType *type); static bool is_const(CPPType *type); static bool is_struct(CPPType *type); + static bool is_scoped_enum(CPPType *type); static bool is_enum(CPPType *type); static bool is_const_enum(CPPType *type); static bool is_const_ref_to_enum(CPPType *type); diff --git a/dtool/src/interrogatedb/py_panda.I b/dtool/src/interrogatedb/py_panda.I index 540e9ee58d..69f8961463 100644 --- a/dtool/src/interrogatedb/py_panda.I +++ b/dtool/src/interrogatedb/py_panda.I @@ -97,6 +97,20 @@ INLINE PyObject *DtoolInstance_RichComparePointers(PyObject *v1, PyObject *v2, i Py_RETURN_RICHCOMPARE(cmpval, 0, op); } +/** + * Converts the enum value to a C long. + */ +INLINE long Dtool_EnumValue_AsLong(PyObject *value) { + PyObject *val = PyObject_GetAttrString(value, "value"); + if (val != nullptr) { + long as_long = PyLongOrInt_AS_LONG(val); + Py_DECREF(val); + return as_long; + } else { + return -1; + } +} + /** * These functions wrap a pointer for a class that defines get_type_handle(). */ diff --git a/dtool/src/interrogatedb/py_panda.cxx b/dtool/src/interrogatedb/py_panda.cxx index 7e867aacad..9e7563036d 100644 --- a/dtool/src/interrogatedb/py_panda.cxx +++ b/dtool/src/interrogatedb/py_panda.cxx @@ -309,7 +309,7 @@ PyObject *_Dtool_Return(PyObject *value) { /** * Creates a Python 3.4-style enum type. Steals reference to 'names'. */ -PyObject *Dtool_EnumType_Create(const char *name, PyObject *names, const char *module) { +PyTypeObject *Dtool_EnumType_Create(const char *name, PyObject *names, const char *module) { static PyObject *enum_class = nullptr; static PyObject *enum_meta = nullptr; static PyObject *enum_create = nullptr; @@ -330,7 +330,8 @@ PyObject *Dtool_EnumType_Create(const char *name, PyObject *names, const char *m PyObject_SetAttrString(result, "__module__", modstr); Py_DECREF(modstr); } - return result; + nassertr(PyType_Check(result), nullptr); + return (PyTypeObject *)result; } /** diff --git a/dtool/src/interrogatedb/py_panda.h b/dtool/src/interrogatedb/py_panda.h index 258ff66484..7916147e4d 100644 --- a/dtool/src/interrogatedb/py_panda.h +++ b/dtool/src/interrogatedb/py_panda.h @@ -258,8 +258,10 @@ EXPCL_INTERROGATEDB PyObject *_Dtool_Return(PyObject *value); /** * Wrapper around Python 3.4's enum library, which does not have a C API. */ -EXPCL_INTERROGATEDB PyObject *Dtool_EnumType_Create(const char *name, PyObject *names, - const char *module = nullptr); +EXPCL_INTERROGATEDB PyTypeObject *Dtool_EnumType_Create(const char *name, PyObject *names, + const char *module = nullptr); +EXPCL_INTERROGATEDB INLINE long Dtool_EnumValue_AsLong(PyObject *value); + /**