// Filename: interfaceMakerPythonObj.cxx // Created by: drose (19Sep01) // //////////////////////////////////////////////////////////////////// // // PANDA 3D SOFTWARE // Copyright (c) Carnegie Mellon University. All rights reserved. // // All use of this software is subject to the terms of the revised BSD // license. You should have received a copy of this license along // with this source code in a file named "LICENSE." // //////////////////////////////////////////////////////////////////// #include "interfaceMakerPythonObj.h" #include "interrogateBuilder.h" #include "interrogate.h" #include "functionRemap.h" #include "parameterRemapUnchanged.h" #include "typeManager.h" #include "functionWriterPtrFromPython.h" #include "functionWriterPtrToPython.h" #include "interrogateDatabase.h" #include "interrogateType.h" #include "interrogateFunction.h" #include "cppFunctionType.h" //////////////////////////////////////////////////////////////////// // Function: InterfaceMakerPythonObj::Constructor // Access: Public // Description: //////////////////////////////////////////////////////////////////// InterfaceMakerPythonObj:: InterfaceMakerPythonObj(InterrogateModuleDef *def) : InterfaceMakerPython(def) { } //////////////////////////////////////////////////////////////////// // Function: InterfaceMakerPythonObj::Destructor // Access: Public, Virtual // Description: //////////////////////////////////////////////////////////////////// InterfaceMakerPythonObj:: ~InterfaceMakerPythonObj() { } //////////////////////////////////////////////////////////////////// // Function: InterfaceMakerPythonObj::write_prototypes // Access: Public, Virtual // Description: Generates the list of function prototypes // corresponding to the functions that will be output in // write_functions(). //////////////////////////////////////////////////////////////////// void InterfaceMakerPythonObj:: write_prototypes(ostream &out, ostream *out_h) { FunctionsByIndex::iterator fi; for (fi = _functions.begin(); fi != _functions.end(); ++fi) { Function *func = (*fi).second; write_prototype_for(out, func); } out << "\n"; InterfaceMakerPython::write_prototypes(out,out_h); } //////////////////////////////////////////////////////////////////// // Function: InterfaceMakerPythonObj::write_functions // Access: Public, Virtual // Description: Generates the list of functions that are appropriate // for this interface. This function is called *before* // write_prototypes(), above. //////////////////////////////////////////////////////////////////// void InterfaceMakerPythonObj:: write_functions(ostream &out) { FunctionsByIndex::iterator fi; for (fi = _functions.begin(); fi != _functions.end(); ++fi) { Function *func = (*fi).second; write_function_for(out, func); } InterfaceMakerPython::write_functions(out); // Finally, generate all the make-class-wrapper functions. Objects::iterator oi; for (oi = _objects.begin(); oi != _objects.end(); ++oi) { Object *object = (*oi).second; write_class_wrapper(out, object); } } //////////////////////////////////////////////////////////////////// // Function: InterfaceMakerPythonObj::write_module // Access: Public, Virtual // Description: Generates whatever additional code is required to // support a module file. //////////////////////////////////////////////////////////////////// void InterfaceMakerPythonObj:: write_module(ostream &out,ostream *out_h, InterrogateModuleDef *def) { InterfaceMakerPython::write_module(out,out_h, def); out << "static PyMethodDef python_obj_funcs[] = {\n"; Objects::iterator oi; for (oi = _objects.begin(); oi != _objects.end(); ++oi) { Object *object = (*oi).second; Functions::iterator fi; for (fi = object->_constructors.begin(); fi != object->_constructors.end(); ++fi) { Function *func = (*fi); out << " { \"" << func->_ifunc.get_name() << "\", &" << func->_name << ", METH_VARARGS },\n"; } } out << " { NULL, NULL }\n" << "};\n\n" << "#if PY_MAJOR_VERSION >= 3\n" << "static struct PyModuleDef python_obj_module = {\n" << " PyModuleDef_HEAD_INIT,\n" << " \"" << def->library_name << "\",\n" << " NULL,\n" << " -1,\n" << " python_obj_funcs,\n" << " NULL, NULL, NULL, NULL\n" << "};\n\n" << "#define INIT_FUNC PyObject *PyInit_" << def->library_name << "\n" << "#else\n" << "#define INIT_FUNC void init" << def->library_name << "\n" << "#endif\n\n" << "#ifdef _WIN32\n" << "extern \"C\" __declspec(dllexport) INIT_FUNC();\n" << "#else\n" << "extern \"C\" INIT_FUNC();\n" << "#endif\n\n" << "INIT_FUNC() {\n" << "#if PY_MAJOR_VERSION >= 3\n" << " return PyModule_Create(&python_obj_module);\n" << "#else\n" << " Py_InitModule(\"" << def->library_name << "\", python_obj_funcs);\n" << "#endif\n" << "}\n\n"; } //////////////////////////////////////////////////////////////////// // Function: InterfaceMakerPythonObj::synthesize_this_parameter // Access: Public, Virtual // Description: This method should be overridden and redefined to // return true for interfaces that require the implicit // "this" parameter, if present, to be passed as the // first parameter to any wrapper functions. //////////////////////////////////////////////////////////////////// bool InterfaceMakerPythonObj:: synthesize_this_parameter() { return true; } //////////////////////////////////////////////////////////////////// // Function: InterfaceMakerPythonObj::get_builder_name // Access: Public, Static // Description: Returns the name of the InterfaceMaker function // generated to define the Python class for the // indicated struct type. //////////////////////////////////////////////////////////////////// string InterfaceMakerPythonObj:: get_builder_name(CPPType *struct_type) { return "get_python_class_" + InterrogateBuilder::clean_identifier(struct_type->get_local_name(&parser)); } //////////////////////////////////////////////////////////////////// // Function: InterfaceMakerPythonObj::get_wrapper_prefix // Access: Protected, Virtual // Description: Returns the prefix string used to generate wrapper // function names. //////////////////////////////////////////////////////////////////// string InterfaceMakerPythonObj:: get_wrapper_prefix() { return "wpo_"; } //////////////////////////////////////////////////////////////////// // Function: InterfaceMakerPythonObj::write_class_wrapper // Access: Private // Description: Writes a function that will define the Python class. //////////////////////////////////////////////////////////////////// void InterfaceMakerPythonObj:: write_class_wrapper(ostream &out, InterfaceMaker::Object *object) { CPPType *struct_type = object->_itype._cpptype; if (struct_type == (CPPType *)NULL) { return; } string name = get_builder_name(struct_type); string python_name = InterrogateBuilder::clean_identifier(struct_type->get_simple_name()); out << "/*\n" << " * Generate unique Python class for " << struct_type->get_local_name(&parser) << "\n" << " */\n" << "PyObject *\n" << name << "() {\n" << " static PyObject *wrapper = (PyObject *)NULL;\n" << " static PyMethodDef methods[] = {\n"; int methods_size = 0; int class_methods_size = 0; Functions::iterator fi; for (fi = object->_methods.begin(); fi != object->_methods.end(); ++fi) { Function *func = (*fi); if (func->_has_this) { out << " { \"" << func->_ifunc.get_name() << "\", &" << func->_name << ", METH_VARARGS },\n"; methods_size++; } } out << " };\n" << " static const int methods_size = " << methods_size << ";\n\n" << " static PyMethodDef class_methods[] = {\n"; for (fi = object->_methods.begin(); fi != object->_methods.end(); ++fi) { Function *func = (*fi); if (!func->_has_this) { out << " { \"" << func->_ifunc.get_name() << "\", &" << func->_name << ", METH_VARARGS },\n"; class_methods_size++; } } out << " };\n" << " static const int class_methods_size = " << class_methods_size << ";\n\n" << " if (wrapper == (PyObject *)NULL) {\n" << " int i;\n" << " PyObject *bases = PyTuple_New(0);\n" << " PyObject *dict = PyDict_New();\n" << "#if PY_MAJOR_VERSION >= 3\n" << " PyObject *name = PyUnicode_FromString(\"" << python_name << "\");\n" << "#else\n" << " PyObject *name = PyString_FromString(\"" << python_name << "\");\n" << "#endif\n" << " wrapper = PyClass_New(bases, dict, name);\n" << " for (i = 0; i < methods_size; ++i) {\n" << " PyObject *function, *method;\n" << " function = PyCFunction_New(&methods[i], (PyObject *)NULL);\n" << " method = PyMethod_New(function, (PyObject *)NULL, wrapper);\n" << " PyDict_SetItemString(dict, methods[i].ml_name, method);\n" << " }\n" << " for (i = 0; i < class_methods_size; ++i) {\n" << " PyObject *function;\n" << " function = PyCFunction_New(&class_methods[i], (PyObject *)NULL);\n" << " PyDict_SetItemString(dict, class_methods[i].ml_name, function);\n" << " }\n" << " }\n" << " return wrapper;\n" << "}\n\n"; } //////////////////////////////////////////////////////////////////// // Function: InterfaceMakerPythonObj::write_prototype_for // Access: Private // Description: Writes the prototype for the indicated function. //////////////////////////////////////////////////////////////////// void InterfaceMakerPythonObj:: write_prototype_for(ostream &out, InterfaceMaker::Function *func) { out << "static PyObject *" << func->_name << "(PyObject *self, PyObject *args);\n"; } //////////////////////////////////////////////////////////////////// // Function: InterfaceMakerPythonObj::write_function_for // Access: Private // Description: Writes the definition for a function that will call // the indicated C++ function or method. //////////////////////////////////////////////////////////////////// void InterfaceMakerPythonObj:: write_function_for(ostream &out, InterfaceMaker::Function *func) { Function::Remaps::const_iterator ri; out << "/*\n" << " * Python object wrapper for\n"; for (ri = func->_remaps.begin(); ri != func->_remaps.end(); ++ri) { FunctionRemap *remap = (*ri); out << " * "; remap->write_orig_prototype(out, 0); out << "\n"; } out << " */\n"; out << "static PyObject *" << func->_name << "(PyObject *, PyObject *args) {\n"; // Now write out each instance of the overloaded function. string expected_params = "Arguments must match one of:"; for (ri = func->_remaps.begin(); ri != func->_remaps.end(); ++ri) { FunctionRemap *remap = (*ri); expected_params += "\\n "; write_function_instance(out, 2, func, remap, expected_params); } // If we get here in the generated code, none of the parameters were // valid. Generate an error exception. (We don't rely on the error // already generated by ParseTuple(), because it only reports the // error for one flavor of the function, whereas we might accept // multiple flavors for the different overloaded C++ function // signatures. out << " PyErr_SetString(PyExc_TypeError, \"" << expected_params << "\");\n" << " return (PyObject *)NULL;\n"; out << "}\n\n"; } //////////////////////////////////////////////////////////////////// // Function: InterfaceMakerPythonObj::write_function_instance // Access: Private // Description: Writes out the part of a function that handles a // single instance of an overloaded function. //////////////////////////////////////////////////////////////////// void InterfaceMakerPythonObj:: write_function_instance(ostream &out, int indent_level, InterfaceMaker::Function *func, FunctionRemap *remap, string &expected_params) { indent(out, indent_level) << "{\n"; indent(out, indent_level + 2) << "/* "; remap->write_orig_prototype(out, 0); out << " */\n\n"; string format_specifiers; string parameter_list; vector_string pexprs; string extra_convert; string extra_param_check; string extra_cleanup; // Make one pass through the parameter list. We will output a // one-line temporary variable definition for each parameter, while // simultaneously building the ParseTuple() function call and also // the parameter expression list for call_function(). expected_params += remap->_cppfunc->get_simple_name(); expected_params += "("; int pn; for (pn = 0; pn < (int)remap->_parameters.size(); pn++) { if (pn != 0) { expected_params += ", "; } indent(out, indent_level + 2); CPPType *orig_type = remap->_parameters[pn]._remap->get_orig_type(); CPPType *type = remap->_parameters[pn]._remap->get_new_type(); string param_name = remap->get_parameter_name(pn); // This is the string to convert our local variable to the // appropriate C++ type. Normally this is just a cast. string pexpr_string = "(" + type->get_local_name(&parser) + ")" + param_name; if (remap->_parameters[pn]._remap->new_type_is_atomic_string()) { if (TypeManager::is_char_pointer(orig_type)) { out << "char *" << param_name; format_specifiers += "s"; parameter_list += ", &" + param_name; } else { out << "char *" << param_name << "_str; Py_ssize_t " << param_name << "_len"; format_specifiers += "s#"; parameter_list += ", &" + param_name + "_str, &" + param_name + "_len"; pexpr_string = "basic_string(" + param_name + "_str, " + param_name + "_len)"; } expected_params += "string"; } else if (TypeManager::is_bool(type)) { out << "PyObject *" << param_name; format_specifiers += "O"; parameter_list += ", &" + param_name; pexpr_string = "(PyObject_IsTrue(" + param_name + ")!=0)"; expected_params += "bool"; } else if (TypeManager::is_unsigned_longlong(type)) { out << "PyObject *" << param_name; format_specifiers += "O"; parameter_list += ", &" + param_name; extra_convert += " PyObject *" + param_name + "_long = PyNumber_Long(" + param_name + ");"; extra_param_check += "|| (" + param_name + "_long == NULL)"; pexpr_string = "PyLong_AsUnsignedLongLong(" + param_name + "_long)"; extra_cleanup += " Py_XDECREF(" + param_name + "_long);"; expected_params += "long"; } else if (TypeManager::is_longlong(type)) { out << "PyObject *" << param_name; format_specifiers += "O"; parameter_list += ", &" + param_name; extra_convert += " PyObject *" + param_name + "_long = PyNumber_Long(" + param_name + ");"; extra_param_check += "|| (" + param_name + "_long == NULL)"; pexpr_string = "PyLong_AsLongLong(" + param_name + "_long)"; extra_cleanup += " Py_XDECREF(" + param_name + "_long);"; expected_params += "long"; } else if (TypeManager::is_unsigned_integer(type)) { out << "PyObject *" << param_name; format_specifiers += "O"; parameter_list += ", &" + param_name; extra_convert += " PyObject *" + param_name + "_uint = PyNumber_Long(" + param_name + ");"; extra_param_check += "|| (" + param_name + "_uint == NULL)"; pexpr_string = "(unsigned int)PyLong_AsUnsignedLong(" + param_name + "_uint)"; extra_cleanup += " Py_XDECREF(" + param_name + "_uint);"; expected_params += "unsigned int"; } else if (TypeManager::is_integer(type)) { out << "int " << param_name; format_specifiers += "i"; parameter_list += ", &" + param_name; expected_params += "integer"; } else if (TypeManager::is_float(type)) { out << "double " << param_name; format_specifiers += "d"; parameter_list += ", &" + param_name; expected_params += "float"; } else if (TypeManager::is_char_pointer(type)) { out << "char *" << param_name; format_specifiers += "s"; parameter_list += ", &" + param_name; expected_params += "string"; } else if (TypeManager::is_pointer(type)) { FunctionWriterPtrFromPython *writer = get_ptr_from_python(type); writer->get_pointer_type()->output_instance(out, param_name, &parser); format_specifiers += "O&"; parameter_list += ", &" + writer->get_name() + ", &" + param_name; expected_params += writer->get_type()->get_preferred_name(); } else { // Ignore a parameter. out << "PyObject *" << param_name; format_specifiers += "O"; parameter_list += ", &" + param_name; expected_params += "any"; } if (remap->_parameters[pn]._has_name) { expected_params += " " + remap->_parameters[pn]._name; } out << ";\n"; pexprs.push_back(pexpr_string); } expected_params += ")"; indent(out, indent_level + 2) << "if (PyArg_ParseTuple(args, \"" << format_specifiers << "\"" << parameter_list << ")) {\n"; if (!extra_convert.empty()) { indent(out, indent_level + 3) << extra_convert << "\n"; } if (!extra_param_check.empty()) { indent(out, indent_level + 4) << "if (" << extra_param_check.substr(3) << ") {\n"; if (!extra_cleanup.empty()) { indent(out, indent_level + 5) << extra_cleanup << "\n"; } indent(out, indent_level + 6) << "PyErr_SetString(PyExc_TypeError, \"Invalid parameters.\");\n"; indent(out, indent_level + 6) << "return (PyObject *)NULL;\n"; indent(out, indent_level + 4) << "}\n"; } if (track_interpreter) { indent(out, indent_level + 4) << "in_interpreter = 0;\n"; } if (!remap->_void_return && remap->_return_type->new_type_is_atomic_string()) { // Treat strings as a special case. We don't want to format the // return expression. string return_expr = remap->call_function(out, indent_level + 4, false, "param0", pexprs); CPPType *type = remap->_return_type->get_orig_type(); indent(out, indent_level + 4); type->output_instance(out, "return_value", &parser); out << " = " << return_expr << ";\n"; if (track_interpreter) { indent(out, indent_level + 4) << "in_interpreter = 1;\n"; } if (!extra_cleanup.empty()) { indent(out, indent_level + 3) << extra_cleanup << "\n"; } return_expr = manage_return_value(out, indent_level + 4, remap, "return_value"); test_assert(out, indent_level + 4); pack_return_value(out, indent_level + 4, remap, return_expr); } else { string return_expr = remap->call_function(out, indent_level + 4, true, "param0", pexprs); if (return_expr.empty()) { if (track_interpreter) { indent(out, indent_level + 4) << "in_interpreter = 1;\n"; } if (!extra_cleanup.empty()) { indent(out, indent_level + 3) << extra_cleanup << "\n"; } test_assert(out, indent_level + 4); indent(out, indent_level + 4) << "return Py_BuildValue(\"\");\n"; } else { CPPType *type = remap->_return_type->get_temporary_type(); indent(out, indent_level + 4); type->output_instance(out, "return_value", &parser); out << " = " << return_expr << ";\n"; if (track_interpreter) { indent(out, indent_level + 4) << "in_interpreter = 1;\n"; } if (!extra_cleanup.empty()) { indent(out, indent_level + 3) << extra_cleanup << "\n"; } return_expr = manage_return_value(out, indent_level + 4, remap, "return_value"); test_assert(out, indent_level + 4); pack_return_value(out, indent_level + 4, remap, remap->_return_type->temporary_to_return(return_expr)); } } indent(out, indent_level + 2) << "}\n"; indent(out, indent_level + 2) << "PyErr_Clear();\n"; // Clear the error generated by ParseTuple() indent(out, indent_level) << "}\n"; } //////////////////////////////////////////////////////////////////// // Function: InterfaceMakerPythonObj::pack_return_value // Access: Private // Description: Outputs a command to pack the indicated expression, // of the return_type type, as a Python return value. //////////////////////////////////////////////////////////////////// void InterfaceMakerPythonObj:: pack_return_value(ostream &out, int indent_level, FunctionRemap *remap, string return_expr) { CPPType *orig_type = remap->_return_type->get_orig_type(); CPPType *type = remap->_return_type->get_new_type(); if (remap->_return_type->new_type_is_atomic_string()) { if (TypeManager::is_char_pointer(orig_type)) { out << "#if PY_MAJOR_VERSION >= 3\n"; indent(out, indent_level) << "return PyUnicode_FromString(" << return_expr << ");\n"; out << "#else\n"; indent(out, indent_level) << "return PyString_FromString(" << return_expr << ");\n"; out << "#endif\n"; } else { out << "#if PY_MAJOR_VERSION >= 3\n"; indent(out, indent_level) << "return PyUnicode_FromStringAndSize(" << return_expr << ".data(), (Py_ssize_t)" << return_expr << ".length());\n"; out << "#else\n"; indent(out, indent_level) << "return PyString_FromStringAndSize(" << return_expr << ".data(), (Py_ssize_t)" << return_expr << ".length());\n"; out << "#endif\n"; } } else if (TypeManager::is_bool(type)) { indent(out, indent_level) << "return PyBool_FromLong(" << return_expr << ");\n"; } else if (TypeManager::is_unsigned_longlong(type)) { indent(out, indent_level) << "return PyLong_FromUnsignedLongLong(" << return_expr << ");\n"; } else if (TypeManager::is_longlong(type)) { indent(out, indent_level) << "return PyLong_FromLongLong(" << return_expr << ");\n"; } else if (TypeManager::is_unsigned_integer(type)) { indent(out, indent_level) << "return PyLong_FromUnsignedLong(" << return_expr << ");\n"; } else if (TypeManager::is_integer(type)) { out << "#if PY_MAJOR_VERSION >= 3\n"; indent(out, indent_level) << "return PyLong_FromLong(" << return_expr << ");\n"; out << "#else\n"; indent(out, indent_level) << "return PyInt_FromLong(" << return_expr << ");\n"; out << "#endif\n"; } else if (TypeManager::is_float(type)) { indent(out, indent_level) << "return PyFloat_FromDouble(" << return_expr << ");\n"; } else if (TypeManager::is_char_pointer(type)) { out << "#if PY_MAJOR_VERSION >= 3\n"; indent(out, indent_level) << "return PyUnicode_FromString(" << return_expr << ");\n"; out << "#else\n"; indent(out, indent_level) << "return PyString_FromString(" << return_expr << ");\n"; out << "#endif\n"; } else if (TypeManager::is_pointer(type)) { bool caller_manages = remap->_return_value_needs_management; FunctionWriterPtrToPython *writer = get_ptr_to_python(type); indent(out, indent_level) << "return " << writer->get_name() << "((" << writer->get_pointer_type()->get_local_name(&parser) << ")" << return_expr << ", " << caller_manages << ");\n"; } else { // Return None. indent(out, indent_level) << "return Py_BuildValue(\"\");\n"; } } //////////////////////////////////////////////////////////////////// // Function: InterfaceMakerPythonObj::get_ptr_from_python // Access: Private // Description: Returns a FunctionWriter pointer suitable for // converting from a Python wrapper of the indicated // type to the corresponding C++ pointer. //////////////////////////////////////////////////////////////////// FunctionWriterPtrFromPython *InterfaceMakerPythonObj:: get_ptr_from_python(CPPType *type) { PtrConverter::iterator ci; ci = _from_python.find(type); if (ci != _from_python.end()) { // We've previously used this type. return (FunctionWriterPtrFromPython *)(*ci).second; } FunctionWriter *writer = _function_writers.add_writer(new FunctionWriterPtrFromPython(type)); _from_python.insert(PtrConverter::value_type(type, writer)); return (FunctionWriterPtrFromPython *)writer; } //////////////////////////////////////////////////////////////////// // Function: InterfaceMakerPythonObj::get_ptr_to_python // Access: Private // Description: Returns a FunctionWriter pointer suitable for // converting from a C++ pointer of the indicated // type to the corresponding Python wrapper. //////////////////////////////////////////////////////////////////// FunctionWriterPtrToPython *InterfaceMakerPythonObj:: get_ptr_to_python(CPPType *type) { PtrConverter::iterator ci; ci = _to_python.find(type); if (ci != _to_python.end()) { // We've previously used this type. return (FunctionWriterPtrToPython *)(*ci).second; } FunctionWriter *writer = _function_writers.add_writer(new FunctionWriterPtrToPython(type)); _to_python.insert(PtrConverter::value_type(type, writer)); return (FunctionWriterPtrToPython *)writer; }