// Filename: wrapperBuilderPython.C // Created by: drose (07Aug00) // //////////////////////////////////////////////////////////////////// #include "wrapperBuilderPython.h" #include "interrogate.h" #include "parameterRemap.h" #include "typeManager.h" #include #include #include #include #include #include #include #include #include #include #include //////////////////////////////////////////////////////////////////// // Function: WrapperBuilderPython::Constructor // Access: Public // Description: //////////////////////////////////////////////////////////////////// WrapperBuilderPython:: WrapperBuilderPython() { } //////////////////////////////////////////////////////////////////// // Function: WrapperBuilderPython::write_wrapper // Access: Public, Virtual // Description: Generates a wrapper function to the indicated output // stream. //////////////////////////////////////////////////////////////////// void WrapperBuilderPython:: write_wrapper(ostream &out, const string &wrapper_name) const { out << "/*\n" << " * Python wrapper for\n" << " * " << _description << "\n" << " */\n"; if (!output_function_names) { // If we're not saving the function names, don't export it from // the library. out << "static "; } out << "PyObject *\n" << wrapper_name << "(PyObject *, PyObject *args) {\n"; int pn; string format_specifiers; string parameter_list; vector pexprs; // 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(). for (pn = 0; pn < (int)_parameters.size(); pn++) { out << " "; CPPType *orig_type = _parameters[pn]._remap->get_orig_type(); CPPType *type = _parameters[pn]._remap->get_new_type(); // 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) + ")" + get_parameter_name(pn); if (_parameters[pn]._remap->new_type_is_atomic_string()) { if (TypeManager::is_char_pointer(orig_type)) { out << "char *" << get_parameter_name(pn); format_specifiers += "s"; parameter_list += ", &" + get_parameter_name(pn); } else { out << "char *" << get_parameter_name(pn) << "_str; int " << get_parameter_name(pn) << "_len"; format_specifiers += "s#"; parameter_list += ", &" + get_parameter_name(pn) + "_str, &" + get_parameter_name(pn) + "_len"; pexpr_string = "basic_string(" + get_parameter_name(pn) + "_str, " + get_parameter_name(pn) + "_len)"; } } else if (TypeManager::is_bool(type)) { out << "PyObject *" << get_parameter_name(pn); format_specifiers += "O"; parameter_list += ", &" + get_parameter_name(pn); pexpr_string = "PyObject_IsTrue(" + get_parameter_name(pn) + ")"; } else if (TypeManager::is_integer(type)) { out << "int " << get_parameter_name(pn); format_specifiers += "i"; parameter_list += ", &" + get_parameter_name(pn); } else if (TypeManager::is_float(type)) { out << "double " << get_parameter_name(pn); format_specifiers += "d"; parameter_list += ", &" + get_parameter_name(pn); } else if (TypeManager::is_char_pointer(type)) { out << "char *" << get_parameter_name(pn); format_specifiers += "s"; parameter_list += ", &" + get_parameter_name(pn); } else if (TypeManager::is_pointer(type)) { out << "int " << get_parameter_name(pn); format_specifiers += "i"; parameter_list += ", &" + get_parameter_name(pn); } else { // Ignore a parameter. out << "PyObject *" << get_parameter_name(pn); format_specifiers += "O"; parameter_list += ", &" + get_parameter_name(pn); } out << ";\n"; pexprs.push_back(pexpr_string); } out << " if (PyArg_ParseTuple(args, \"" << format_specifiers << "\"" << parameter_list << ")) {\n"; if (_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 = call_function(out, 4, false, pexprs); CPPType *type = _return_type->get_orig_type(); out << " "; type->output_instance(out, "return_value", &parser); out << " = " << return_expr << ";\n"; return_expr = manage_return_value(out, 4, "return_value"); test_assert(out, 4); pack_return_value(out, return_expr); } else { string return_expr = call_function(out, 4, true, pexprs); if (return_expr.empty()) { test_assert(out, 4); out << " return Py_BuildValue(\"\");\n"; } else { CPPType *type = _return_type->get_temporary_type(); out << " "; type->output_instance(out, "return_value", &parser); out << " = " << return_expr << ";\n"; return_expr = manage_return_value(out, 4, "return_value"); test_assert(out, 4); pack_return_value(out, _return_type->temporary_to_return(return_expr)); } } out << " }\n"; out << " return (PyObject *)NULL;\n"; out << "}\n\n"; } //////////////////////////////////////////////////////////////////// // Function: WrapperBuilderPython::get_wrapper_name // Access: Public, Virtual // Description: Returns the callable name for this wrapper function. //////////////////////////////////////////////////////////////////// string WrapperBuilderPython:: get_wrapper_name(const string &library_hash_name) const { return "_inP" + library_hash_name + _hash; } //////////////////////////////////////////////////////////////////// // Function: WrapperBuilderPython::supports_atomic_strings // Access: Public, Virtual // Description: Returns true if this kind of wrapper can support true // atomic string objects (and not have to fiddle with // char *). //////////////////////////////////////////////////////////////////// bool WrapperBuilderPython:: supports_atomic_strings() const { return true; } //////////////////////////////////////////////////////////////////// // Function: WrapperBuilderPython::get_calling_convention // Access: Public, Virtual // Description: Returns an indication of what kind of function we are // building. //////////////////////////////////////////////////////////////////// WrapperBuilder::CallingConvention WrapperBuilderPython:: get_calling_convention() const { return CC_python; } //////////////////////////////////////////////////////////////////// // Function: WrapperBuilderPython::test_assert // Access: Protected // Description: Outputs code to check to see if an assertion has // failed while the C++ code was executing, and report // this failure back to Python. //////////////////////////////////////////////////////////////////// void WrapperBuilderPython:: test_assert(ostream &out, int indent_level) const { if (watch_asserts) { out << "#ifndef NDEBUG\n"; indent(out, indent_level) << "Notify *notify = Notify::ptr();\n"; indent(out, indent_level) << "if (notify->has_assert_failed()) {\n"; indent(out, indent_level + 2) << "PyErr_SetString(PyExc_AssertionError, notify->get_assert_error_message().c_str());\n"; indent(out, indent_level + 2) << "notify->clear_assert_failed();\n"; indent(out, indent_level + 2) << "return (PyObject *)NULL;\n"; indent(out, indent_level) << "}\n"; out << "#endif\n"; } } //////////////////////////////////////////////////////////////////// // Function: WrapperBuilderPython::pack_return_value // Access: Protected // Description: Outputs a command to pack the indicated expression, // of the return_type type, as a Python return value. //////////////////////////////////////////////////////////////////// void WrapperBuilderPython:: pack_return_value(ostream &out, string return_expr) const { CPPType *orig_type = _return_type->get_orig_type(); CPPType *type = _return_type->get_new_type(); out << " return Py_BuildValue("; if (_return_type->new_type_is_atomic_string()) { if (TypeManager::is_char_pointer(orig_type)) { out << "\"s\", " << return_expr; } else { out << "\"s#\", " << return_expr << ".data(), " << return_expr << ".length()"; } } else if (TypeManager::is_integer(type)) { out << "\"i\", (int)(" << return_expr << ")"; } else if (TypeManager::is_float(type)) { out << "\"d\", (double)(" << return_expr << ")"; } else if (TypeManager::is_char_pointer(type)) { out << "\"s\", " << return_expr; } else if (TypeManager::is_pointer(type)) { out << "\"i\", (int)" << return_expr; } else { // Return None. out << "\"\""; } out << ");\n"; }