mirror of
https://github.com/boostorg/python.git
synced 2026-01-22 05:22:45 +00:00
added ExtensionClass<...>::declare_base(...) which
- declares a wrapped C++ class to be a base class of another wrapped class - enables conversions between those classes - ensures that base class methods are inherited by the subclass [SVN r7971]
This commit is contained in:
@@ -65,7 +65,38 @@ class ClassWrapper
|
||||
template <class MemberType>
|
||||
void def_read_write(MemberType T::*pm, const char* name)
|
||||
{ m_class->def_read_write(pm, name); }
|
||||
private:
|
||||
|
||||
// declare the given class a base class of this and register
|
||||
// conversion functions
|
||||
template <class S, class V>
|
||||
void declare_base(ClassWrapper<S, V> const & base)
|
||||
{
|
||||
m_class->declare_base(base.m_class.get());
|
||||
}
|
||||
|
||||
// declare the given class a base class of this and register
|
||||
// conversion functions
|
||||
template <class S, class V>
|
||||
void declare_base(ClassWrapper<S, V> const & base, WithoutDowncast)
|
||||
{
|
||||
m_class->declare_base(base.m_class.get(), without_downcast);
|
||||
}
|
||||
|
||||
template <class S, class V>
|
||||
void declare_base(ExtensionClass<S, V> * base)
|
||||
{
|
||||
m_class->declare_base(base);
|
||||
}
|
||||
|
||||
// declare the given class a base class of this and register
|
||||
// only upcast function
|
||||
template <class S, class V>
|
||||
void declare_base(ExtensionClass<S, V> * base, WithoutDowncast)
|
||||
{
|
||||
m_class->declare_base(base, without_downcast);
|
||||
}
|
||||
|
||||
// private:
|
||||
PyPtr<ExtensionClass<T, U> > m_class;
|
||||
};
|
||||
|
||||
|
||||
37
extclass.cpp
37
extclass.cpp
@@ -157,7 +157,7 @@ void report_missing_instance_data(
|
||||
}
|
||||
else
|
||||
{
|
||||
two_string_error(PyExc_TypeError, "extension class '%.*s' is not derived from '%.*s'.",
|
||||
two_string_error(PyExc_TypeError, "extension class '%.*s' is not convertible into '%.*s'.",
|
||||
instance->ob_type->tp_name, target_class->tp_name);
|
||||
}
|
||||
}
|
||||
@@ -218,6 +218,41 @@ ExtensionClassBase::ExtensionClassBase(const char* name)
|
||||
{
|
||||
}
|
||||
|
||||
void * ExtensionClassBase::try_class_conversions(InstanceHolderBase * object) const
|
||||
{
|
||||
void * result = try_sub_class_conversions(object);
|
||||
if(result) return result;
|
||||
result = try_super_class_conversions(object);
|
||||
return result;
|
||||
}
|
||||
|
||||
void * ExtensionClassBase::try_super_class_conversions(InstanceHolderBase * object) const
|
||||
{
|
||||
void * result = 0;
|
||||
for(int i=0; i<base_classes().size(); ++i)
|
||||
{
|
||||
if(base_classes()[i].second == 0) continue;
|
||||
result = base_classes()[i].first->convert_from_holder(object);
|
||||
if(result) return (*base_classes()[i].second)(result);
|
||||
result = base_classes()[i].first->try_super_class_conversions(object);
|
||||
if(result) return (*base_classes()[i].second)(result);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void * ExtensionClassBase::try_sub_class_conversions(InstanceHolderBase * object) const
|
||||
{
|
||||
void * result = 0;
|
||||
for(int i=0; i<sub_classes().size(); ++i)
|
||||
{
|
||||
result = sub_classes()[i].first->convert_from_holder(object);
|
||||
if(result) return (*sub_classes()[i].second)(result);
|
||||
result = sub_classes()[i].first->try_sub_class_conversions(object);
|
||||
if(result) return (*sub_classes()[i].second)(result);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void ExtensionClassBase::add_method(Function* method, const char* name)
|
||||
{
|
||||
add_method(PyPtr<Function>(method), name);
|
||||
|
||||
118
extclass.h
118
extclass.h
@@ -52,6 +52,9 @@ T* check_non_null(T* p)
|
||||
}
|
||||
|
||||
template <class T> class HeldInstance;
|
||||
typedef void * (*ConversionFct)(void *);
|
||||
typedef std::pair<TypeObjectBase *, ConversionFct> BaseClassInfo;
|
||||
typedef std::pair<TypeObjectBase *, ConversionFct> SubClassInfo;
|
||||
|
||||
class ExtensionClassBase : public Class<ExtensionInstance>
|
||||
{
|
||||
@@ -66,18 +69,30 @@ class ExtensionClassBase : public Class<ExtensionInstance>
|
||||
void add_constructor_object(Function*);
|
||||
void add_setter_method(Function*, const char* name);
|
||||
void add_getter_method(Function*, const char* name);
|
||||
|
||||
virtual void * try_class_conversions(InstanceHolderBase*) const;
|
||||
virtual void * try_super_class_conversions(InstanceHolderBase*) const;
|
||||
virtual void * try_sub_class_conversions(InstanceHolderBase*) const;
|
||||
virtual std::vector<BaseClassInfo> const & base_classes() const = 0;
|
||||
virtual std::vector<SubClassInfo> const & sub_classes() const = 0;
|
||||
};
|
||||
|
||||
template <class T>
|
||||
class ClassRegistry
|
||||
{
|
||||
public:
|
||||
public:
|
||||
static Class<ExtensionInstance>* class_object()
|
||||
{ return static_class_object; }
|
||||
static void register_class(py::Class<py::ExtensionInstance>*);
|
||||
static void unregister_class(py::Class<py::ExtensionInstance>*);
|
||||
static void register_base_class(BaseClassInfo const &);
|
||||
static void register_sub_class(SubClassInfo const &);
|
||||
static std::vector<BaseClassInfo> const & base_classes();
|
||||
static std::vector<SubClassInfo> const & sub_classes();
|
||||
private:
|
||||
static py::Class<py::ExtensionInstance>* static_class_object;
|
||||
static std::vector<BaseClassInfo> static_base_class_info;
|
||||
static std::vector<SubClassInfo> static_sub_class_info;
|
||||
};
|
||||
|
||||
#ifdef PY_NO_INLINE_FRIENDS_IN_NAMESPACE // back to global namespace for this GCC bug
|
||||
@@ -123,6 +138,10 @@ class PyExtensionClassConverters
|
||||
py::InstanceHolder<T>* held = dynamic_cast<py::InstanceHolder<T>*>(*p);
|
||||
if (held != 0)
|
||||
return held->target();
|
||||
|
||||
void * target = py::ClassRegistry<T>::class_object()->try_class_conversions(*p);
|
||||
if(target)
|
||||
return static_cast<T *>(target);
|
||||
}
|
||||
py::report_missing_instance_data(self, py::ClassRegistry<T>::class_object(), typeid(T));
|
||||
throw py::ArgumentError();
|
||||
@@ -250,6 +269,23 @@ class ReadOnlySetattrFunction : public Function
|
||||
String m_name;
|
||||
};
|
||||
|
||||
template <class From, class To>
|
||||
struct ConversionFunction
|
||||
{
|
||||
static void * upcast_ptr(void * v)
|
||||
{
|
||||
return static_cast<To *>(static_cast<From *>(v));
|
||||
}
|
||||
|
||||
static void * downcast_ptr(void * v)
|
||||
{
|
||||
return dynamic_cast<To *>(static_cast<From *>(v));
|
||||
}
|
||||
};
|
||||
|
||||
enum WithoutDowncast { without_downcast };
|
||||
|
||||
|
||||
// An easy way to make an extension base class which wraps T. Note that Python
|
||||
// subclasses of this class will simply be Class<ExtensionInstance> objects.
|
||||
//
|
||||
@@ -344,10 +380,40 @@ class ExtensionClass
|
||||
this->def_getter(pm, name);
|
||||
this->def_setter(pm, name);
|
||||
}
|
||||
|
||||
// declare the given class a base class of this and register
|
||||
// conversion functions
|
||||
template <class S, class V>
|
||||
void declare_base(ExtensionClass<S, V> * base)
|
||||
{
|
||||
BaseClassInfo baseInfo(base, &ConversionFunction<S, T>::downcast_ptr);
|
||||
ClassRegistry<T>::register_base_class(baseInfo);
|
||||
add_base(Ptr(as_object(base)));
|
||||
|
||||
SubClassInfo subInfo(this, &ConversionFunction<T, S>::upcast_ptr);
|
||||
ClassRegistry<S>::register_sub_class(subInfo);
|
||||
}
|
||||
|
||||
// declare the given class a base class of this and register
|
||||
// only upcast function
|
||||
template <class S, class V>
|
||||
void declare_base(ExtensionClass<S, V> * base, WithoutDowncast)
|
||||
{
|
||||
BaseClassInfo baseInfo(base, 0);
|
||||
ClassRegistry<T>::register_base_class(baseInfo);
|
||||
add_base(Ptr(as_object(base)));
|
||||
|
||||
SubClassInfo subInfo(this, &ConversionFunction<T, S>::upcast_ptr);
|
||||
ClassRegistry<S>::register_sub_class(subInfo);
|
||||
}
|
||||
|
||||
private:
|
||||
typedef InstanceValueHolder<T,U> Holder;
|
||||
|
||||
virtual std::vector<BaseClassInfo> const & base_classes() const;
|
||||
virtual std::vector<SubClassInfo> const & sub_classes() const;
|
||||
virtual void * convert_from_holder(InstanceHolderBase * v) const;
|
||||
|
||||
template <class Signature>
|
||||
void add_constructor(Signature sig)
|
||||
{
|
||||
@@ -464,6 +530,28 @@ ExtensionClass<T, U>::ExtensionClass(const char* name)
|
||||
ClassRegistry<T>::register_class(this);
|
||||
}
|
||||
|
||||
template <class T, class U>
|
||||
inline
|
||||
std::vector<BaseClassInfo> const & ExtensionClass<T, U>::base_classes() const
|
||||
{
|
||||
return ClassRegistry<T>::base_classes();
|
||||
}
|
||||
|
||||
template <class T, class U>
|
||||
inline
|
||||
std::vector<SubClassInfo> const & ExtensionClass<T, U>::sub_classes() const
|
||||
{
|
||||
return ClassRegistry<T>::sub_classes();
|
||||
}
|
||||
|
||||
template <class T, class U>
|
||||
void * ExtensionClass<T, U>::convert_from_holder(InstanceHolderBase * v) const
|
||||
{
|
||||
py::InstanceHolder<T>* held = dynamic_cast<py::InstanceHolder<T>*>(v);
|
||||
if(held) return held->target();
|
||||
return 0;
|
||||
}
|
||||
|
||||
template <class T, class U>
|
||||
ExtensionClass<T, U>::~ExtensionClass()
|
||||
{
|
||||
@@ -487,11 +575,39 @@ inline void ClassRegistry<T>::unregister_class(Class<ExtensionInstance>* p)
|
||||
static_class_object = 0;
|
||||
}
|
||||
|
||||
template <class T>
|
||||
void ClassRegistry<T>::register_base_class(BaseClassInfo const & i)
|
||||
{
|
||||
static_base_class_info.push_back(i);
|
||||
}
|
||||
|
||||
template <class T>
|
||||
void ClassRegistry<T>::register_sub_class(SubClassInfo const & i)
|
||||
{
|
||||
static_sub_class_info.push_back(i);
|
||||
}
|
||||
|
||||
template <class T>
|
||||
std::vector<BaseClassInfo> const & ClassRegistry<T>::base_classes()
|
||||
{
|
||||
return static_base_class_info;
|
||||
}
|
||||
|
||||
template <class T>
|
||||
std::vector<SubClassInfo> const & ClassRegistry<T>::sub_classes()
|
||||
{
|
||||
return static_sub_class_info;
|
||||
}
|
||||
|
||||
//
|
||||
// Static data member declaration.
|
||||
//
|
||||
template <class T>
|
||||
Class<py::ExtensionInstance>* ClassRegistry<T>::static_class_object;
|
||||
template <class T>
|
||||
std::vector<BaseClassInfo> ClassRegistry<T>::static_base_class_info;
|
||||
template <class T>
|
||||
std::vector<SubClassInfo> ClassRegistry<T>::static_sub_class_info;
|
||||
|
||||
} // namespace py
|
||||
|
||||
|
||||
@@ -363,6 +363,68 @@ static int getX(OverloadTest * u)
|
||||
return u->x();
|
||||
}
|
||||
|
||||
|
||||
/************************************************************/
|
||||
/* */
|
||||
/* classes to test base declarations snd conversions */
|
||||
/* */
|
||||
/************************************************************/
|
||||
|
||||
struct Dummy
|
||||
{
|
||||
int dummy_;
|
||||
};
|
||||
|
||||
struct Base
|
||||
{
|
||||
virtual int x() const { return 999; };
|
||||
};
|
||||
|
||||
// inherit Dummy so that the Base part of Concrete starts at an offset
|
||||
// otherwise, typecast tests wouldn't be very meaningful
|
||||
struct Derived1 : public Dummy, public Base
|
||||
{
|
||||
Derived1(int x): x_(x) {}
|
||||
virtual int x() const { return x_; }
|
||||
|
||||
private:
|
||||
int x_;
|
||||
};
|
||||
|
||||
struct Derived2 : public Dummy, public Base
|
||||
{
|
||||
Derived2(int x): x_(x) {}
|
||||
virtual int x() const { return x_; }
|
||||
|
||||
private:
|
||||
int x_;
|
||||
};
|
||||
|
||||
static int testUpcast(Base * b)
|
||||
{
|
||||
return b->x();
|
||||
}
|
||||
|
||||
static std::auto_ptr<Base> derived1Factory(int i)
|
||||
{
|
||||
return std::auto_ptr<Base>(new Derived1(i));
|
||||
}
|
||||
|
||||
static std::auto_ptr<Base> derived2Factory(int i)
|
||||
{
|
||||
return std::auto_ptr<Base>(new Derived2(i));
|
||||
}
|
||||
|
||||
static int testDowncast1(Derived1 * d)
|
||||
{
|
||||
return d->x();
|
||||
}
|
||||
|
||||
static int testDowncast2(Derived2 * d)
|
||||
{
|
||||
return d->x();
|
||||
}
|
||||
|
||||
/************************************************************/
|
||||
/* */
|
||||
/* init the module */
|
||||
@@ -426,6 +488,25 @@ void init_module(py::Module& m)
|
||||
over.def(&OverloadTest::p4, "overloaded");
|
||||
over.def(&OverloadTest::p5, "overloaded");
|
||||
|
||||
py::ClassWrapper<Base> base(m, "Base");
|
||||
base.def(&Base::x, "x");
|
||||
|
||||
py::ClassWrapper<Derived1> derived1(m, "Derived1");
|
||||
// this enables conversions between Base and Derived1
|
||||
// and makes wrapped methods of Base available
|
||||
derived1.declare_base(base);
|
||||
derived1.def(py::Constructor<int>());
|
||||
|
||||
py::ClassWrapper<Derived2> derived2(m, "Derived2");
|
||||
// don't enable downcast from Base to Derived2
|
||||
derived2.declare_base(base, py::without_downcast);
|
||||
derived2.def(py::Constructor<int>());
|
||||
|
||||
m.def(&testUpcast, "testUpcast");
|
||||
m.def(&derived1Factory, "derived1Factory");
|
||||
m.def(&derived2Factory, "derived2Factory");
|
||||
m.def(&testDowncast1, "testDowncast1");
|
||||
m.def(&testDowncast2, "testDowncast2");
|
||||
}
|
||||
|
||||
void init_module()
|
||||
|
||||
@@ -30,6 +30,8 @@
|
||||
|
||||
namespace py {
|
||||
|
||||
class InstanceHolderBase;
|
||||
|
||||
class TypeObjectBase : public PythonType
|
||||
{
|
||||
public:
|
||||
@@ -59,6 +61,11 @@ class TypeObjectBase : public PythonType
|
||||
virtual PyObject* instance_getattr(PyObject* instance, const char* name) const;
|
||||
virtual int instance_setattr(PyObject* instance, const char* name, PyObject* value) const;
|
||||
|
||||
virtual void * try_class_conversions(InstanceHolderBase*) const { return 0; }
|
||||
virtual void * try_super_class_conversions(InstanceHolderBase*) const { return 0; }
|
||||
virtual void * try_sub_class_conversions(InstanceHolderBase*) const { return 0; }
|
||||
virtual void * convert_from_holder(InstanceHolderBase*) const { return 0; }
|
||||
|
||||
// Dealloc is a special case, since every type needs a nonzero tp_dealloc slot.
|
||||
virtual void instance_dealloc(PyObject*) const = 0;
|
||||
|
||||
|
||||
@@ -130,7 +130,7 @@ But objects not derived from Bar cannot:
|
||||
>>> baz.pass_bar(baz)
|
||||
Traceback (innermost last):
|
||||
...
|
||||
TypeError: extension class 'Baz' is not derived from 'Bar'.
|
||||
TypeError: extension class 'Baz' is not convertible into 'Bar'.
|
||||
|
||||
The clone function on Baz returns a smart pointer; we wrap it into an
|
||||
ExtensionInstance and make it look just like any other Baz instance.
|
||||
@@ -437,28 +437,28 @@ Testing overloaded free functions
|
||||
|
||||
Testing overloaded constructors
|
||||
|
||||
>>> x = OverloadTest()
|
||||
>>> x.getX()
|
||||
>>> over = OverloadTest()
|
||||
>>> over.getX()
|
||||
1000
|
||||
>>> x = OverloadTest(1)
|
||||
>>> x.getX()
|
||||
>>> over = OverloadTest(1)
|
||||
>>> over.getX()
|
||||
1
|
||||
>>> x = OverloadTest(1,1)
|
||||
>>> x.getX()
|
||||
>>> over = OverloadTest(1,1)
|
||||
>>> over.getX()
|
||||
2
|
||||
>>> x = OverloadTest(1,1,1)
|
||||
>>> x.getX()
|
||||
>>> over = OverloadTest(1,1,1)
|
||||
>>> over.getX()
|
||||
3
|
||||
>>> x = OverloadTest(1,1,1,1)
|
||||
>>> x.getX()
|
||||
>>> over = OverloadTest(1,1,1,1)
|
||||
>>> over.getX()
|
||||
4
|
||||
>>> x = OverloadTest(1,1,1,1,1)
|
||||
>>> x.getX()
|
||||
>>> over = OverloadTest(1,1,1,1,1)
|
||||
>>> over.getX()
|
||||
5
|
||||
>>> x = OverloadTest(x)
|
||||
>>> x.getX()
|
||||
>>> over = OverloadTest(over)
|
||||
>>> over.getX()
|
||||
5
|
||||
>>> try: x = OverloadTest(1, 'foo')
|
||||
>>> try: over = OverloadTest(1, 'foo')
|
||||
... except TypeError, err:
|
||||
... assert re.match("No overloaded functions match \(OverloadTest, int, string\)\. Candidates are:",
|
||||
... str(err))
|
||||
@@ -467,25 +467,51 @@ Testing overloaded constructors
|
||||
|
||||
Testing overloaded methods
|
||||
|
||||
>>> x.setX(3)
|
||||
>>> x.overloaded()
|
||||
>>> over.setX(3)
|
||||
>>> over.overloaded()
|
||||
3
|
||||
>>> x.overloaded(1)
|
||||
>>> over.overloaded(1)
|
||||
1
|
||||
>>> x.overloaded(1,1)
|
||||
>>> over.overloaded(1,1)
|
||||
2
|
||||
>>> x.overloaded(1,1,1)
|
||||
>>> over.overloaded(1,1,1)
|
||||
3
|
||||
>>> x.overloaded(1,1,1,1)
|
||||
>>> over.overloaded(1,1,1,1)
|
||||
4
|
||||
>>> x.overloaded(1,1,1,1,1)
|
||||
>>> over.overloaded(1,1,1,1,1)
|
||||
5
|
||||
>>> try: x.overloaded(1,'foo')
|
||||
>>> try: over.overloaded(1,'foo')
|
||||
... except TypeError, err:
|
||||
... assert re.match("No overloaded functions match \(OverloadTest, int, string\)\. Candidates are:",
|
||||
... str(err))
|
||||
... else:
|
||||
... print 'no exception'
|
||||
|
||||
Testing base class conversions
|
||||
|
||||
>>> testUpcast(over)
|
||||
Traceback (innermost last):
|
||||
TypeError: extension class 'OverloadTest' is not convertible into 'Base'.
|
||||
>>> der1 = Derived1(333)
|
||||
>>> der1.x()
|
||||
333
|
||||
>>> testUpcast(der1)
|
||||
333
|
||||
>>> der1 = derived1Factory(1000)
|
||||
>>> testDowncast1(der1)
|
||||
1000
|
||||
>>> testDowncast2(der1)
|
||||
Traceback (innermost last):
|
||||
TypeError: extension class 'Base' is not convertible into 'Derived2'.
|
||||
>>> der2 = Derived2(444)
|
||||
>>> der2.x()
|
||||
444
|
||||
>>> testUpcast(der2)
|
||||
444
|
||||
>>> der2 = derived2Factory(1111)
|
||||
>>> testDowncast2(der1)
|
||||
Traceback (innermost last):
|
||||
TypeError: extension class 'Base' is not convertible into 'Derived2'.
|
||||
'''
|
||||
|
||||
from demo import *
|
||||
|
||||
Reference in New Issue
Block a user