2
0
mirror of https://github.com/boostorg/python.git synced 2026-01-20 16:52:15 +00:00
Files
python/extclass_demo.cpp
nobody 6b0144ef31 This commit was manufactured by cvs2svn to create branch
'coercion_experiments'.

[SVN r8021]
2000-10-20 02:25:42 +00:00

696 lines
19 KiB
C++

// (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_demo.h"
#include "class_wrapper.h"
#include <stdio.h> // used for portability on broken compilers
#include <boost/rational.hpp>
namespace extclass_demo {
FooCallback::FooCallback(PyObject* self, int x)
: Foo(x), m_self(self)
{
}
int FooCallback::add_len(const char* x) const
{
// Try to call the "add_len" method on the corresponding Python object.
return py::Callback<int>::call_method(m_self, "add_len", x);
}
// A function which Python can call in case bar is not overridden from
// Python. In true Python style, we use a free function taking an initial self
// parameter. This function anywhere needn't be a static member of the callback
// class. The only reason to do it this way is that Foo::add_len is private, and
// FooCallback is already a friend of Foo.
int FooCallback::default_add_len(const Foo* self, const char* x)
{
// Don't forget the Foo:: qualification, or you'll get an infinite
// recursion!
return self->Foo::add_len(x);
}
// Since Foo::pure() is pure virtual, we don't need a corresponding
// default_pure(). A failure to override it in Python will result in an
// exception at runtime when pure() is called.
const char* FooCallback::pure() const
{
return py::Callback<const char*>::call_method(m_self, "pure");
}
// The initializer for ExtensionClass<Foo,FooCallback> is entirely optional. It
// only affects the way that instances of this class _print_ in Python. If you
// need an absolutely predictable name for the type, use the
// initializer. Otherwise, C++ will generate an implementation-dependent
// representation of the type name, usually something like "class
// extclass_demo::Foo". I've supplied it here in part so that I can write
// doctests that exactly anticipate the generated error messages.
Foo::PythonClass::PythonClass()
: py::ExtensionClass<Foo,FooCallback>("Foo") // optional
{
def(py::Constructor<int>());
def(&Foo::mumble, "mumble");
def(&Foo::set, "set");
def(&Foo::call_pure, "call_pure");
def(&Foo::call_add_len, "call_add_len");
// This is the way we add a virtual function that has a default implementation.
def(&Foo::add_len, "add_len", &FooCallback::default_add_len);
// Since pure() is pure virtual, we are leaving it undefined.
}
BarPythonClass::BarPythonClass()
: py::ExtensionClass<Bar>("Bar") // optional
{
def(py::Constructor<int, int>());
def(&Bar::first, "first");
def(&Bar::second, "second");
def(&Bar::pass_baz, "pass_baz");
}
BazPythonClass::BazPythonClass()
: py::ExtensionClass<Baz>("Baz") // optional
{
def(py::Constructor<py::Void>());
def(&Baz::pass_bar, "pass_bar");
def(&Baz::clone, "clone");
def(&Baz::create_foo, "create_foo");
def(&Baz::get_foo_value, "get_foo_value");
def(&Baz::eat_baz, "eat_baz");
}
StringMapPythonClass::StringMapPythonClass()
: py::ExtensionClass<StringMap >("StringMap")
{
def(py::Constructor<py::Void>());
def(&StringMap::size, "__len__");
def(&get_item, "__getitem__");
def(&set_item, "__setitem__");
def(&del_item, "__delitem__");
}
int get_first(const IntPair& p)
{
return p.first;
}
void set_first(IntPair& p, int value)
{
p.first = -value;
}
void del_first(const IntPair&)
{
PyErr_SetString(PyExc_AttributeError, "first can't be deleted!");
throw py::ErrorAlreadySet();
}
IntPairPythonClass::IntPairPythonClass()
: py::ExtensionClass<IntPair>("IntPair")
{
def(py::Constructor<int, int>());
def(&getattr, "__getattr__");
def(&setattr, "__setattr__");
def(&delattr, "__delattr__");
def(&get_first, "__getattr__first__");
def(&set_first, "__setattr__first__");
def(&del_first, "__delattr__first__");
}
void IntPairPythonClass::setattr(IntPair& x, const std::string& name, int value)
{
if (name == "second")
{
x.second = value;
}
else
{
PyErr_SetString(PyExc_AttributeError, name.c_str());
throw py::ErrorAlreadySet();
}
}
void IntPairPythonClass::delattr(IntPair&, const char*)
{
PyErr_SetString(PyExc_AttributeError, "Attributes can't be deleted!");
throw py::ErrorAlreadySet();
}
int IntPairPythonClass::getattr(const IntPair& p, const std::string& s)
{
if (s == "second")
{
return p.second;
}
else
{
PyErr_SetString(PyExc_AttributeError, s.c_str());
throw py::ErrorAlreadySet();
}
#if defined(__MWERKS__) && __MWERKS__ <= 0x6000
return 0;
#endif
}
namespace { namespace file_local {
void throw_key_error_if_end(const StringMap& m, StringMap::const_iterator p, std::size_t key)
{
if (p == m.end())
{
PyErr_SetObject(PyExc_KeyError, py::converters::to_python(key));
throw py::ErrorAlreadySet();
}
}
}} // namespace <anonymous>::file_local
const std::string& StringMapPythonClass::get_item(const StringMap& m, std::size_t key)
{
const StringMap::const_iterator p = m.find(key);
file_local::throw_key_error_if_end(m, p, key);
return p->second;
}
void StringMapPythonClass::set_item(StringMap& m, std::size_t key, const std::string& value)
{
m[key] = value;
}
void StringMapPythonClass::del_item(StringMap& m, std::size_t key)
{
const StringMap::iterator p = m.find(key);
file_local::throw_key_error_if_end(m, p, key);
m.erase(p);
}
//
// Show that polymorphism can work. a DerivedFromFoo object will be passed to
// Python in a smart pointer object.
//
class DerivedFromFoo : public Foo
{
public:
DerivedFromFoo(int x) : Foo(x) {}
private:
const char* pure() const
{ return "this was never pure!"; }
int add_len(const char*) const
{ return 1000; }
};
//
// function implementations
//
IntPair make_pair(int x, int y)
{
return std::make_pair(x, y);
}
const char* Foo::mumble()
{
return "mumble";
}
void Foo::set(long x)
{
m_x = x;
}
const char* Foo::call_pure()
{
return this->pure();
}
int Foo::call_add_len(const char* s) const
{
return this->add_len(s);
}
int Foo::add_len(const char* s) const // sum the held value and the length of s
{
return PY_CSTD_::strlen(s) + static_cast<int>(m_x);
}
boost::shared_ptr<Foo> Baz::create_foo()
{
return boost::shared_ptr<Foo>(new DerivedFromFoo(0));
}
// We can accept smart pointer parameters
int Baz::get_foo_value(boost::shared_ptr<Foo> foo)
{
return foo->call_add_len("");
}
// Show what happens in python when we take ownership from an auto_ptr
void Baz::eat_baz(std::auto_ptr<Baz> baz)
{
baz->clone(); // just do something to show that it is valid.
}
Baz Bar::pass_baz(Baz b)
{
return b;
}
std::string stringpair_repr(const StringPair& sp)
{
return "('" + sp.first + "', '" + sp.second + "')";
}
int stringpair_compare(const StringPair& sp1, const StringPair& sp2)
{
return sp1 < sp2 ? -1 : sp2 < sp1 ? 1 : 0;
}
py::String range_str(const Range& r)
{
char buf[200];
sprintf(buf, "(%d, %d)", r.m_start, r.m_finish);
return py::String(buf);
}
int range_compare(const Range& r1, const Range& r2)
{
int d = r1.m_start - r2.m_start;
if (d == 0)
d = r1.m_finish - r2.m_finish;
return d;
}
long range_hash(const Range& r)
{
return r.m_start * 123 + r.m_finish;
}
/************************************************************/
/* */
/* some functions to test overloading */
/* */
/************************************************************/
static std::string testVoid()
{
return std::string("Hello world!");
}
static int testInt(int i)
{
return i;
}
static std::string testString(std::string i)
{
return i;
}
static int test2(int i1, int i2)
{
return i1+i2;
}
static int test3(int i1, int i2, int i3)
{
return i1+i2+i3;
}
static int test4(int i1, int i2, int i3, int i4)
{
return i1+i2+i3+i4;
}
static int test5(int i1, int i2, int i3, int i4, int i5)
{
return i1+i2+i3+i4+i5;
}
/************************************************************/
/* */
/* a class to test overloading */
/* */
/************************************************************/
struct OverloadTest
{
OverloadTest(): x_(1000) {}
OverloadTest(int x): x_(x) {}
OverloadTest(int x,int y): x_(x+y) { }
OverloadTest(int x,int y,int z): x_(x+y+z) {}
OverloadTest(int x,int y,int z, int a): x_(x+y+z+a) {}
OverloadTest(int x,int y,int z, int a, int b): x_(x+y+z+a+b) {}
int x() const { return x_; }
void setX(int x) { x_ = x; }
int p1(int x) { return x; }
int p2(int x, int y) { return x + y; }
int p3(int x, int y, int z) { return x + y + z; }
int p4(int x, int y, int z, int a) { return x + y + z + a; }
int p5(int x, int y, int z, int a, int b) { return x + y + z + a + b; }
private:
int x_;
};
static int getX(OverloadTest * u)
{
return u->x();
}
/************************************************************/
/* */
/* classes to test base declarations and conversions */
/* */
/************************************************************/
struct Dummy
{
virtual ~Dummy() {}
int dummy_;
};
struct Base
{
virtual int x() const { return 999; };
virtual ~Base() {}
};
// 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();
}
/************************************************************/
/* */
/* test classes for interaction of overloading, */
/* base declarations, and callbacks */
/* */
/************************************************************/
struct CallbackTestBase
{
virtual int testCallback(int i) { return callback(i); }
virtual int callback(int i) = 0;
virtual ~CallbackTestBase() {}
};
struct CallbackTest : public CallbackTestBase
{
virtual int callback(int i) { return i + 1; }
virtual std::string callbackString(std::string const & i) { return i + " 1"; }
};
struct CallbackTestCallback : public CallbackTest
{
CallbackTestCallback(PyObject* self)
: m_self(self)
{}
int callback(int x)
{
return py::Callback<int>::call_method(m_self, "callback", x);
}
std::string callbackString(std::string const & x)
{
return py::Callback<std::string>::call_method(m_self, "callback", x);
}
static int default_callback(CallbackTest * self, int x)
{
return self->CallbackTest::callback(x);
}
static std::string default_callbackString(CallbackTest * self, std::string x)
{
return self->CallbackTest::callbackString(x);
}
PyObject * m_self;
};
int testCallback(CallbackTestBase * b, int i)
{
return b->testCallback(i);
}
typedef boost::rational<int> Ratio;
py::String ratio_str(const Ratio& r)
{
char buf[200];
if (r.denominator() == 1)
sprintf(buf, "%d", r.numerator());
else
sprintf(buf, "%d/%d", r.numerator(), r.denominator());
return py::String(buf);
}
py::String ratio_repr(const Ratio& r)
{
char buf[200];
sprintf(buf, "Rational(%d, %d)", r.numerator(), r.denominator());
return py::String(buf);
}
py::Tuple ratio_coerce(const Ratio& r1, int r2)
{
return py::Tuple(r1, Ratio(r2));
}
// The most reliable way, across compilers, to grab the particular abs function
// we're interested in.
Ratio ratio_abs(const Ratio& r)
{
return boost::abs(r);
}
// An experiment, to be integrated into the py_cpp library at some point.
template <class T>
struct StandardOps
{
static T add(const T& x, const T& y) { return x + y; }
static T sub(const T& x, const T& y) { return x - y; }
static T mul(const T& x, const T& y) { return x * y; }
static T div(const T& x, const T& y) { return x / y; }
static T cmp(const T& x, const T& y) { return std::less<T>()(x, y) ? -1 : std::less<T>()(y, x) ? 1 : 0; }
};
/************************************************************/
/* */
/* init the module */
/* */
/************************************************************/
void init_module(py::Module& m)
{
m.add(new Foo::PythonClass);
m.add(new BarPythonClass);
m.add(new BazPythonClass);
m.add(new StringMapPythonClass);
m.add(new IntPairPythonClass);
m.def(make_pair, "make_pair");
m.add(new CompareIntPairPythonClass);
py::ClassWrapper<StringPair> string_pair(m, "StringPair");
string_pair.def(py::Constructor<std::string, std::string>());
string_pair.def_readonly(&StringPair::first, "first");
string_pair.def_read_write(&StringPair::second, "second");
string_pair.def(&stringpair_repr, "__repr__");
string_pair.def(&stringpair_compare, "__cmp__");
m.def(first_string, "first_string");
m.def(second_string, "second_string");
// This shows the wrapping of a 3rd-party numeric type.
py::ClassWrapper<boost::rational<int> > rational(m, "Rational");
rational.def(py::Constructor<int, int>());
rational.def(py::Constructor<int>());
rational.def(py::Constructor<>());
rational.def(StandardOps<Ratio>::add, "__add__");
rational.def(StandardOps<Ratio>::sub, "__sub__");
rational.def(StandardOps<Ratio>::mul, "__mul__");
rational.def(StandardOps<Ratio>::div, "__div__");
rational.def(StandardOps<Ratio>::cmp, "__cmp__");
rational.def(ratio_coerce, "__coerce__");
rational.def(ratio_str, "__str__");
rational.def(ratio_repr, "__repr__");
rational.def(ratio_abs, "__abs__");
py::ClassWrapper<Range> range(m, "Range");
range.def(py::Constructor<int>());
range.def(py::Constructor<int, int>());
range.def((void (Range::*)(std::size_t))&Range::length, "__len__");
range.def((std::size_t (Range::*)() const)&Range::length, "__len__");
range.def(&Range::operator[], "__getitem__");
range.def(&Range::slice, "__getslice__");
range.def(&range_str, "__str__");
range.def(&range_compare, "__cmp__");
range.def(&range_hash, "__hash__");
range.def_readonly(&Range::m_start, "start");
range.def_readonly(&Range::m_finish, "finish");
m.def(&testVoid, "overloaded");
m.def(&testInt, "overloaded");
m.def(&testString, "overloaded");
m.def(&test2, "overloaded");
m.def(&test3, "overloaded");
m.def(&test4, "overloaded");
m.def(&test5, "overloaded");
py::ClassWrapper<OverloadTest> over(m, "OverloadTest");
over.def(py::Constructor<py::Void>());
over.def(py::Constructor<OverloadTest const &>());
over.def(py::Constructor<int>());
over.def(py::Constructor<int, int>());
over.def(py::Constructor<int, int, int>());
over.def(py::Constructor<int, int, int, int>());
over.def(py::Constructor<int, int, int, int, int>());
over.def(&getX, "getX");
over.def(&OverloadTest::setX, "setX");
over.def(&OverloadTest::x, "overloaded");
over.def(&OverloadTest::p1, "overloaded");
over.def(&OverloadTest::p2, "overloaded");
over.def(&OverloadTest::p3, "overloaded");
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");
py::ClassWrapper<CallbackTestBase> callbackTestBase(m, "CallbackTestBase");
callbackTestBase.def(&CallbackTestBase::testCallback, "testCallback");
m.def(&testCallback, "testCallback");
py::ClassWrapper<CallbackTest, CallbackTestCallback> callbackTest(m, "CallbackTest");
callbackTest.def(py::Constructor<py::Void>());
callbackTest.def(&CallbackTest::callback, "callback",
&CallbackTestCallback::default_callback);
callbackTest.def(&CallbackTest::callbackString, "callback",
&CallbackTestCallback::default_callbackString);
callbackTest.declare_base(callbackTestBase);
}
void init_module()
{
py::Module demo("demo");
init_module(demo);
// Just for giggles, add a raw metaclass.
demo.add(new py::MetaClass<py::Instance>);
}
extern "C"
#ifdef _WIN32
__declspec(dllexport)
#endif
void initdemo()
{
try {
extclass_demo::init_module();
}
catch(...) {
py::handle_exception();
} // Need a way to report other errors here
}
CompareIntPairPythonClass::CompareIntPairPythonClass()
: py::ExtensionClass<CompareIntPair>("CompareIntPair")
{
def(py::Constructor<py::Void>());
def(&CompareIntPair::operator(), "__call__");
}
} // namespace extclass_demo
#if defined(_WIN32)
# include <windows.h>
extern "C" BOOL WINAPI DllMain ( HINSTANCE hInst, DWORD wDataSeg, LPVOID lpvReserved );
# ifdef PY_COMPILER_IS_MSVC
extern "C" void structured_exception_translator(unsigned int, EXCEPTION_POINTERS*)
{
throw;
}
# endif
BOOL WINAPI DllMain(
HINSTANCE, //hDllInst
DWORD fdwReason,
LPVOID // lpvReserved
)
{
# ifdef PY_COMPILER_IS_MSVC
_set_se_translator(structured_exception_translator);
#endif
return 1;
(void)fdwReason; // warning suppression.
}
#endif // _WIN32