4. Case 2 - Hierarchy of DBo Derived Classes¶
Now we want to export the following C++ class hierarchy into Python:
PyEntity <-- PyComponent <-+- PyContact
+- PySegment <-+- PyHorizontal
+- PyVertical
4.1 Base Class Header¶
Remark: this is only a partial description of the tree for the sake of clarity.
One important fact to remember is that PyEntity and PyComponent being related to C++ abstract classes, no objects of those types will be created, only PyContact, PyHorizontal or PyVertical will.
The consequence is that there is no PyEntity_Link() like in 3.1 Class Associated Header File but instead two functions:
- PyEntity_NEW() which create the relevant PyEntity derived object from the Entity one. For example, if the Entity* given as argument is in fact a Horizontal*, then the function will return a PyHorizontal*.
- EntityCast() do the reverse of PyEntity_NEW() that is, from a PyEntity, return the C++ derived object. Again, if the PyEntity* is a PyHorizontal*, the function will cast it as a Horizontal* then return it as an Entity*.
#ifndef ISOBAR_PY_ENTITY_H
#define ISOBAR_PY_ENTITY_H
#include "hurricane/isobar/PyHurricane.h"
#include "hurricane/Entity.h"
namespace Isobar {
extern "C" {
typedef struct {
PyObject_HEAD
Hurricane::Entity* _object;
} PyEntity;
extern PyObject* PyEntity_NEW ( Hurricane::Entity* entity );
extern void PyEntity_LinkPyType ();
extern PyTypeObject PyTypeEntity;
extern PyMethodDef PyEntity_Methods[];
#define IsPyEntity(v) ( (v)->ob_type == &PyTypeEntity )
#define PYENTITY(v) ( (PyEntity*)(v) )
#define PYENTITY_O(v) ( PYENTITY(v)->_object )
} // extern "C".
Hurricane::Entity* EntityCast ( PyObject* derivedObject );
} // Isobar namespace.
#endif // ISOBAR_PY_ENTITY_H
4.2 Base Class File¶
Changes from 3.2 Class Associated File are:
- No call to DBoLinkCreateMethod() because there must be no PyEntity_Link(), but the definitions of PyEntity_NEW() and EntityCast.
- For defining the PyTypeEntity Python type, we call a different macro: PyTypeRootObjectDefinitions, dedicated to base classes.
#include "hurricane/isobar/PyCell.h"
#include "hurricane/isobar/PyHorizontal.h"
#include "hurricane/isobar/PyVertical.h"
#include "hurricane/isobar/PyContact.h"
namespace Isobar {
using namespace Hurricane;
extern "C" {
#if defined(__PYTHON_MODULE__)
#define METHOD_HEAD(function) GENERIC_METHOD_HEAD(Entity,entity,function)
DBoDestroyAttribute(PyEntity_destroy ,PyEntity)
static PyObject* PyEntity_getCell ( PyEntity *self )
{
Cell* cell = NULL;
HTRY
METHOD_HEAD( "Entity.getCell()" )
cell = entity->getCell();
HCATCH
return PyCell_Link( cell );
}
PyMethodDef PyEntity_Methods[] =
{ { "getCell", (PyCFunction)PyEntity_getCell, METH_NOARGS
, "Returns the entity cell." }
, { "destroy", (PyCFunction)PyEntity_destroy, METH_NOARGS
, "Destroy associated hurricane object, the python object remains." }
, {NULL, NULL, 0, NULL} /* sentinel */
};
DBoDeleteMethod(Entity)
PyTypeObjectLinkPyType(Entity)
#else // End of Python Module Code Part.
PyObject* PyEntity_NEW ( Entity* entity )
{
if (not entity) {
PyErr_SetString ( HurricaneError, "Invalid Entity (bad occurrence)" );
return NULL;
}
Horizontal* horizontal = dynamic_cast<Horizontal*>(entity);
if (horizontal) return PyHorizontal_Link( horizontal );
Vertical* vertical = dynamic_cast<Vertical*>(entity);
if (vertical) return PyVertical_Link( vertical );
Contact* contact = dynamic_cast<Contact*>(entity);
if (contact) return PyContact_Link( contact );
Py_RETURN_NONE;
}
PyTypeRootObjectDefinitions(Entity)
#endif // Shared Library Code Part (1).
} // extern "C".
#if !defined(__PYTHON_MODULE__)
Hurricane::Entity* EntityCast ( PyObject* derivedObject ) {
if (IsPyHorizontal(derivedObject)) return PYHORIZONTAL_O(derivedObject);
if (IsPyVertical (derivedObject)) return PYVERTICAL_O(derivedObject);
if (IsPyContact (derivedObject)) return PYCONTACT_O(derivedObject);
return NULL;
}
#endif // Shared Library Code Part (2).
} // Isobar namespace.
4.3 Intermediate Class Header¶
Changes from 3.1 Class Associated Header File are:
- As for PyEntity, and because this is still an abstract class, there is no PyComponent_Link() function.
- The definition of the PyComponent struct is differs. There is no PyObject_HEAD (it is a Python derived class). The only field is of the base class type PyEntity and for use with Coriolis macros, it must be named _baseObject (note that this is not a pointer but a whole object).
#ifndef ISOBAR_PY_COMPONENT_H
#define ISOBAR_PY_COMPONENT_H
#include "hurricane/isobar/PyEntity.h"
#include "hurricane/Component.h"
namespace Isobar {
extern "C" {
typedef struct {
PyEntity _baseObject;
} PyComponent;
extern PyTypeObject PyTypeComponent;
extern PyMethodDef PyComponent_Methods[];
extern void PyComponent_LinkPyType ();
#define IsPyComponent(v) ((v)->ob_type == &PyTypeComponent)
#define PYCOMPONENT(v) ((PyComponent*)(v))
#define PYCOMPONENT_O(v) (static_cast<Component*>(PYCOMPONENT(v)->_baseObject._object))
} // extern "C".
} // Isobar namespace.
#endif
4.4 Intermediate Class File¶
Changes from 3.2 Class Associated File are:
- Redefinition of the default macros ACCESS_OBJECT and ACCESS_CLASS.
- The pointer to the C++ encapsulated object (attribute _object) is hold by the base class PyEntity. The ACCESS_OBJECT macro which is tasked to give access to that attribute is then _baseObject._object as PyComponent is a direct derived class of PyEntity.
- ACCESS_CLASS is similar to ACCESS_OBJECT for accessing the base class, that is a pointer to PyEntity.
- For defining the PyTypeComponent Python type, we call a yet different macro: PyTypeInheritedObjectDefinitions(), dedicated to derived classes. For this this macro we need to give as argument the derived class and the base class.
#include "hurricane/isobar/PyComponent.h"
#include "hurricane/isobar/PyNet.h"
namespace Isobar {
using namespace Hurricane;
extern "C" {
#undef ACCESS_OBJECT
#undef ACCESS_CLASS
#define ACCESS_OBJECT _baseObject._object
#define ACCESS_CLASS(_pyObject) &(_pyObject->_baseObject)
#define METHOD_HEAD(function) GENERIC_METHOD_HEAD(Component,component,function)
#if defined(__PYTHON_MODULE__)
DirectGetLongAttribute(PyComponent_getX,getX,PyComponent,Component)
DirectGetLongAttribute(PyComponent_getY,getY,PyComponent,Component)
DBoDestroyAttribute(PyComponent_destroy,PyComponent)
static PyObject* PyComponent_getNet ( PyComponent *self )
{
Net* net = NULL;
HTRY
METHOD_HEAD( "Component.getNet()" )
net = component->getNet( );
HCATCH
return PyNet_Link( net );
}
PyMethodDef PyComponent_Methods[] =
{ { "getX" , (PyCFunction)PyComponent_getX , METH_NOARGS
, "Return the Component X value." }
, { "getY" , (PyCFunction)PyComponent_getY , METH_NOARGS
, "Return the Component Y value." }
, { "getNet" , (PyCFunction)PyComponent_getNet , METH_NOARGS
, "Returns the net owning the component." }
, { "destroy", (PyCFunction)PyComponent_destroy, METH_NOARGS
, "destroy associated hurricane object, the python object remains." }
, {NULL, NULL, 0, NULL} /* sentinel */
};
DBoDeleteMethod(Component)
PyTypeObjectLinkPyType(Component)
#else // Python Module Code Part.
PyTypeInheritedObjectDefinitions(Component, Entity)
#endif // Shared Library Code Part.
} // extern "C".
} // Isobar namespace.
4.5 Terminal Class Header¶
The contents of this file is almost identical to 4.3 Intermediate Class Header, save for the presence of a PyContact_Link() function. She is present at this level because the class is a concrete one and can be instanciated.
#ifndef ISOBAR_PY_CONTACT_H
#define ISOBAR_PY_CONTACT_H
#include "hurricane/isobar/PyComponent.h"
#include "hurricane/Contact.h"
namespace Isobar {
extern "C" {
typedef struct {
PyComponent _baseObject;
} PyContact;
extern PyTypeObject PyTypeContact;
extern PyMethodDef PyContact_Methods[];
extern PyObject* PyContact_Link ( Hurricane::Contact* object );
extern void PyContact_LinkPyType ();
#define IsPyContact(v) ( (v)->ob_type == &PyTypeContact )
#define PYCONTACT(v) ( (PyContact*)(v) )
#define PYCONTACT_O(v) ( PYCONTACT(v)->_baseObject._baseObject._object )
} // extern "C".
} // Isobar namespace.
#endif // ISOBAR_PY_CONTACT_H
4.6 Terminal Class File¶
Changes from 4.4 Intermediate Class File are:
- As previously, we have to redefine the macros ACCESS_OBJECT and ACCESS_CLASS.
But, as we are one level deeper into the hierarchy, one more level of
indirection using _baseObject must be used.
- ACCESS_OBJECT becomes _baseObject._baseObject._object.
- ACCESS_CLASS becomes &(_pyObject->_baseObject._baseObject).
- For defining the PyTypeContact Python type, we call again PyTypeInheritedObjectDefinitions(). It is the same whether the class is terminal or not.
- And, this time, as the Python class is concrete, we call the macro DBoLinkCreateMethod() to create the PyContact_Link() function.
#include "hurricane/isobar/PyContact.h"
namespace Isobar {
using namespace Hurricane;
extern "C" {
#undef ACCESS_OBJECT
#undef ACCESS_CLASS
#define ACCESS_OBJECT _baseObject._baseObject._object
#define ACCESS_CLASS(_pyObject) &(_pyObject->_baseObject._baseObject)
#define METHOD_HEAD(function) GENERIC_METHOD_HEAD(Contact,contact,function)
#if defined(__PYTHON_MODULE__)
DirectGetLongAttribute(PyContact_getWidth , getWidth , PyContact,Contact)
DirectGetLongAttribute(PyContact_getHeight, getHeight, PyContact,Contact)
DBoDestroyAttribute(PyContact_destroy, PyContact)
static PyObject* PyContact_create ( PyObject*, PyObject *args )
{
Contact* contact = NULL;
HTRY
// Usual signature then arguments parsing.
HCATCH
return PyContact_Link(contact);
}
PyMethodDef PyContact_Methods[] =
{ { "create" , (PyCFunction)PyContact_create , METH_VARARGS|METH_STATIC
, "Create a new Contact." }
, { "destroy" , (PyCFunction)PyContact_destroy , METH_NOARGS
, "Destroy associated hurricane object, the python object remains." }
, { "getWidth" , (PyCFunction)PyContact_getWidth , METH_NOARGS
, "Return the contact width." }
, { "getHeight", (PyCFunction)PyContact_getHeight, METH_NOARGS
, "Return the contact height." }
, {NULL, NULL, 0, NULL} /* sentinel */
};
DBoDeleteMethod(Contact)
PyTypeObjectLinkPyType(Contact)
#else // Python Module Code Part.
DBoLinkCreateMethod(Contact)
PyTypeInheritedObjectDefinitions(Contact, Component)
#endif // Shared Library Code Part.
} // extern "C".
} // Isobar namespace.
4.8 Python Module¶
DL_EXPORT(void) initHurricane ()
{
PyEntity_LinkPyType(); // step 1.
PyComponent_LinkPyType();
PyContact_LinkPyType();
PYTYPE_READY( Entity ) // step 2.
PYTYPE_READY_SUB( Component, Entity )
PYTYPE_READY_SUB( Contact , Component )
__cs.addType( "ent" , &PyTypeEntity , "<Entity>" , false ); // step 3.
__cs.addType( "comp" , &PyTypeComponent, "<Component>", false, "ent" );
__cs.addType( "contact", &PyTypeContact , "<Contact>" , false, "comp" );
PyObject* module = Py_InitModule( "Hurricane", PyHurricane_Methods );
if (module == NULL) {
cerr << "[ERROR]\n"
<< " Failed to initialize Hurricane module." << endl;
return;
}
Py_INCREF( &PyTypeContact ); // step 4.
PyModule_AddObject( module, "Contact", (PyObject*)&PyTypeContact ); // step 4.
}