// (C) Copyright David Abrahams 2000. Permission to copy, use, modify, sell and // distribute this software is granted provided this copyright notice appears // in all copies. This software is provided "as is" without express or implied // warranty, and with no claim as to its suitability for any purpose. // // The author gratefully acknowleges the support of Dragon Systems, Inc., in // producing this work. #include "extclass.h" #include namespace py { ExtensionInstance* get_extension_instance(PyObject* p) { // The object's type will just be some Class object, // but if its meta-type is right, then it is an ExtensionInstance. if (p->ob_type->ob_type != extension_meta_class()) { PyErr_SetString(PyExc_TypeError, p->ob_type->tp_name); throw py::ArgumentError(); } return static_cast(p); } void ExtensionInstance::add_implementation(std::auto_ptr holder) { for (WrappedObjects::const_iterator p = m_wrapped_objects.begin(); p != m_wrapped_objects.end(); ++p) { if (typeid(*holder) == typeid(**p)) { PyErr_SetString(PyExc_RuntimeError, "Base class already initialized"); throw ErrorAlreadySet(); } } m_wrapped_objects.push_back(holder.release()); } ExtensionInstance::ExtensionInstance(PyTypeObject* class_) : Instance(class_) { } ExtensionInstance::~ExtensionInstance() { for (WrappedObjects::const_iterator p = m_wrapped_objects.begin(), finish = m_wrapped_objects.end(); p != finish; ++p) { delete *p; } } MetaClass* extension_meta_class() { static MetaClass result; return &result; } typedef Class ExtClass; bool is_subclass(const ExtClass* derived, const PyObject* possible_base) { Tuple bases = derived->bases(); for (std::size_t i = 0, size = bases.size(); i < size; ++i) { const PyObject* base = bases[i].get(); if (base == possible_base) return true; if (base->ob_type == extension_meta_class()) { const ExtClass* base_class = Downcast(base); if (is_subclass(base_class, possible_base)) return true; } } return false; } // Return true iff instance is an instance of target_class bool is_instance(ExtensionInstance* instance, Class* target_class) { if (instance->ob_type == target_class) return true; else { return is_subclass( Downcast >(instance->ob_type).get(), as_object(target_class)); } } void two_string_error(PyObject* exception_object, const char* format, const char* s1, const char* s2) { char buffer[256]; std::size_t format_length = PY_CSTD_::strlen(format); std::size_t length1 = PY_CSTD_::strlen(s1); std::size_t length2 = PY_CSTD_::strlen(s2); std::size_t additional_length = length1 + length2; if (additional_length + format_length > format_length - 1) { std::size_t difference = sizeof(buffer) - 1 - additional_length; length1 -= difference / 2; additional_length -= difference / 2; } sprintf(buffer, format, length1, s1, length2, s2); PyErr_SetString(exception_object, buffer); if (exception_object == PyExc_TypeError) throw ArgumentError(); else throw ErrorAlreadySet(); } // This is called when an attempt has been made to convert the given instance to // a C++ type for which it doesn't have any instance data. In that case, either // the instance was not derived from the target_class, or the appropriate // __init__ function wasn't called to initialize the instance data of the target class. void report_missing_instance_data( ExtensionInstance* instance, // The object being converted Class* target_class, // the extension class of the C++ type const std::type_info& target_typeid, // The typeid of the C++ type bool target_is_ptr) { char buffer[256]; if (is_instance(instance, target_class)) { if (target_is_ptr) { two_string_error(PyExc_RuntimeError, "Object of extension class '%.*s' does not wrap <%.*s>.", instance->ob_type->tp_name, target_typeid.name()); } else { const char message[] = "__init__ function for extension class '%.*s' was never called."; sprintf(buffer, message, sizeof(buffer) - sizeof(message) - 1, target_class->tp_name); } PyErr_SetString(PyExc_RuntimeError, buffer); } else if (target_class == 0) { const char message[] = "Cannot convert to <%.*s>; its Python class was never created or has been deleted."; sprintf(buffer, message, sizeof(buffer) - sizeof(message) - 1, target_typeid.name()); PyErr_SetString(PyExc_RuntimeError, buffer); } else { two_string_error(PyExc_TypeError, "extension class '%.*s' is not convertible into '%.*s'.", instance->ob_type->tp_name, target_class->tp_name); } } void report_missing_instance_data( ExtensionInstance* instance, // The object being converted Class* target_class, // the extension class of the C++ type const std::type_info& target_typeid) // The typeid of the C++ type { report_missing_instance_data(instance, target_class, target_typeid, false); } void report_missing_ptr_data( ExtensionInstance* instance, // The object being converted Class* target_class, // the extension class of the C++ type const std::type_info& target_typeid) // The typeid of the C++ type { report_missing_instance_data(instance, target_class, target_typeid, true); } void report_missing_class_object(const std::type_info& info) { char buffer[256]; const char message[] = "Cannot convert <%.*s> to python; its Python class was never created or has been deleted."; sprintf(buffer, message, sizeof(buffer) - sizeof(message) - 1, info.name()); PyErr_SetString(PyExc_RuntimeError, buffer); throw ErrorAlreadySet(); } void report_released_smart_pointer(const std::type_info& info) { char buffer[256]; const char message[] = "Converting from python, pointer or smart pointer to <%.*s> is NULL."; sprintf(buffer, message, sizeof(buffer) - sizeof(message) - 1, info.name()); PyErr_SetString(PyExc_RuntimeError, buffer); throw ArgumentError(); } ReadOnlySetattrFunction::ReadOnlySetattrFunction(const char* name) : m_name(name) { } PyObject* ReadOnlySetattrFunction::do_call(PyObject* /*args*/, PyObject* /*keywords*/) const { PyErr_SetObject(PyExc_AttributeError, ("'" + m_name + "' attribute is read-only").get()); return 0; } const char* ReadOnlySetattrFunction::description() const { return "uncallable"; } ExtensionClassBase::ExtensionClassBase(const char* name) : Class( extension_meta_class(), String(name), Tuple(), Dict()) { } /* This function is used in from_python() to convert wrapped classes that are related by inheritance. The problem is this: although C++ provides all necessary conversion operators, source and target of a conversion must be known at compile time. However, in Python we want to convert classes at runtime. The solution is to generate conversion functions at compile time, register them within the appropriate class objects and call them when a particular runtime conversion is required. If functions for any possible conversion have to be stored, their number will grow qudratically. To reduce this number, we actually store only conversion functions between adjacent levels in the inheritance tree. By traversing the tree recursively, we can build any allowed conversion as a concatenation of simple conversions. This traversal is done in the functions try_base_class_conversions() and try_derived_class_conversions(). If a particular conversion is impossible, all conversion functions will return a NULL pointer. The function extract_object_from_holder() attempts to actually extract the pointer to the contained object from an InstanceHolderBase (a wrapper class). A conversion of the held object to 'T *' is allowed when the conversion 'dynamic_cast *>(an_instance_holder_base)' succeeds. */ void * ExtensionClassBase::try_class_conversions(InstanceHolderBase* object) const { void* result = try_derived_class_conversions(object); if(result) return result; return try_base_class_conversions(object); } void* ExtensionClassBase::try_base_class_conversions(InstanceHolderBase* object) const { for (std::size_t i = 0; i < base_classes().size(); ++i) { if(base_classes()[i].convert == 0) continue; void* result1 = base_classes()[i].class_object->extract_object_from_holder(object); if (result1) return (*base_classes()[i].convert)(result1); void* result2 = base_classes()[i].class_object->try_base_class_conversions(object); if (result2) return (*base_classes()[i].convert)(result2); } return 0; } void* ExtensionClassBase::try_derived_class_conversions(InstanceHolderBase* object) const { for (std::size_t i = 0; i < derived_classes().size(); ++i) { void* result1 = derived_classes()[i].class_object->extract_object_from_holder(object); if (result1) return (*derived_classes()[i].convert)(result1); void* result2 = derived_classes()[i].class_object->try_derived_class_conversions(object); if (result2) return (*derived_classes()[i].convert)(result2); } return 0; } void ExtensionClassBase::add_method(Function* method, const char* name) { add_method(PyPtr(method), name); } void ExtensionClassBase::add_method(PyPtr method, const char* name) { // If we have created a special Python base class which wraps C++ classes // derived from T, the method should really be added there. Target will be // that Python class object. Class* target = (bases().size() == 0) ? this : Downcast >(bases()[0].get()).get(); // Add the attribute to the computed target Function::add_to_namespace(method, name, target->dict().get()); // If it is a special member function it should be enabled both here and there. enable_named_method(this, name); } void ExtensionClassBase::add_default_method(Function* method, const char* name) { add_default_method(PyPtr(method), name); } // A rather complicated thing is going on here in order to make a very specific // class of cases work. When wrapping the following C++: // // struct Base { // Base(); // will be constructed from Python // virtual int f() const // might be called from C++ // { return 1; } // default implementation // }; // // struct Derived : Base { // int f() const { return 0; } // overridden in C++ // }; // // boost::shared_ptr factory(bool selector) { // return boost::shared_ptr(selector ? new Base : new Derived); // } // // Normally we would use the same Python ExtensionClass object to represent both // Base and boost::shared_ptr, since they have essentially the same // operations (see the comment on InstanceHolder in extclass_pygen.h for // details). If there was no need to override Base::f() in Python, that would // work fine. In this case, since f() is virtual, the programmer must provide a // subclass of Base which calls back into Python: // // struct BaseCallback : Base { // BaseCallback(PyObject* self) : m_self(self) {} // int f() const { return py::Callback::call_method(m_self, "f"); } // static int default_f(const Base* self) const { self->Base::f(); } // }; // // default_f() is what gets registered under the name "f" in the "Base" // ExtensionClass' attribute dict. When C++ calls f() on a wrapped instance of // Base, we call back into Python to find the "f" attribute, which calls // default_f() (unless it has been overridden in Python) and in turn the default // implementation (Base::f()) is called. // // Now consider what happens when the Python programmer writes // >>> factory(0).f() // // The shared_ptr which is created on the C++ side is converted by // to_python() into a Python instance of the "Base" ExtensionClass. Then Python // looks up the "f" attribute, and finds the wrapper for default_f(), which it // calls. That calls Base::f(), returning 1. What we really wanted was a call to // Derived::f(), returning 0. // // In this case we actually need a different Python ExtensionClass to represent // C++ subclasses of Base. When the first default method implementation is added // to an ExtensionClass, we "push" all of the non-default methods up into a // newly-created base class of the "Base" ExtensionClass, called // "Base_base". "Base's" attribute dict contains only default method // implementations. // // A Python call to factory() then results in an object of class "Base_base", // whose "f" method is bound to Base::f() - since this is a virtual function // pointer, the member function actually called is determined by the // most-derived class that implements f(). // // A Python call to Base() results in an object of class "Base" wrapping a // BaseCallback object, whose "f" method is bound to BaseCallback::default_f() // ...which calls Base::f() explicitly. void ExtensionClassBase::add_default_method(PyPtr method, const char* name) { if (bases().size() == 0) { Class* new_base = new Class( extension_meta_class(), this->name() + String("_base"), Tuple(), dict()); add_base(Ptr(as_object(new_base))); // We have transferred everything in our dict into the base class, so // clear our dict now. It will henceforth contain only default method // implementations. dict() = Dict(); } Function::add_to_namespace(method, name, dict().get()); } void ExtensionClassBase::add_constructor_object(Function* init_function) { add_method(init_function, "__init__"); } void ExtensionClassBase::add_setter_method(Function* setter_, const char* name) { PyPtr setter(setter_); add_method(setter, (detail::setattr_string() + name + "__").c_str()); } void ExtensionClassBase::add_getter_method(Function* getter_, const char* name) { PyPtr getter(getter_); add_method(getter, (detail::getattr_string() + name + "__").c_str()); } } // namespace py