1297 lines
42 KiB
ReStructuredText
1297 lines
42 KiB
ReStructuredText
.. -*- Mode: rst -*-
|
|
|
|
.. include:: ../etc/definitions.rst
|
|
|
|
|
|
|medskip|
|
|
|
|
|
|
===================================
|
|
Hurricane Python/C++ API Tutorial
|
|
===================================
|
|
|
|
.. contents::
|
|
|
|
|newpage|
|
|
|
|
|
|
1. Introduction
|
|
=================
|
|
|
|
* This document is written for people already familiar with the
|
|
`Python/C API Reference Manual`_.
|
|
|
|
* The macros provided by the Hurricane Python/C API are written using
|
|
the standard Python C/API. That is, you may not use them and write
|
|
directly your functions with the original API or any mix between.
|
|
You only have to respect some naming convention.
|
|
|
|
* Coriolis is build against Python 2.7.
|
|
|
|
|
|
1.1 First, A Disclaimer
|
|
~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
|
|
The Hurricane Python/C++ API has been written about ten years ago, at a time
|
|
my mastering of template programming was less than complete. This is why this
|
|
interface is build with old fashioned C macro instead of C++ template.
|
|
|
|
It is my hope that at some point in the future I will have time to completly
|
|
rewrite it, borrowing the interface from ``boost::python``.
|
|
|
|
|
|
1.2 About Technical Choices
|
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
|
|
Some would say, why not use *off the shelf* wrappers like ``swig``
|
|
or ``boost::python``, here are some clues.
|
|
|
|
#. **Partial exposure of the C++ class tree.** We expose at Python level
|
|
C++ base classes, only if they provides common methods that we want
|
|
to see. Otherwise, we just show them as base classes under Python.
|
|
For instance ``Library`` is derived from ``DBo``, but we won't see
|
|
it under Python.
|
|
|
|
#. **Bi-directional communication.** When a Python object is deleted, the
|
|
wrapper obviously has a pointer toward the underlying C++ object and
|
|
is able to delete it. But, the reverse case can occurs, meaning that
|
|
you have a C++ object wrapped in Python and the database delete the
|
|
underlying object. The wrapped Python object *must* be informed that
|
|
it no longer refer a valid C++ one. Moreover, as we do not control
|
|
when Python objects gets deleteds (that is, when their reference count
|
|
reaches zero), we can have valid Python object with a dangling
|
|
C++ pointer. So our Python objects can be warned by the C++ objects
|
|
that they are no longer valid and any other operation than the
|
|
deletion should result in a severe non-blocking error.
|
|
|
|
To be precise, this apply to persistent object in the C++ database,
|
|
like ``Cell``, ``Net``, ``Instance`` or ``Component``. Short lived
|
|
objects like ``Box`` or ``Point`` retains the classic Python behavior.
|
|
|
|
Another aspect is that, for all derived ``DBo`` objects, one and only
|
|
one Python object is associated. For one given ``Instance`` object we
|
|
will always return the *same* ``PyInstance`` object, thanks to the
|
|
bi-directional link. Obviously, the *reference count* of the
|
|
``PyInstance`` is managed accordingly. This mechanism is implemented
|
|
by the ``PyInstance_Link()`` function.
|
|
|
|
#. **Linking accross modules.** As far as I understand, the wrappers
|
|
are for monolithic libraries. That is, you wrap the entire library
|
|
in one go. But Hurricane has a modular design, the core database
|
|
then various tools. We do not, and cannot, have one gigantic wrapper
|
|
that would encompass all the libraries in one go. We do one Python
|
|
module for one C++ library.
|
|
|
|
This brings another issue, at Python level this time. The Python
|
|
modules for the libraries have to share some functions. Python
|
|
provides a mechanism to pass C function pointers accross modules,
|
|
but I did found it cumbersome. Instead, all our modules are split
|
|
in two:
|
|
|
|
* The first part contains the classic Python module code.
|
|
* The second part is to be put in a separate dynamic library that
|
|
will hold the shared functions. The Python module is dynamically linked
|
|
against that library like any other. And any other Python module
|
|
requiring the functions will link against the associated shared
|
|
library.
|
|
|
|
Each module file will be compiled *twice*, once to build the Python
|
|
module (``__PYTHON_MODULE`` is defined) and once to build the supporting
|
|
shared library (``__PYTHON_MODULE__`` **not** defined). This tricky
|
|
double compilation is taken care of though the ``add_python_module``
|
|
``cmake`` macro.
|
|
|
|
For the core Hurricane library we will have:
|
|
|
|
* ``Hurricane.so`` the Python module (use with: ``import Hurricane``).
|
|
* ``libisobar.so.1.0`` the supporting shared library.
|
|
|
|
The ``PyLibrary.cpp`` file will have the following structure:
|
|
|
|
.. code:: c++
|
|
|
|
#include "hurricane/isobar/PyLibrary.h"
|
|
|
|
namespace Isobar {
|
|
|
|
extern "C" {
|
|
|
|
#if defined(__PYTHON_MODULE__)
|
|
|
|
// +=================================================================+
|
|
// | "PyLibrary" Python Module Code Part |
|
|
// +=================================================================+
|
|
//
|
|
// The classic part of a Python module. Goes into Hurricane.so.
|
|
|
|
|
|
#else // End of Python Module Code Part.
|
|
|
|
// x=================================================================x
|
|
// | "PyLibrary" Shared Library Code Part |
|
|
// x=================================================================x
|
|
//
|
|
// Functions here will be part of the associated shared library and
|
|
// made available to all other Python modules. Goes into libisobar.so.1.0
|
|
|
|
|
|
# endif // Shared Library Code Part.
|
|
|
|
} // extern "C".
|
|
|
|
} // Isobar namespace.
|
|
|
|
|
|
This way, we do not rely upon a pointer transmission through Python
|
|
modules, but directly uses linker capabilities.
|
|
|
|
|
|
1.3 Botched Design
|
|
~~~~~~~~~~~~~~~~~~~~
|
|
|
|
The mechanism to compute the signature of a call to a Python function,
|
|
the ``__cs`` object, is much too complex and, in fact, not needed.
|
|
At some point I may root it out, but it is used in so many places...
|
|
|
|
What I should have used the ``"O!"`` capablity of ``PyArg_ParseTuple()``,
|
|
like in the code below:
|
|
|
|
|newpage|
|
|
|
|
.. code:: c++
|
|
|
|
static PyObject* PyContact_create ( PyObject*, PyObject *args )
|
|
{
|
|
Contact* contact = NULL;
|
|
HTRY
|
|
PyNet* pyNet = NULL;
|
|
PyLayer* pyLayer = NULL;
|
|
PyComponent* pyComponent = NULL;
|
|
DbU::Unit x = 0;
|
|
DbU::Unit y = 0;
|
|
DbU::Unit width = 0;
|
|
DbU::Unit height = 0;
|
|
|
|
if (PyArg_ParseTuple( args, "O!O!ll|ll:Contact.create"
|
|
, &PyTypeNet , &pyNet
|
|
, &PyTypeLayer, &pyLayer
|
|
, &x, &y, &width, &height)) {
|
|
contact = Contact::create( PYNET_O(pyNet), PYLAYER_O(pyLayer)
|
|
, x, y, width, height );
|
|
} else {
|
|
PyErr_Clear();
|
|
if (PyArg_ParseTuple( args, "O!O!ll|ll:Contact.create"
|
|
, &PyTypeComponent, &pyComponent
|
|
, &PyTypeLayer , &pyLayer
|
|
, &x, &y, &width, &height)) {
|
|
contact = Contact::create( PYCOMPONENT_O(pyComponent), PYLAYER_O(pyLayer)
|
|
, x, y, width, height );
|
|
} else {
|
|
PyErr_SetString( ConstructorError
|
|
, "invalid number of parameters for Contact constructor." );
|
|
return NULL;
|
|
}
|
|
}
|
|
HCATCH
|
|
return PyContact_Link( contact );
|
|
}
|
|
|
|
|
|
2. Basic File Structure and CMake configuration
|
|
=================================================
|
|
|
|
As a first example we will consider the ``Hurrican::Library``
|
|
class. To export a class into Python, we must create three files:
|
|
|
|
#. ``PyLibrary.h``, defines the ``PyLibrary`` C-Struct and the functions
|
|
needed outside the module istself (mostly for ``PyHurricane.cpp``).
|
|
|
|
#. ``PyLibrary.cpp``, contains the complete wrapping of the class and
|
|
the Python type definition (``PyTypeLibrary``).
|
|
|
|
#. ``PyHurricane.cpp``, the definition of the Python module into which
|
|
the classes are registered. The module act as a ``namespace`` in
|
|
Python so it is good practice to give it the same name as it's
|
|
associated C++ namespace.
|
|
|
|
|newpage|
|
|
|
|
To build a Python module in |cmake|, use the following macro:
|
|
|
|
.. code:: cmake
|
|
|
|
set( pyCpps PyLibrary.cpp
|
|
PyHurricane.cpp )
|
|
set( pyIncludes hurricane/isobar/PyLibrary.h
|
|
|
|
add_python_module( "${pyCpps}"
|
|
"${pyIncludes}"
|
|
"isobar;1.0;1" # Name & version of the supporting
|
|
# shared library.
|
|
Hurricane # Name of the Python module will give:
|
|
# Hurricane.so
|
|
"${depLibs}" # List of dependency libraries.
|
|
include/coriolis2/hurricane/isobar
|
|
# Where to install the include files.
|
|
)
|
|
|
|
|
|
3. Case 1 - DBo Derived, Standalone
|
|
======================================
|
|
|
|
As example, we take ``Library``. This a ``DBo`` derived class, but we
|
|
choose not to export the parent classes. From Python, it will appear
|
|
as a base class.
|
|
|
|
|
|
3.1 Class Associated Header File
|
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
|
|
Here is the typical content of a header file (for ``PyLibrary``):
|
|
|
|
.. code:: c++
|
|
|
|
#ifndef PY_LIBRARY_H
|
|
#define PY_LIBRARY_H
|
|
|
|
#include "hurricane/isobar/PyHurricane.h"
|
|
#include "hurricane/Library.h"
|
|
|
|
namespace Isobar {
|
|
using namespace Hurricane;
|
|
|
|
extern "C" {
|
|
|
|
typedef struct {
|
|
PyObject_HEAD
|
|
Library* _object;
|
|
} PyLibrary;
|
|
|
|
extern PyTypeObject PyTypeLibrary;
|
|
extern PyMethodDef PyLibrary_Methods[];
|
|
extern PyObject* PyLibrary_Link ( Hurricane::Library* lib );
|
|
extern void PyLibrary_LinkPyType ();
|
|
|
|
|
|
#define IsPyLibrary(v) ( (v)->ob_type == &PyTypeLibrary )
|
|
#define PYLIBRARY(v) ( (PyLibrary*)(v) )
|
|
#define PYLIBRARY_O(v) ( PYLIBRARY(v)->_object )
|
|
|
|
} // extern "C".
|
|
} // Isobar namespace.
|
|
|
|
#endif // PY_LIBRARY_H
|
|
|
|
|
|
The code is organized as follow:
|
|
|
|
1. It must have, *as the first include* ``PyHurricane.h``, which provides
|
|
the complete bunch of macros needed to build the module. Then the include
|
|
of the C++ class we want to wrap (``Library.h``).
|
|
|
|
2. As Python is written in C, all the wrapper code has to be but inside
|
|
an ``extern "C"`` namespace.
|
|
|
|
3. Definition of the wrapped |struct|, ``PyLibrary``. It is standard Python here.
|
|
|
|
.. note::
|
|
For our set of macros to work, the name of the pointer to the
|
|
C++ class must always be **_object**, and the various functions and
|
|
macros defined here must take the name of the class (either in
|
|
lowercase, camel case or capitals).
|
|
|
|
4. Declaration of the Python type ``PyTypeLibrary`` (standard).
|
|
|
|
5. Declaration of the Python type table of methods ``PyLibrary_Methods`` (standard).
|
|
|
|
.. _3.6:
|
|
|
|
6. Declaration of ``PyLibrary_Link()``, helper to convert a C++ ``Lybrary`` into
|
|
a ``PyLibrary`` (put in the support shared library).
|
|
|
|
7. Declaration of ``PyLibrary_LinkPyType()``, this function setup the class-level
|
|
function of the new Python type (here, ``PyTypeLibrary``).
|
|
|
|
8. And, lastly, three macros to:
|
|
|
|
* ``IsPylibrary()``, know if a Python object is a ``PyLibrary``
|
|
* ``PYLIBRARY()``, force cast (C style) of a ``PyObject`` into a ``PyLibrary``.
|
|
* ``PYLIBRARY_O()``, extract the C++ object (``Library*``) from the Python
|
|
object (``PyLibrary``).
|
|
|
|
|
|
3.2 Class Associated File
|
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
|
|
3.2.1 Head of the file
|
|
------------------------
|
|
|
|
.. code:: c++
|
|
|
|
#include "hurricane/isobar/PyLibrary.h"
|
|
#include "hurricane/isobar/PyDataBase.h"
|
|
#include "hurricane/isobar/PyCell.h"
|
|
|
|
namespace Isobar {
|
|
using namespace Hurricane;
|
|
|
|
extern "C" {
|
|
|
|
#define METHOD_HEAD(function) GENERIC_METHOD_HEAD(Library,lib,function)
|
|
|
|
As for the header, all the code must be put inside a ``extern "C"`` namespace.
|
|
|
|
A convenience macro ``METHOD_HEAD()`` must be defined, by refining
|
|
``GENERIC_METHOD_HEAD()``. This macro will be used in the method wrappers
|
|
below to cast the ``_object`` field of the Python object into the
|
|
appropriate C++ class, this is done using a C-style cast.
|
|
The parameters of that macro are:
|
|
|
|
#. The C++ encapsulated class (``Library``).
|
|
#. The name of the *variable* that will be used to store a pointer
|
|
to the C++ working object.
|
|
#. The name of the C++ method which is to be wrapped.
|
|
|
|
|
|
3.2.2 The Python Module Part
|
|
------------------------------
|
|
|
|
First, we have to build all the wrappers to the C++ methods of
|
|
the class. For common predicates, accessors, and mutators macros
|
|
are supplied.
|
|
|
|
Wrapping of the ``Library::getCell()`` method:
|
|
|
|
.. code:: c++
|
|
|
|
static PyObject* PyLibrary_getCell ( PyLibrary* self, PyObject* args )
|
|
{
|
|
Cell* cell = NULL;
|
|
|
|
HTRY
|
|
METHOD_HEAD( "Library.getCell()" )
|
|
char* name = NULL;
|
|
if (PyArg_ParseTuple(args,"s:Library.getCell", &name)) {
|
|
cell = lib->getCell( Name(name) );
|
|
} else {
|
|
PyErr_SetString( ConstructorError
|
|
, "invalid number of parameters for Library::getCell." );
|
|
return NULL;
|
|
}
|
|
HCATCH
|
|
|
|
return PyCell_Link(cell);
|
|
}
|
|
|
|
Key points about this method wrapper:
|
|
|
|
#. The ``HTRY`` / ``HCATCH`` macros provides an insulation from the C++
|
|
exceptions. If one is emitted, it will be catched and transformed in
|
|
a Python one. This way, the Python program will be cleanly interrupted
|
|
and the usual stack trace displayed.
|
|
|
|
#. The returned value of this method is of type ``Cell*``, we have to
|
|
transform it into a Python one. This is done with ``PyCell_Link()``.
|
|
This macro is supplied by the ``PyCell.h`` header and this is why
|
|
it must be included.
|
|
|
|
|newpage|
|
|
|
|
|
|
Wrapping of the ``Library::create()`` method:
|
|
|
|
.. code:: c++
|
|
|
|
static PyObject* PyLibrary_create( PyObject*, PyObject* args )
|
|
{
|
|
PyObject* arg0;
|
|
PyObject* arg1;
|
|
Library* library = NULL;
|
|
|
|
HTRY
|
|
__cs.init( "Library.create" ); // Step (1).
|
|
if (not PyArg_ParseTuple( args, "O&O&:Library.create"
|
|
, Converter, &arg0
|
|
, Converter, &arg1 )) { // Step (2).
|
|
PyErr_SetString( ConstructorError
|
|
, "invalid number of parameters for Library constructor." );
|
|
return NULL;
|
|
}
|
|
if (__cs.getObjectIds() == ":db:string") { // Step (3.a)
|
|
DataBase* db = PYDATABASE_O(arg0);
|
|
library = Library::create( db, Name(PyString_AsString(arg1)) );
|
|
} else if (__cs.getObjectIds() == ":library:string") { // Step (3.b)
|
|
Library* masterLibrary = PYLIBRARY_O(arg0);
|
|
library = Library::create( masterLibrary, Name(PyString_AsString(arg1)) );
|
|
} else {
|
|
PyErr_SetString( ConstructorError
|
|
, "invalid number of parameters for Library constructor." );
|
|
return NULL;
|
|
}
|
|
HCATCH
|
|
|
|
return PyLibrary_Link( library );
|
|
}
|
|
|
|
Key point about this constructor:
|
|
|
|
#. We want the Python interface to mimic as closely as possible the
|
|
C++ API. As such, Python object will be created using a static
|
|
``.create()`` method. So we do not use the usual Python allocation
|
|
mechanism.
|
|
|
|
#. As it is a *static* method, there is no first argument.
|
|
|
|
#. Python do not allow function overload like C++. To emulate that
|
|
behavior we use the ``__cs`` object (which is a global variable).
|
|
|
|
#. Init/reset the ``__cs`` object: see *step (1)*.
|
|
|
|
#. Call ``PyArg_ParseTuple()``, read every mandatory or optional
|
|
argument as a Python object (``"O&"``) and use ``Converter``
|
|
on each one. ``Converter`` will determine the real type of
|
|
the Python object given as argument by looking at the
|
|
encapsulated C++ class. It then update the ``__cs`` object.
|
|
Done in *step (2)*
|
|
|
|
#. After the call to ``PyArg_ParseTuple()``, the function
|
|
``__cs.getObjectIds()`` will return the *signature* of
|
|
the various arguments. In our case, the valid signatures
|
|
will be ``":db:string"`` (*step (3.a)*a) and ``":library:string"``
|
|
(*step (3.b)*).
|
|
|
|
#. Call the C++ method after extracting the C++ objects from
|
|
the Python arguments. Note the use of the ``PYLIBRARY_O()``
|
|
and ``PYDATABSE_O()`` macros to perform the conversion.
|
|
|
|
#. Return the result, encapsulated through a call to ``PyLibrary_Link()``.
|
|
|
|
|newpage|
|
|
|
|
|
|
Wrapping of the ``Library::destroy()`` method:
|
|
|
|
.. code:: c++
|
|
|
|
DBoDestroyAttribute(PyLibrary_destroy, PyLibrary)
|
|
|
|
For C++ classes **that are derived** from ``DBo``, the destroy method
|
|
wrapper must be defined using the macro ``DBoDestroyAttribute()``.
|
|
This macro implements the bi-directional communication mechanism
|
|
using ``Hurricane::Property``. It **must not** be used for
|
|
non ``DBo`` derived classes.
|
|
|
|
|
|
Defining the method table of the PyLibrary type:
|
|
|
|
.. code:: c++
|
|
|
|
PyMethodDef PyLibrary_Methods[] =
|
|
{ { "create" , (PyCFunction)PyLibrary_create , METH_VARARGS|METH_STATIC
|
|
, "Creates a new library." }
|
|
, { "getCell" , (PyCFunction)PyLibrary_getCell, METH_VARARGS
|
|
, "Get the cell of name <name>" }
|
|
, { "destroy" , (PyCFunction)PyLibrary_destroy, METH_NOARGS
|
|
, "Destroy associated hurricane object The python object remains." }
|
|
, {NULL, NULL, 0, NULL} /* sentinel */
|
|
};
|
|
|
|
|
|
This is standard Python/C API. The name of the ``PyMethodDef`` table must be
|
|
named from the class: ``PyLibrary_Methods``.
|
|
|
|
|
|
3.2.3 Python Type Linking
|
|
---------------------------
|
|
|
|
Defining the ``PyTypeLibrary`` class methods and the type linking function.
|
|
|
|
Those are the functions for the Python object itself to work, not the
|
|
wrapped method from the C++ class.
|
|
|
|
.. note::
|
|
At this point we **do not** define the ``PyTypeLibrary`` itself.
|
|
Only it's functions and a function to set them up *once* the
|
|
type will be defined.
|
|
|
|
.. code:: c++
|
|
|
|
DBoDeleteMethod(Library)
|
|
PyTypeObjectLinkPyType(Library)
|
|
|
|
|
|
The macro ``DBoDeleteMethod()`` define the function to delete a
|
|
``PyLibrary`` *Python* object. Again, do not mistake it for the deletion
|
|
of the C++ class (implemented by ``DBoDestroyAttribute()``).
|
|
Here again, ``DBoDeleteMethod()`` is specially tailored for
|
|
``DBo`` derived classes.
|
|
|
|
.. _PyLibrary_LinkPyType():
|
|
|
|
To define ``PyLibrary_LinkPyType()``, use the ``PyTypeObjectLinkPyType()``
|
|
macro. This macro is specific for ``DBo`` derived classes that are seen as
|
|
base classes under Python (i.e. we don't bother exposing the base
|
|
class under Python). ``PyLibrary_LinkPyType()`` setup the class functions
|
|
in the ``PyTypeLibrary`` type object, it **must** be called in the
|
|
Python module this class is part of (in this case: ``PyHurricane.cpp``).
|
|
This particular flavor of the macro *will define* and setup the
|
|
following class functions:
|
|
|
|
* ``PyTypeLibrary.tp_compare`` (defined by the macro).
|
|
* ``PyTypeLibrary.tp_repr`` (defined by the macro).
|
|
* ``PyTypeLibrary.tp_str`` (defined by the macro).
|
|
* ``PyTypeLibrary.tp_hash`` (defined by the macro).
|
|
* ``PyTypeLibrary.tp_methods`` sets to the previously defined ``PyLibrary_Methods`` table.
|
|
* ``PyTypeLibrary.tp_dealloc`` is set to a function that *must* be named ``PyLibrary_DeAlloc``,
|
|
this is what ``DBoDeleteMethod`` does. It is *not* done by ``PyTypeObjectLinkPyType``.
|
|
|
|
Defining the ``PyTypeLibrary`` type:
|
|
|
|
|
|
3.2.4 The Shared Library Part
|
|
-------------------------------
|
|
|
|
This part will be put in a separate supporting shared library, allowing
|
|
other Python module to link against it (and make use of its symbols).
|
|
|
|
.. code:: c++
|
|
|
|
DBoLinkCreateMethod(Library)
|
|
PyTypeObjectDefinitions(Library)
|
|
|
|
|
|
To define ``PyTypeLibrary``, use the ``PyTypeObjectDefinitions()`` macro.
|
|
This macro is specific for classes that, as exposed by Python,
|
|
are neither *derived* classes nor *base* classes for others.
|
|
That is, they are standalone from the inheritance point of view.
|
|
|
|
The ``DBoLinkCreateMethod()`` macro will define the ``PyLibrary_Link()``
|
|
function which is responsible for encapsulating a C++ ``Library`` object
|
|
into a Python ``PyLibrary`` one.
|
|
|
|
|
|
3.3 Python Module (C++ namespace)
|
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
|
|
We use the Python module to replicate the C++ *namespace*. Thus, for the
|
|
``Hurricane`` namespace we create a Python ``Hurricane`` module which is
|
|
defined in the ``PyHurricane.cpp`` file, then we add into that module
|
|
dictionary all the Python types encapsulating the C++ classes of that
|
|
namespace.
|
|
|
|
.. code:: c++
|
|
|
|
DL_EXPORT(void) initHurricane ()
|
|
{
|
|
PyLibrary_LinkPyType(); // step 1.
|
|
|
|
PYTYPE_READY( Library ) // step 2.
|
|
|
|
__cs.addType( "library", &PyTypeLibrary, "<Library>", false ); // step 3.
|
|
|
|
PyObject* module = Py_InitModule( "Hurricane", PyHurricane_Methods );
|
|
if (module == NULL) {
|
|
cerr << "[ERROR]\n"
|
|
<< " Failed to initialize Hurricane module." << endl;
|
|
return;
|
|
}
|
|
|
|
Py_INCREF( &PyTypeLibrary ); // step 4.
|
|
PyModule_AddObject( module, "Library", (PyObject*)&PyTypeLibrary ); // step 4.
|
|
}
|
|
|
|
The ``initHurricane()`` initialisation function shown above has
|
|
been scrubbed of everything not relevant to the ``PyLibrary`` class.
|
|
The integration of the ``PyLibrary`` class into the module needs
|
|
four steps:
|
|
|
|
#. A call to `PyLibrary_LinkPyType()`_ to hook the Python type functions
|
|
in the Python type object.
|
|
|
|
#. A call to the ``PYTYPE_READY()`` macro (standard Python).
|
|
|
|
#. Registering the type into the ``__cs`` object, with ``addType()``.
|
|
The arguments are self explanatory, save for the last which is a
|
|
boolean to tell if this is a *derived* class or not.
|
|
|
|
#. Adding the type object (``PyTypeLibrary``) into the dictionnary of
|
|
the module itself. This allow to mimic closely the C++ syntax:
|
|
|
|
.. code:: python
|
|
|
|
import Hurricane
|
|
lib = Hurricane.Library.create( db, 'root' )
|
|
|
|
|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 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.6`_
|
|
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:: 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 `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:: 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 `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:: 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 `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:: 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:: 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:: 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:: 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.
|
|
}
|
|
|
|
|
|
5. Case 3 - Non-DBo Standalone Classe
|
|
=======================================
|
|
|
|
Let's have a look at the encapsulation of ``Hurricane::Point``.
|
|
|
|
Non-BDo derived classes do not support the bi-directionnal communication.
|
|
So each Python object is associated with one C++ object. The C++ object
|
|
is created and deleted along with the Python one. This behavior implies
|
|
that the C++ object is *copy constructible* (which should be the case).
|
|
|
|
|
|
5.1 Class Header
|
|
~~~~~~~~~~~~~~~~~~
|
|
|
|
Changes from `3.1 Class Associated Header File`_:
|
|
|
|
* There is no ``PyPoint_Link()`` function, as it's related to the
|
|
bi-directional communication mechanism.
|
|
|
|
.. note::
|
|
**About the _object attribute** of the PyPoint. As the C++ object life span
|
|
(``Point``) is linked to the Python (``PyPoint``) one, we may have used a
|
|
value instead of a pointer. It is best to keep a pointer as the macros
|
|
written for ``DBo`` derived classes will remain usables.
|
|
|
|
|
|
.. code:: c++
|
|
|
|
#ifndef ISOBAR_PY_POINT_H
|
|
#define ISOBAR_PY_POINT_H
|
|
|
|
#include "hurricane/isobar/PyHurricane.h"
|
|
#include "hurricane/Point.h"
|
|
|
|
namespace Isobar {
|
|
extern "C" {
|
|
|
|
typedef struct {
|
|
PyObject_HEAD
|
|
Hurricane::Point* _object;
|
|
} PyPoint;
|
|
|
|
extern PyTypeObject PyTypePoint;
|
|
extern PyMethodDef PyPoint_Methods[];
|
|
extern void PyPoint_LinkPyType();
|
|
|
|
#define IsPyPoint(v) ( (v)->ob_type == &PyTypePoint )
|
|
#define PYPOINT(v) ( (PyPoint*)(v) )
|
|
#define PYPOINT_O(v) ( PYPOINT(v)->_object )
|
|
|
|
} // extern "C".
|
|
} // Isobar namespace.
|
|
|
|
#endif // ISOBAR_PY_POINT_H
|
|
|
|
|newpage|
|
|
|
|
|
|
5.2 Class File
|
|
~~~~~~~~~~~~~~~~
|
|
|
|
Changes from `3.2 Class Associated File`_:
|
|
|
|
* As there is no ``PyPoint_Link()`` function, there is no call to any
|
|
flavor of the ``DBoLinkcreatemethod()`` macro (obvious as it's *not*
|
|
a ``DBo``).
|
|
|
|
* To use the standard Python constructor, we have to define ``PyPoint_NEW()``
|
|
and ``PyPoint_Init()`` functions, I'm not absolutely certain that the later
|
|
needs to be defined (that part is still not clear to me from the Python doc).
|
|
|
|
* As it's not a ``DBo`` there is no ``destroy()`` method, so no call to
|
|
``DirectDestroyMethod()``
|
|
|
|
* Lastly, as this object has a ``PyPoint_NEW()`` (field ``tp_new``) and
|
|
a ``PyPoint_Init()`` (field ``tp_init``) we have to use the macro
|
|
``PyTypeObjectLinkPyTypeNewInit()`` to define ``PyPoint_LinkPyType()``.
|
|
|
|
|
|
.. code:: c++
|
|
|
|
#include "hurricane/isobar/PyPoint.h"
|
|
|
|
namespace Isobar {
|
|
using namespace Hurricane;
|
|
|
|
extern "C" {
|
|
|
|
#define METHOD_HEAD(function) GENERIC_METHOD_HEAD(Point,point,function)
|
|
|
|
#if defined(__PYTHON_MODULE__)
|
|
|
|
static PyObject* PyPoint_NEW ( PyObject* module, PyObject *args )
|
|
{
|
|
Point* point = NULL;
|
|
HTRY
|
|
PyObject* arg0 = NULL;
|
|
PyObject* arg1 = NULL;
|
|
|
|
__cs.init( "Point.Point" );
|
|
if (not PyArg_ParseTuple( args,"|O&O&:Point.Point"
|
|
, Converter,&arg0
|
|
, Converter,&arg1 )) {
|
|
PyErr_SetString ( ConstructorError
|
|
, "invalid number of parameters for Point constructor." );
|
|
return NULL;
|
|
}
|
|
|
|
if (__cs.getObjectIds() == "")
|
|
{ point = new Point()); }
|
|
else if (__cs.getObjectIds() == ":point")
|
|
{ point = new Point( *PYPOINT_O(arg0) ); }
|
|
else if (__cs.getObjectIds() == ":int:int")
|
|
{ point = new Point( PyAny_AsLong(arg0), PyAny_AsLong(arg1) ); }
|
|
else {
|
|
PyErr_SetString ( ConstructorError
|
|
, "invalid number of parameters for Point constructor." );
|
|
return NULL;
|
|
}
|
|
|
|
PyPoint* pyPoint = PyObject_NEW( PyPoint, &PyTypePoint );
|
|
if (pyPoint == NULL) { delete point; return NULL; }
|
|
pyPoint->_object = point;
|
|
HCATCH
|
|
|
|
return (PyObject*)pyPoint;
|
|
}
|
|
|
|
static int PyPoint_Init ( PyPoint* self, PyObject* args, PyObject* kwargs )
|
|
{ return 0; }
|
|
|
|
DirectGetLongAttribute(PyPoint_getX,getX,PyPoint,Point)
|
|
DirectGetLongAttribute(PyPoint_getY,getY,PyPoint,Point)
|
|
DirectSetLongAttribute(PyPoint_SetX,setX,PyPoint,Point)
|
|
DirectSetLongAttribute(PyPoint_SetY,setY,PyPoint,Point)
|
|
|
|
PyMethodDef PyPoint_Methods[] =
|
|
{ { "getX" , (PyCFunction)PyPoint_getX , METH_NOARGS
|
|
, "Return the Point X value." }
|
|
, { "getY" , (PyCFunction)PyPoint_getY , METH_NOARGS
|
|
, "Return the Point Y value." }
|
|
, { "setX" , (PyCFunction)PyPoint_SetX , METH_VARARGS
|
|
, "Modify the Point X value." }
|
|
, { "setY" , (PyCFunction)PyPoint_SetY , METH_VARARGS
|
|
, "Modify the Point Y value." }
|
|
, {NULL, NULL, 0, NULL} /* sentinel */
|
|
};
|
|
|
|
DirectDeleteMethod(PyPoint_DeAlloc,PyPoint)
|
|
PyTypeObjectLinkPyTypeNewInit(Point)
|
|
|
|
#else // Python Module Code Part.
|
|
|
|
PyTypeObjectDefinitions(Point)
|
|
|
|
#endif // Shared Library Code Part.
|
|
|
|
} // extern "C".
|
|
} // Isobar namespace.
|
|
|
|
|
|
5.2 Class File
|
|
~~~~~~~~~~~~~~~~
|
|
|
|
To put it bluntly, there is no difference in the Python module for
|
|
a standalone ``DBo`` class and a non-``DBo`` class.
|
|
|
|
|
|
6. Encapsulating DbU
|
|
======================
|
|
|
|
While ``Hurricane::DbU`` is a class, the ``Hurricane::DbU::Unit`` is only
|
|
a ``typedef`` over ``uint64_t``. The ``DbU`` class only provides a set of
|
|
static methods to manipulate and convert to and from other units.
|
|
At Python level, ``DbU::Unit`` will be stored in plain ``long long``.
|
|
|
|
When a ``DbU::Unit`` argument is expected in a Python functions, just use
|
|
the ``DbU::Unit PyAny_AsLong( PyObject* )`` function to convert it.
|
|
|
|
For example, if we explicit the expension of:
|
|
|
|
.. code:: c++
|
|
|
|
DirectSetLongAttribute(PyPoint_SetX,setX,PyPoint,Point)
|
|
|
|
|newpage|
|
|
|
|
We would get:
|
|
|
|
.. code:: c++
|
|
|
|
static PyObject* PyPoint_setX ( PyPoint *self, PyObject* args )
|
|
{
|
|
Point* cobject = static_cast<Point*>( self->_object );
|
|
if (cobject == NULL) {
|
|
PyErr_SetString( ProxyError
|
|
, "Attempt to call Point.setX() on an unbound Hurricane object" );
|
|
return NULL;
|
|
}
|
|
|
|
HTRY
|
|
PyObject* arg0 = NULL;
|
|
if (not PyArg_ParseTuple( args, "O:Point.setX()", &arg0 ))
|
|
return ( NULL );
|
|
cobject->setX( Isobar::PyAny_AsLong(arg0) );
|
|
HCATCH
|
|
Py_RETURN_NONE;
|
|
}
|
|
|
|
|
|
For the other way around, use ``PyObject* PyDbU_FromLong( DbU::Unit )``.
|
|
|
|
.. code:: c++
|
|
|
|
DirectGetLongAttribute(PyPoint_GetX,getX,PyPoint,Point)
|
|
|
|
We would get:
|
|
|
|
.. code:: c++
|
|
|
|
static PyObject* PyPoint_GetX ( PyPoint *self, PyObject* args )
|
|
{
|
|
Point* cobject = static_cast<Point*>( self->_object );
|
|
if (cobject == NULL) {
|
|
PyErr_SetString( ProxyError
|
|
, "Attempt to call Point.getX() on an unbound Hurricane object" );
|
|
return NULL;
|
|
}
|
|
return Isobar::PyDbU_FromLong(cobject->getX());
|
|
}
|
|
|
|
|
|
7. No C++ Hurricane::Name encapsulation
|
|
==========================================
|
|
|
|
To be written.
|