// (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. #ifndef BPL_TEST_DWA052200_H_ # define BPL_TEST_DWA052200_H_ // // Example code demonstrating extension class usage // # include # include # include # include # include # include # include # include namespace bpl_test { // // example: Foo, Bar, and Baz are C++ classes we want to wrap. // class Foo // prohibit copying, proving that it doesn't choke : boost::noncopyable // our generation of to_python(). { public: // constructor/destructor Foo(int x) : m_x(x) {} virtual ~Foo() {} public: // non-virtual functions const char* mumble(); // mumble something void set(long x); // change the held value // These two call virtual functions std::string call_pure(); // call a pure virtual fuction int call_add_len(const char* s) const; // virtual function with a default implementation private: // by default, sum the held value and the length of s virtual int add_len(const char* s) const; // Derived classes can do whatever they want here, but they must do something! virtual std::string pure() const = 0; public: // friend declarations // If you have private virtual functions such as add_len which you want to // override in Python and have default implementations, they must be // accessible by the thing making the def() call on the extension_class (in // this case, the nested PythonClass itself), and by the C++ derived class // which is used to cause the Python callbacks (in this case, // FooCallback). See the definition of FooCallback::add_len() struct PythonClass; friend struct PythonClass; friend class FooCallback; private: int m_x; // the held value }; // // Bar and Baz have mutually-recursive type conversion dependencies (see // pass_xxx functions). I've done this to prove that it doesn't cause a // problem for Python class definitions, which happen later. // // Bar and Baz functions are only virtual to increase the likelihood of a crash // if I inadvertently use a pointer to garbage memory (a likely thing to test // for considering the amount of type casting needed to translate to and from // Python). struct Baz; struct Bar { Bar(int x, int y) : m_first(x), m_second(y) {} virtual int first() const { return m_first; } virtual int second() const { return m_second; } virtual Baz pass_baz(Baz x); int m_first, m_second; }; struct Baz { virtual Bar pass_bar(const Bar& x) { return x; } // We can return smart pointers virtual std::auto_ptr clone() { return std::auto_ptr(new Baz(*this)); } // This illustrates creating a polymorphic derived class of Foo virtual boost::shared_ptr create_foo(); // We can accept smart pointer parameters virtual int get_foo_value(boost::shared_ptr); // Show what happens in python when we take ownership from an auto_ptr virtual void eat_baz(std::auto_ptr); }; typedef std::map StringMap; typedef std::pair IntPair; IntPair make_pair(int, int); typedef std::less CompareIntPair; typedef std::pair StringPair; inline std::string first_string(const StringPair& x) { return x.first; } inline std::string second_string(const StringPair& x) { return x.second; } struct Range { Range(int x) : m_start(x), m_finish(x) {} Range(int start, int finish) : m_start(start), m_finish(finish) {} std::size_t length() const { return m_finish < m_start ? 0 : m_finish - m_start; } void length(std::size_t new_length) { m_finish = m_start + new_length; } int operator[](std::size_t n) { return m_start + n; } Range slice(std::size_t start, std::size_t end) { if (start > length()) start = length(); if (end > length()) end = length(); return Range(m_start + start, m_start + end); } int m_start, m_finish; }; //////////////////////////////////////////////////////////////////////// // // // Begin wrapping code. Usually this would live in a separate header. // // // //////////////////////////////////////////////////////////////////////// // Since Foo has virtual functions which we want overriden in Python, we must // derive FooCallback. class FooCallback : public Foo { public: // Note the additional constructor parameter "self", which is needed to // allow function overriding from Python. FooCallback(PyObject* self, int x); friend struct PythonClass; // give it access to the functions below private: // implementations of Foo virtual functions that are overridable in python. int add_len(const char* x) const; // 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. You can put this function anywhere; it needn't be a // static member of the wrapping class. static int default_add_len(const Foo* self, const char* 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. std::string pure() const; private: // Required boilerplate if functions will be overridden PyObject* m_self; // No, we don't want a boost::python::ref here, or we'd get an ownership cycle. }; // Define the Python base class struct Foo::PythonClass : boost::python::class_builder { PythonClass(boost::python::module_builder&); }; // No virtual functions on Bar or Baz which are actually supposed to behave // virtually from C++, so we'll rely on the library to define a wrapper for // us. Even so, Python class_t types for each type we're wrapping should be // _defined_ here in a header where they can be seen by other extension class // definitions, since it is the definition of the boost::python::class_builder<> that // causes to_python/from_python conversion functions to be generated. struct BarPythonClass : boost::python::class_builder { BarPythonClass(boost::python::module_builder&); }; struct BazPythonClass : boost::python::class_builder { BazPythonClass(boost::python::module_builder&); }; struct StringMapPythonClass : boost::python::class_builder { StringMapPythonClass(boost::python::module_builder&); // These static functions implement the right argument protocols for // implementing the Python "special member functions" for mapping on // StringMap. Could just as easily be global functions. static const std::string& get_item(const StringMap& m, std::size_t key); static void set_item(StringMap& m, std::size_t key, const std::string& value); static void del_item(StringMap& m, std::size_t key); }; struct IntPairPythonClass : boost::python::class_builder { IntPairPythonClass(boost::python::module_builder&); // The following could just as well be a free function; it implements the // getattr functionality for IntPair. static int getattr(const IntPair&, const std::string& s); static void setattr(IntPair&, const std::string& name, int value); static void delattr(IntPair&, const char* name); }; struct CompareIntPairPythonClass : boost::python::class_builder { CompareIntPairPythonClass(boost::python::module_builder&); }; } // namespace bpl_test #endif // BPL_TEST_DWA052200_H_