[C++-SIG] just to introduce me and some comments

Thomas Malik Thomas.Malik at t-online.de
Sat Mar 4 00:53:18 CET 2000


Hello, since i am new to this group, ok, i'm Thomas.
I've taken the CXX package 3 months ago (don't know which version it was)
and changed many things to suit my needs. My requirements were:
- simple, homogeneous look of my wrapper classes, since i had to write a
whole lot of them
- use of inheritance in the wrapper classes, since the class library i had
to incorporate into python, had it, and i didn't want to do mass copies of
source code (every base class method in every subclass)
- the whole thing has to run rock solid, since it's going to be used for
interconnecting stock trading applications in a financial institute.
- I wanted to rely on C++ exceptions as error notification mechanism.

What i did:
I moved the inheritance of the PythonExtension template from PyObject 'one
level up', by introducing an intermediate class
CXXPyObject with a virtual destructor:

// The purpose of this is only to get a virtual destructor into a PyObject
and to
// be able to use dynamic_cast on it. PythonExtension inherits virtually
from this.
class CXXPyObject : public PyObject {
  protected:
    CXXPyObject() {}
  public:
    virtual ~CXXPyObject() {}
};

CXXObject is a virtual base class for PythonExtension.
Now, i can use dynamic_cast even in situations, where i inherit from
PythonExtension multiple times, see below.
Another thing i changed was to eliminate the template argument from
PythonType. It's only purpose was to get the Object size, but you can pass
this easily as a constructor argument, or even pass the size as a template
argument. The downside is, you have to keep PythonExtension as a template
class, since the constructor cannot determine the object size at
construction time.

To do easy, safe casting in the static member functions (Python entry
points) i introduced a template function
T python_cast<class T>(PyObject *). This does first a static_cast to the
CXXObject*, and then a dynamic_cast to the object i'm actually in. Of
course, this does NOT lead me magically into the paths of the right member
functions of the right class, but i'm using dynamic_cast & the virtual
function table for type checking, which gives me a fairly efficient type
safe conversion.

template<class T>
T python_cast(PyObject *p) {
  T result = dynamic_cast<T>(static_cast< CXXPyObject* >(p));
  if (result == NULL) {
    cerr << "!shit! cast failed!" << endl;
    throw bad_cast();
  }
  return result;
}

Lastly, i've added some 'standard' functionality into PythonType &
PythonExtension, such as str(), repr(), getattr().
I strictly obey the following rule in my source:
- Python entry points always have a name starting with 's_' (such as
s_str(PyObject* self, PyObject* a)  )
- the first lines look like
MyClassName* self=python_cast<MyClassName*>(s); // looks damn familiar,
doesn't it ?
Tuple args(a);

so, here's some chunk out of the PythonExtension template:
// since CXXPyObject is a virtual Baseclass of PythonExtension, you can
// safely use multiple inheritance with the following Scheme:
// class A : public PythonExtension<A> {
// };
// class B : public A, public PythonExtension<B> {
// }
//
// in A's an B's methods you may use the python_cast<A*>() thing below
// to get a A* or B* out of a PyObject *
template<class T>
class PythonExtension: public virtual CXXPyObject
{
  private:
    static void extension_object_deallocator(PyObject* t) {
      CXXPyObject* o=static_cast<CXXPyObject*>(t);
      delete o;
      // delete t;
    }

    explicit PythonExtension(const PythonExtension<T>& other);
    void operator=(const PythonExtension<T>& rhs);
  protected:
    explicit PythonExtension() {
      ob_refcnt = 1;
      ob_type = type_object();
    }
  public:
    virtual ~PythonExtension() {}

    static PythonType& behaviors() {
      static PythonType* p;
      if(!p) {
         p = new PythonType(sizeof(T));
         p->dealloc(extension_object_deallocator);
         p->str(s_str);
         p->repr(s_repr);
      }
...
}

If someone's interested; i can send the whole story(code).
BTW, I've also implemented some huge macros for easy (and safe!) use of
exceptions within C++ Modules. Note that you CAN'T throw exceptions
'through' the python interpreter. Consider this:

PyObject* mainEventLoop()
{
    PyObject* o=someObject;
    try {
        while (...)
            // some crufty event magic going on
            if (eventOccured())
                Py_CallMethod(o,"crashbang",...);
    }
    catch (const Exception&) {
    }
}

and 'crashbang()' calls a C++ method from within your module, which throws
an exception. My experience was that the process will go wreck, with the
default execption handler aborting the program. What i did is to write a
macro to catch these exceptions and to return Null instead.

Ok, that's it for today. Comments are welcome.

Thomas Malik







More information about the Cplusplus-sig mailing list