440 lines
14 KiB
ReStructuredText
440 lines
14 KiB
ReStructuredText
.. -*- Mode: rst -*-
|
|
|
|
.. include:: ../etc/definitions.rst
|
|
|
|
|
|
|newpage|
|
|
|
|
|
|
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 :ref:`3.1`
|
|
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*``.
|
|
|
|
.. code-block:: python
|
|
|
|
#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
|
|
|
|
|newpage|
|
|
|
|
|
|
4.2 Base Class File
|
|
~~~~~~~~~~~~~~~~~~~~~
|
|
|
|
Changes from :ref:`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.
|
|
|
|
|
|
.. code-block:: c++
|
|
|
|
#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.
|
|
|
|
|newpage|
|
|
|
|
|
|
4.3 Intermediate Class Header
|
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
|
|
Changes from :ref:`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).
|
|
|
|
.. code-block:: c++
|
|
|
|
#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 :ref:`3.2 Class Associated File` are:
|
|
|
|
1. 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``.
|
|
|
|
|newpage|
|
|
|
|
2. 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.
|
|
|
|
.. code-block:: c++
|
|
|
|
#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.
|
|
|
|
.. code-block:: c++
|
|
|
|
#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.
|
|
|
|
|
|
.. code-block:: c++
|
|
|
|
#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
|
|
~~~~~~~~~~~~~~~~~~~
|
|
|
|
.. code-block:: c++
|
|
|
|
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.
|
|
}
|