mirror of
https://github.com/boostorg/python.git
synced 2026-01-21 05:02:17 +00:00
383 lines
9.9 KiB
C++
383 lines
9.9 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
|
|
|
|
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;
|
|
}
|
|
|
|
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");
|
|
|
|
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");
|
|
}
|
|
|
|
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
|