diff --git a/gen_extclass.py b/gen_extclass.py index 04c2c555..13b84fb4 100644 --- a/gen_extclass.py +++ b/gen_extclass.py @@ -23,10 +23,352 @@ def gen_extclass(args): // This file automatically generated for %d-argument constructors by // gen_extclass.py -#ifndef EXTCLASS_PYGEN_DWA070900_H_ -# define EXTCLASS_PYGEN_DWA070900_H_ +#ifndef EXTENSION_CLASS_DWA052000_H_ +# define EXTENSION_CLASS_DWA052000_H_ +# include "pyconfig.h" +# include "subclass.h" +# include +# include "none.h" +# include "objects.h" +# include "functions.h" +# include +# include "init_function.h" +# include +# include + +namespace py { + +// forward declarations +class ExtensionInstance; +template class InstanceHolder; +template class InstanceValueHolder; +template class InstancePtrHolder; + +MetaClass* extension_meta_class(); +ExtensionInstance* get_extension_instance(PyObject* p); +void report_missing_instance_data(ExtensionInstance*, Class*, const std::type_info&); +void report_missing_ptr_data(ExtensionInstance*, Class*, const std::type_info&); +void report_missing_class_object(const std::type_info&); +void report_released_smart_pointer(const std::type_info&); +template +struct ExtensionClassFromPython +{ +}; + +template +T* check_non_null(T* p) +{ + if (p == 0) + report_released_smart_pointer(typeid(T)); + return p; +} + +template class HeldInstance; + +class ExtensionClassBase : public Class +{ + public: + ExtensionClassBase(const char* name); + protected: + void add_method(PyPtr method, const char* name); + void add_default_method(PyPtr method, const char* name); + void add_method(Function* method, const char* name); + void add_default_method(Function* method, const char* name); + + void add_constructor_object(Function*); + void add_setter_method(Function*, const char* name); + void add_getter_method(Function*, const char* name); +}; + +template +class ClassRegistry +{ + public: + static Class* class_object() + { return static_class_object; } + static void register_class(py::Class*); + static void unregister_class(py::Class*); + private: + static py::Class* static_class_object; +}; + +#ifdef PY_NO_INLINE_FRIENDS_IN_NAMESPACE // back to global namespace for this GCC bug +} +#endif + +// This class' only job is to define from_python and to_python converters for T +// and U. T is the class the user really intends to wrap. U is a class derived +// from T with some virtual function overriding boilerplate, or if there are no +// virtual functions, U = HeldInstance. +template > +class PyExtensionClassConverters +{ + public: +#ifdef BOOST_MSVC + // Convert return values of type T to python objects. What happens if T is + // not copyable? Apparently there is no problem with g++ or MSVC unless this + // is actually used. With a conforming compiler we will have a problem. + friend PyObject* to_python(const T& x) + { + py::PyPtr result(create_instance(false)); + result->add_implementation( + std::auto_ptr( + new py::InstanceValueHolder(result.get(), x))); + return result.release(); + } +#else + friend py::Type py_holder_type(const T&) + { return py::Type(); } +#endif + + PyExtensionClassConverters() {} + + // Convert to T* + friend T* from_python(PyObject* obj, py::Type) + { + // Downcast to an ExtensionInstance, then find the actual T + py::ExtensionInstance* self = py::get_extension_instance(obj); + typedef std::vector::const_iterator Iterator; + for (Iterator p = self->wrapped_objects().begin(); + p != self->wrapped_objects().end(); ++p) + { + py::InstanceHolder* held = dynamic_cast*>(*p); + if (held != 0) + return held->target(); + } + py::report_missing_instance_data(self, py::ClassRegistry::class_object(), typeid(T)); + throw py::ArgumentError(); + } + + // Convert to PtrType, where PtrType can be dereferenced to obtain a T. + template + static PtrType& ptr_from_python(PyObject* obj, py::Type) + { + // Downcast to an ExtensionInstance, then find the actual T + py::ExtensionInstance* self = py::get_extension_instance(obj); + typedef std::vector::const_iterator Iterator; + for (Iterator p = self->wrapped_objects().begin(); + p != self->wrapped_objects().end(); ++p) + { + py::InstancePtrHolder* held = + dynamic_cast*>(*p); + if (held != 0) + return held->ptr(); + } + py::report_missing_ptr_data(self, py::ClassRegistry::class_object(), typeid(T)); + throw py::ArgumentError(); + } + + template + static PyObject* ptr_to_python(PtrType x) + { + py::PyPtr result(create_instance(true)); + result->add_implementation( + std::auto_ptr( + new py::InstancePtrHolder(x))); + return result.release(); + } + + static py::PyPtr create_instance(bool seek_base) + { + if (py::ClassRegistry::class_object() == 0) + py::report_missing_class_object(typeid(T)); + + py::Class* class_ + = seek_base && py::ClassRegistry::class_object()->bases().size() > 0 + ? py::Downcast >( + py::ClassRegistry::class_object()->bases()[0].get()).get() + : py::ClassRegistry::class_object(); + + return py::PyPtr(new py::ExtensionInstance(class_)); + } + + + // Convert to const T* + friend const T* from_python(PyObject* p, py::Type) + { return from_python(p, py::Type()); } + + // Convert to T& + friend T& from_python(PyObject* p, py::Type) + { return *py::check_non_null(from_python(p, py::Type())); } + + // Convert to const T& + friend const T& from_python(PyObject* p, py::Type) + { return from_python(p, py::Type()); } + + // Convert to T + friend const T& from_python(PyObject* p, py::Type) + { return from_python(p, py::Type()); } + + friend std::auto_ptr& from_python(PyObject* p, py::Type&>) + { return ptr_from_python(p, py::Type >()); } + + friend std::auto_ptr& from_python(PyObject* p, py::Type >) + { return ptr_from_python(p, py::Type >()); } + + friend const std::auto_ptr& from_python(PyObject* p, py::Type&>) + { return ptr_from_python(p, py::Type >()); } + + friend PyObject* to_python(std::auto_ptr x) + { return ptr_to_python(x); } + + friend boost::shared_ptr& from_python(PyObject* p, py::Type&>) + { return ptr_from_python(p, py::Type >()); } + + friend boost::shared_ptr& from_python(PyObject* p, py::Type >) + { return ptr_from_python(p, py::Type >()); } + + friend const boost::shared_ptr& from_python(PyObject* p, py::Type&>) + { return ptr_from_python(p, py::Type >()); } + + friend PyObject* to_python(boost::shared_ptr x) + { return ptr_to_python(x); } +}; + +#ifndef BOOST_MSVC +template +py::InstanceHolderBase* +py_copy_to_new_value_holder(py::ExtensionInstance* p, const T& x, py::Type) +{ + return new py::InstanceValueHolder(p, x); +} + +template +PyObject* to_python(const T& x) +{ + py::PyPtr result( + PyExtensionClassConverters::create_instance(false)); + result->add_implementation( + std::auto_ptr( + py_copy_to_new_value_holder(result.get(), x, py_holder_type(x)))); + return result.release(); +} +#endif + +#ifdef PY_NO_INLINE_FRIENDS_IN_NAMESPACE // back from global namespace for this GCC bug +namespace py { +using ::PyExtensionClassConverters; +#endif + +template class InstanceHolder; + +class ReadOnlySetattrFunction : public Function +{ + public: + ReadOnlySetattrFunction(const char* name); + PyObject* do_call(PyObject* args, PyObject* keywords) const; + const char* description() const; + private: + String m_name; +}; + +// An easy way to make an extension base class which wraps T. Note that Python +// subclasses of this class will simply be Class objects. +// +// U should be a class derived from T which overrides virtual functions with +// boilerplate code to call back into Python. See extclass_demo.h for examples. +// +// U is optional, but you won't be able to override any member functions in +// Python which are called from C++ if you don't supply it. If you just want to +// be able to use T in python without overriding member functions, you can omit +// U. +template > +class ExtensionClass + : public PyExtensionClassConverters, // This generates the to_python/from_python functions + public ExtensionClassBase +{ + public: + typedef T WrappedType; + typedef U CallbackType; + + // Construct with a name that comes from typeid(T).name(). The name only + // affects the objects of this class are represented through repr() + ExtensionClass(); + + // Construct with the given name. The name only affects the objects of this + // class are represented through repr() + ExtensionClass(const char* name); + + ~ExtensionClass(); + + // define constructors +""" % args + + gen_function( +""" template <%(class A%n%:, %)> + void def(Constructor<%(A%n%:, %)>) + // The following incantation builds a Signature1, Signature2,... object. It + // should _all_ get optimized away. + { add_constructor( + %(prepend(Type::Id(), + %) Signature0()%()%)); + } +""", args) + + +""" + // define member functions. In fact this works for free functions, too - + // they act like static member functions, or if they start with the + // appropriate self argument (as a pointer), they can be used just like + // ordinary member functions -- just like Python! + template + void def(Fn fn, const char* name) + { + this->add_method(new_wrapped_function(fn), name); + } + + // Define a virtual member function with a default implementation. + // default_fn should be a function which provides the default implementation. + // Be careful that default_fn does not in fact call fn virtually! + template + void def(Fn fn, const char* name, DefaultFn default_fn) + { + this->add_default_method(new_wrapped_function(default_fn), name); + this->add_method(new_wrapped_function(fn), name); + } + + // Provide a function which implements x., reading from the given + // member (pm) of the T instance + template + void def_getter(MemberType T::*pm, const char* name) + { + this->add_getter_method(new GetterFunction(pm), name); + } + + // Provide a function which implements assignment to x., writing to + // the given member (pm) of the T instance + template + void def_setter(MemberType T::*pm, const char* name) + { + this->add_setter_method(new SetterFunction(pm), name); + } + + // Expose the given member (pm) of the T instance as a read-only attribute + template + void def_readonly(MemberType T::*pm, const char* name) + { + this->add_setter_method(new ReadOnlySetattrFunction(name), name); + this->def_getter(pm, name); + } + + // Expose the given member (pm) of the T instance as a read/write attribute + template + void def_read_write(MemberType T::*pm, const char* name) + { + this->def_getter(pm, name); + this->def_setter(pm, name); + } + + private: + typedef InstanceValueHolder Holder; + + template + void add_constructor(Signature sig) + { + this->add_constructor_object(InitFunction::create(sig)); + } +}; + // A simple wrapper over a T which allows us to use ExtensionClass with a // single template parameter only. See ExtensionClass, above. template @@ -34,7 +376,7 @@ class HeldInstance : public T { // There are no member functions: we want to avoid inadvertently overriding // any virtual functions in T. -public:""" % args +public:""" + gen_functions(held_instance, args) + """ protected: @@ -66,8 +408,85 @@ public: private: Wrapper m_held; }; +""" + +""" +template +class InstancePtrHolder : public InstanceHolder +{ + public: + HeldType* target() { return &*m_ptr; } + PtrType& ptr() { return m_ptr; } -#endif + InstancePtrHolder(PtrType ptr) : m_ptr(ptr) {} + private: + PtrType m_ptr; +}; + +class ExtensionInstance : public Instance +{ + public: + ExtensionInstance(PyTypeObject* class_); + ~ExtensionInstance(); + + void add_implementation(std::auto_ptr holder); + + typedef std::vector WrappedObjects; + const WrappedObjects& wrapped_objects() const + { return m_wrapped_objects; } + private: + WrappedObjects m_wrapped_objects; +}; + +// +// Template function implementations +// + +template +ExtensionClass::ExtensionClass() + : ExtensionClassBase(typeid(T).name()) +{ + ClassRegistry::register_class(this); +} + +template +ExtensionClass::ExtensionClass(const char* name) + : ExtensionClassBase(name) +{ + ClassRegistry::register_class(this); +} + +template +ExtensionClass::~ExtensionClass() +{ + ClassRegistry::unregister_class(this); +} + +template +inline void ClassRegistry::register_class(Class* p) +{ + // You're not expected to create more than one of these! + assert(static_class_object == 0); + static_class_object = p; +} + +template +inline void ClassRegistry::unregister_class(Class* p) +{ + // The user should be destroying the same object they created. + assert(static_class_object == p); + (void)p; // unused in shipping version + static_class_object = 0; +} + +// +// Static data member declaration. +// +template +Class* ClassRegistry::static_class_object; + +} // namespace py + +#endif // EXTENSION_CLASS_DWA052000_H_ """) if __name__ == '__main__':