// (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