2
0
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:
Ullrich Köthe
2000-10-17 14:50:07 +00:00
parent f6f8a3d5d0
commit 26bf57c4e9
6 changed files with 323 additions and 27 deletions

View File

@@ -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;
};

View File

@@ -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);

View File

@@ -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

View File

@@ -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()

View File

@@ -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;

View File

@@ -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 *