// Copyright David Abrahams 2002. // Distributed under the Boost Software License, Version 1.0. (See // accompanying file LICENSE_1_0.txt or copy at // http://www.boost.org/LICENSE_1_0.txt) #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace boost { namespace python { namespace converter { shared_ptr_deleter::shared_ptr_deleter(handle<> owner) : owner(owner) {} shared_ptr_deleter::~shared_ptr_deleter() {} void shared_ptr_deleter::operator()(void const*) { owner.reset(); } namespace { // An lvalue conversion function which extracts a char const* from a // Python String. void* convert_to_cstring(PyObject* obj) { return PyString_Check(obj) ? PyString_AsString(obj) : 0; } // Given a target type and a SlotPolicy describing how to perform a // given conversion, registers from_python converters which use the // SlotPolicy to extract the type. template struct slot_rvalue_from_python { public: slot_rvalue_from_python() { registry::insert( &slot_rvalue_from_python::convertible , &slot_rvalue_from_python::construct , type_id() ); } private: static void* convertible(PyObject* obj) { unaryfunc* slot = SlotPolicy::get_slot(obj); return slot && *slot ? slot : 0; } static void construct(PyObject* obj, rvalue_from_python_stage1_data* data) { // Get the (intermediate) source object unaryfunc creator = *static_cast(data->convertible); handle<> intermediate(creator(obj)); // Get the location in which to construct void* storage = ((rvalue_from_python_storage*)data)->storage.bytes; # ifdef _MSC_VER # pragma warning(push) # pragma warning(disable:4244) # endif new (storage) T( SlotPolicy::extract(intermediate.get()) ); # ifdef _MSC_VER # pragma warning(pop) # endif // record successful construction data->convertible = storage; } }; // A SlotPolicy for extracting signed integer types from Python objects struct signed_int_rvalue_from_python_base { static unaryfunc* get_slot(PyObject* obj) { PyNumberMethods* number_methods = obj->ob_type->tp_as_number; if (number_methods == 0) return 0; return (PyInt_Check(obj) || PyLong_Check(obj)) ? &number_methods->nb_int : 0; } }; template struct signed_int_rvalue_from_python : signed_int_rvalue_from_python_base { static T extract(PyObject* intermediate) { long x = PyInt_AsLong(intermediate); if (PyErr_Occurred()) throw_error_already_set(); return numeric_cast(x); } }; // identity_unaryfunc/py_object_identity -- manufacture a unaryfunc // "slot" which just returns its argument. extern "C" PyObject* identity_unaryfunc(PyObject* x) { Py_INCREF(x); return x; } unaryfunc py_object_identity = identity_unaryfunc; // A SlotPolicy for extracting unsigned integer types from Python objects struct unsigned_int_rvalue_from_python_base { static unaryfunc* get_slot(PyObject* obj) { PyNumberMethods* number_methods = obj->ob_type->tp_as_number; if (number_methods == 0) return 0; return (PyInt_Check(obj) || PyLong_Check(obj)) ? &py_object_identity : 0; } }; template struct unsigned_int_rvalue_from_python : unsigned_int_rvalue_from_python_base { static T extract(PyObject* intermediate) { return numeric_cast( PyLong_Check(intermediate) ? PyLong_AsUnsignedLong(intermediate) : PyInt_AS_LONG(intermediate)); } }; // Checking Python's macro instead of Boost's - we don't seem to get // the config right all the time. Furthermore, Python's is defined // when long long is absent but __int64 is present. #ifdef HAVE_LONG_LONG // A SlotPolicy for extracting long long types from Python objects struct long_long_rvalue_from_python_base { static unaryfunc* get_slot(PyObject* obj) { PyNumberMethods* number_methods = obj->ob_type->tp_as_number; if (number_methods == 0) return 0; // Return the identity conversion slot to avoid creating a // new object. We'll handle that in the extract function if (PyInt_Check(obj)) return &number_methods->nb_int; else if (PyLong_Check(obj)) return &number_methods->nb_long; else return 0; } }; struct long_long_rvalue_from_python : long_long_rvalue_from_python_base { static BOOST_PYTHON_LONG_LONG extract(PyObject* intermediate) { if (PyInt_Check(intermediate)) { return PyInt_AS_LONG(intermediate); } else { BOOST_PYTHON_LONG_LONG result = PyLong_AsLongLong(intermediate); if (PyErr_Occurred()) throw_error_already_set(); return result; } } }; struct unsigned_long_long_rvalue_from_python : long_long_rvalue_from_python_base { static unsigned BOOST_PYTHON_LONG_LONG extract(PyObject* intermediate) { if (PyInt_Check(intermediate)) { return numeric_cast(PyInt_AS_LONG(intermediate)); } else { unsigned BOOST_PYTHON_LONG_LONG result = PyLong_AsUnsignedLongLong(intermediate); if (PyErr_Occurred()) throw_error_already_set(); return result; } } }; #endif // A SlotPolicy for extracting bool from a Python object struct bool_rvalue_from_python { static unaryfunc* get_slot(PyObject* obj) { return obj == Py_None || PyInt_Check(obj) ? &py_object_identity : 0; } static bool extract(PyObject* intermediate) { return PyObject_IsTrue(intermediate); } }; // A SlotPolicy for extracting floating types from Python objects. struct float_rvalue_from_python { static unaryfunc* get_slot(PyObject* obj) { PyNumberMethods* number_methods = obj->ob_type->tp_as_number; if (number_methods == 0) return 0; // For integer types, return the tp_int conversion slot to avoid // creating a new object. We'll handle that below if (PyInt_Check(obj)) return &number_methods->nb_int; return (PyLong_Check(obj) || PyFloat_Check(obj)) ? &number_methods->nb_float : 0; } static double extract(PyObject* intermediate) { if (PyInt_Check(intermediate)) { return PyInt_AS_LONG(intermediate); } else { return PyFloat_AS_DOUBLE(intermediate); } } }; // A SlotPolicy for extracting C++ strings from Python objects. struct string_rvalue_from_python { // If the underlying object is "string-able" this will succeed static unaryfunc* get_slot(PyObject* obj) { return (PyString_Check(obj)) ? &obj->ob_type->tp_str : 0; }; // Remember that this will be used to construct the result object static std::string extract(PyObject* intermediate) { return std::string(PyString_AsString(intermediate),PyString_Size(intermediate)); } }; #if defined(Py_USING_UNICODE) && !defined(BOOST_NO_STD_WSTRING) // encode_string_unaryfunc/py_encode_string -- manufacture a unaryfunc // "slot" which encodes a Python string using the default encoding extern "C" PyObject* encode_string_unaryfunc(PyObject* x) { return PyUnicode_FromEncodedObject( x, 0, 0 ); } unaryfunc py_encode_string = encode_string_unaryfunc; // A SlotPolicy for extracting C++ strings from Python objects. struct wstring_rvalue_from_python { // If the underlying object is "string-able" this will succeed static unaryfunc* get_slot(PyObject* obj) { return PyUnicode_Check(obj) ? &py_object_identity : PyString_Check(obj) ? &py_encode_string : 0; }; // Remember that this will be used to construct the result object static std::wstring extract(PyObject* intermediate) { std::wstring result(::PyObject_Length(intermediate), L' '); if (!result.empty()) { int err = PyUnicode_AsWideChar( (PyUnicodeObject *)intermediate , &result[0] , result.size()); if (err == -1) throw_error_already_set(); } return result; } }; #endif struct complex_rvalue_from_python { static unaryfunc* get_slot(PyObject* obj) { if (PyComplex_Check(obj)) return &py_object_identity; else return float_rvalue_from_python::get_slot(obj); } static std::complex extract(PyObject* intermediate) { if (PyComplex_Check(intermediate)) { return std::complex( PyComplex_RealAsDouble(intermediate) , PyComplex_ImagAsDouble(intermediate)); } else if (PyInt_Check(intermediate)) { return PyInt_AS_LONG(intermediate); } else { return PyFloat_AS_DOUBLE(intermediate); } } }; } BOOST_PYTHON_DECL PyObject* do_return_to_python(char x) { return PyString_FromStringAndSize(&x, 1); } BOOST_PYTHON_DECL PyObject* do_return_to_python(char const* x) { return x ? PyString_FromString(x) : boost::python::detail::none(); } BOOST_PYTHON_DECL PyObject* do_return_to_python(PyObject* x) { return x ? x : boost::python::detail::none(); } BOOST_PYTHON_DECL PyObject* do_arg_to_python(PyObject* x) { if (x == 0) return boost::python::detail::none(); Py_INCREF(x); return x; } #define REGISTER_INT_CONVERTERS(signedness, U) \ slot_rvalue_from_python< \ signedness U \ ,signedness##_int_rvalue_from_python \ >() #define REGISTER_INT_CONVERTERS2(U) \ REGISTER_INT_CONVERTERS(signed, U); \ REGISTER_INT_CONVERTERS(unsigned, U) void initialize_builtin_converters() { // booleans slot_rvalue_from_python(); // integer types REGISTER_INT_CONVERTERS2(char); REGISTER_INT_CONVERTERS2(short); REGISTER_INT_CONVERTERS2(int); REGISTER_INT_CONVERTERS2(long); // using Python's macro instead of Boost's - we don't seem to get the // config right all the time. # ifdef HAVE_LONG_LONG slot_rvalue_from_python(); slot_rvalue_from_python(); # endif // floating types slot_rvalue_from_python(); slot_rvalue_from_python(); slot_rvalue_from_python(); slot_rvalue_from_python,complex_rvalue_from_python>(); slot_rvalue_from_python,complex_rvalue_from_python>(); slot_rvalue_from_python,complex_rvalue_from_python>(); // Add an lvalue converter for char which gets us char const* registry::insert(convert_to_cstring,type_id()); // Register by-value converters to std::string, std::wstring #if defined(Py_USING_UNICODE) && !defined(BOOST_NO_STD_WSTRING) slot_rvalue_from_python(); # endif slot_rvalue_from_python(); } }}} // namespace boost::python::converter