Updated doc of the new Python/C++ template wrapper.

* Change: In Isobar3::PyWrapper, C++ exceptions where not catcheds,
    forgot to call exceptionWrapper().
* Change: In Isobar3, remove unused pyToC<>() flavor where T is a
    simple pointer and the argument is also a simple pointer, does
    not make sense.
This commit is contained in:
Jean-Paul Chaput 2021-09-26 17:27:57 +02:00
parent a53281cdb3
commit 9c71ce8a03
11 changed files with 621 additions and 353 deletions

View File

@ -10,48 +10,78 @@ the Python/C API but we provides templates to automate as much as possible the
boring tasks (and code duplication). This way, if we need a very specific boring tasks (and code duplication). This way, if we need a very specific
feature at some point, we can still revert back to the pure Python/C API. feature at some point, we can still revert back to the pure Python/C API.
The key features of our wrapper are: The wrapper basically provides support for three kind of operations:
1. Encapsulate C++ object *in* Python ones, done by ``cToPy<>()`` template.
Template specializations are defined for the ``POD`` and basic ``STL``
types like ``std::string``.
To add more easily new specializations, they resides in the top level
scope (**not** inside ``Isobar``).
2. Decapsulate a C++ object *from* a Python one, done by ``pyToC()``.
It's implementation is slightly different from the one of ``cToPy<>()``
in the sense that it is a mix of normal C++ functions and template
functions. I was having trouble inside the ``callMethod()`` to access
to templates specialization defined *after* that point, so function be
it.
There are two mutually exclusives versions of the ``pyToC<>()`` for
objects managed through the type manager. One is for value types and
the other for pointer types.
3. Wrap C/C++ functions & methods inside C-linkage ``PyCFunction``, that is
``PyOject* (*)(PyObject*, PyObject*)``. This is done respectively through
``callFunction<>()`` and ``callMethod<>()``. ``callMethod<>()`` having
two variants, one for directly calling the function member, if there is
no overload and one for calling one given flavor of the overloaded
function member.
In addition we provides special variants for Python unary, binary and
operator functions.
In addition to those user exposed features, we have:
* The ``PyTypeManager`` and it's derived classes to store and share informations * The ``PyTypeManager`` and it's derived classes to store and share informations
about all our newly defined ``PyTypeObjects``. about all our newly defined ``PyTypeObjects``.
* The ``callMethod<>()`` and ``callFunction<>()`` function templates to * Various wrapper *classes* to wrap functions & methods. They are not directly
automatically generate a wrapper around C++ object methods or functions. exposed because the template class intanciation needs the template parameters
to be explicitely given, wich is tedious. Instead we create them *through*
* The ``PyBoject* objectLink<CppT>( CppT* )`` functions templates that provides C++ to Python a function template call, which will perform for us the template type
object translation. deduction.
* The ``bool pyAs<CppT>( PyObject*, CppT*& )`` functions templates that provides Python to C++
object translation.
We creates only two kind of ``PyObject`` (but many ``PyTypeObject``): We creates only two kind of ``PyObject`` (but many ``PyTypeObject``):
* ``PyVoidpointer`` which encapsulate one void pointer to the C++ associated * ``PyOneVoid`` which encapsulate one void pointer to the C++ associated
object. object.
.. code-block:: Python .. code-block:: Python
extern "C" { extern "C" {
typedef struct PyVoidPointer { typedef struct PyOneVoid {
PyObject_HEAD PyObject_HEAD
void* _object; void* _object1;
}; };
} }
* ``PyIteratorPointer`` which encapsulate one void pointer to the C++ associated * ``PyTwoVoid`` which encapsulate one void pointer to the C++ associated
iterator and one another to the ``PyObject`` of the container. object (an iterator) and one another to the ``PyObject`` of the container.
.. code-block:: Python .. code-block:: Python
extern "C" { extern "C" {
typedef struct PyVoidPointer { typedef struct PyTwoVoid {
PyObject_HEAD PyObject_HEAD
void* _object; // C++ iterator. void* _object1; // C++ iterator.
PyVoidPointer* _container; // Python wrapped container. PyObject* _object2; // Python wrapped container.
}; };
} }
.. note:: A ``PyTwoVoid`` can be casted/accessed as a ``PyOneVoid``.
2.1 PyTypeManager 2.1 PyTypeManager
~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~
@ -62,42 +92,41 @@ We creates only two kind of ``PyObject`` (but many ``PyTypeObject``):
* Provide a non-template abstract base class for all the derived ``PyTypeObject``. * Provide a non-template abstract base class for all the derived ``PyTypeObject``.
As said, it is not a template class but it supplies function member As said, it is not a template class but it supplies function member
template. Derived classes are provides for different kind of C++ template. Derived classes are provided to manage different kind of C++
class. classes.
* :cb:`PyTypeManagerVTrunk<CppT>` * :cb:`PyTypeManagerVTrunk<CppT>`
Is an intermediate between the non-template base class and all the Is an intermediate between the non-template base class and all the
templatized others. templatized others (do **not** use it directly).
* :cb:`PyTypeManagerNonDBo<CppT>` * :cb:`PyTypeManagerNonDBo<CppT>`
Template for standalone C++ classes that are not derived from ``DBo``. Template for standalone C++ classes that are not derived from ``DBo``.
For example ``Box`` or ``Parameter``. For example ``Box`` or ``Parameter``.
* :cb:`PyTypeManagerDBo<CppT>` * :cb:`PyTypeManagerDBo<CppT>`
Template for C++ classes that *are* not derived from ``DBo``. Template for C++ classes that *are* not derived from ``DBo``.
For example ``Cell`` or ``Instance``. For example ``Cell`` or ``Instance``.
* :cb:`PyTypeManagerDerivedDBo<CppT,BaseT>`
Template for a ``CppT`` class derived derived from a ``BaseT`` class.
``CppT`` doesn'y need to be a direct derived of ``BaseT``. ``BaseT``
needs to derive from ``DBo``
* :cb:`PyTypeManagerVector<CppT>`, template for C++ ``std::vector<CppT*>``. * :cb:`PyTypeManagerVector<CppT>`, template for C++ ``std::vector<CppT*>``.
* :cb:`PyTypeManagerVectorIterator<CppT>` * :cb:`PyTypeManagerVectorIterator<CppT>`
Template for C++ ``std::vector<CppT*>::iterator``, automatically created Template for C++ ``std::vector<CppT*>::iterator``, automatically created
from the vector registration. from the vector registration.
* :cb:`PyTypeManagerMap<CppK,CppT>`, template for C++ ``std::map<CppK*,CppT*>``. * :cb:`PyTypeManagerMap<CppK,CppT>`, template for C++ ``std::map<CppK*,CppT*>``.
* :cb:`PyTypeManagerMapIterator<CppK,CppT>` * :cb:`PyTypeManagerMapIterator<CppK,CppT>`
Template for C++ ``std::vector<CppK*,CppT*>::iterator``, automatically created Template for C++ ``std::vector<CppK*,CppT*>::iterator``, automatically created
from the map registration. from the map registration.
* :cb:`PyTypeManagerCollection<,CppT>`, template for C++ ``Hurricane::Collection<CppT*>``. * :cb:`PyTypeManagerCollection<,CppT>`, template for C++ ``Hurricane::Collection<CppT*>``.
* :cb:`PyTypeManagerCollectionIterator<,CppT>` * :cb:`PyTypeManagerCollectionIterator<,CppT>`
Template for C++ ``Hurricane::Locator<CppT*>``, automatically created from Template for C++ ``Hurricane::Locator<CppT*>``, automatically created from
the collection registration. the collection registration.
@ -108,7 +137,7 @@ We creates only two kind of ``PyObject`` (but many ``PyTypeObject``):
Functions of a ``PyTypeObject`` like the *tp* methods (``tp_alloc``, ``tp_print``, Functions of a ``PyTypeObject`` like the *tp* methods (``tp_alloc``, ``tp_print``,
``tp_hash``, ...) must have a C-linkage. So we create *one* function per slot that ``tp_hash``, ...) must have a C-linkage. So we create *one* function per slot that
we want to use, set that *same* function for all the created ``PyTypeObject``, and we want to use, set that *same* function for all the created ``PyTypeObject``, and
perform a dispact in it. The drawback is that for each access we have to perform perform a dispacth in it. The drawback is that for each access we have to perform
a map lookup. Hope it is fast. a map lookup. Hope it is fast.
Excerpt from the code: Excerpt from the code:
@ -123,7 +152,7 @@ Excerpt from the code:
extern long _tpHash ( PyObject* self ) extern long _tpHash ( PyObject* self )
{ {
// Dispatch towards the relevant class, based on ob_type pointer. // Dispatch towards the relevant class, based on ob_type pointer.
return PyTypeManager::get( self->ob_type )->_getTpHash( asVPtr(self) ); return PyTypeManager::get( Py_TYPE(self)->_getTpHash( self );
} }
} }
@ -132,7 +161,7 @@ Excerpt from the code:
public: public:
void PyTypeManager::_setupPyType () void PyTypeManager::_setupPyType ()
// Derived classes must implement it as they see fit. // Derived classes must implement it as they see fit.
virtual long _getTpHash ( PyVoidPointer* ) = 0; virtual long _getTpHash ( PyObject* ) = 0;
template<typename CppT> template<typename CppT>
static PyTypeManager* _get(); static PyTypeManager* _get();
private: private:
@ -158,63 +187,101 @@ Excerpt from the code:
2.3 Going From Python to C++ 2.3 Going From Python to C++
~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~
To convert a C++ object (pointer) into a Python object, a set of To convert a C++ object (pointer) into a Python object, a mix of
:cb:`pyAs<>` templates functions are supplieds. :cb:`pyToC<>()` templates functions and real functions are supplieds.
Templates functions are used for all types/classes managed through
the ``PyTypeManger``. They come in two flavors:
2. **Value as pointer version** (C++ argment type is ``T*``):
The encapsulated C++ object is still a pointer,
but to a *stand-alone* one which has been created for the sole
purpose of this ``PyObject``. Typical example is the ``Box``.
Then, we *copy by value* the contents of the pointed object into
the contents of the pointer argument that we where given.
3. **Pointer version** (C++ argument type is ``T**``):
The encapsulated C++ object is truly a pointer
to an element of the data-structure, then we just extract the
C++ pointer value.
Normal function overload are used for ``POD`` types (``bool``, ``int``,
``long``, ``double``, ...) and basic ``STL`` types (``std::string``, ...).
Specialization for all POD type that can be directly translated into Specialization for all POD type that can be directly translated into
Python types must be provideds (``bool``, ``int``, ``long``, ``double``, Python types must be provideds (``bool``, ``int``, ``long``, ``double``,
``std::string``, ...). ``std::string``, ...).
Partial specialization for type availables through the ``PyTypeManager`` Those templates/functions are the ones the ``Isobar::parse_objects()`` recursive
is supplied. It checks the manager for the type's existence. template function call in turn for each ``PyObject*`` argument.
.. note:: ``Hurricane::Name`` are *not* exposed to the Python interface, they
must be treated as ``std::string``.
.. code-block:: C++ .. code-block:: C++
// Most generic template, should *not* be used. // Template/Pointer to a value flavor.
// So issue an error message and report a failed conversion. template< typename T
template<typename T> , typename std::enable_if< !std::is_pointer<T>::value, bool >::type = true >
inline bool pyAs ( PyObject* pyArg, T& arg ) inline bool pyToC ( PyObject* pyArg, T* arg )
{ {
std::cerr << "Isobar3.pyAs<T>(): Unsupported type \"" typedef typename std::remove_cv<T>::type NonConstT;
<< demangle(typeid(T).name()) << "\"" << endl; Isobar3::PyTypeManager* manager = Isobar3::PyTypeManager::_get<T>();
return false; if (not manager) {
std::cerr << "Isobar3::pyToC<>(const T*): Unsupported type." << std::endl;
return false;
}
if (Py_TYPE(pyArg) != manager->_getTypeObject()) return false;
*(const_cast< NonConstT* >(arg)) = *(( T* )( Isobar3::object1( pyArg )));
return true;
} }
// Template specialization for POD type "int". // Template/Pointer to a pointer flavor.
template<> template<typename T>
inline bool pyAs ( PyObject* pyArg, int& arg ) inline bool pyToC ( PyObject* pyArg, T** arg )
{ {
if (PyInt_Check (pyArg)) { arg = PyInt_AsLong ( pyArg ); } Isobar3::PyTypeManager* manager = Isobar3::PyTypeManager::_get<T>();
else if (PyLong_Check(pyArg)) { arg = PyLong_AsLong( pyArg ); } if (not manager) {
std::cerr << "Isobar3::pyToC<T>(T*&): Unsupported type \"" << typeid(T).name() << "\"" << std::endl;
return false;
}
*arg = (T*)( Isobar3::object1( pyArg ));
return true;
}
// True function overload for std::string.
inline bool pyToC ( PyObject* pyArg, std::string* arg )
{
if (not PyUnicode_Check(pyArg)) return false;
PyObject* pyBytes = PyUnicode_AsASCIIString( pyArg );
*arg = PyBytes_AsString( pyBytes );
Py_DECREF( pyBytes );
return true;
}
// True function overload for bool.
inline bool pyToC ( PyObject* pyArg, bool* arg )
{
if (not PyBool_Check(pyArg)) return false;
*arg = (pyArg == Py_True);
return true;
}
// True function overload for int.
inline bool pyToC ( PyObject* pyArg, int* arg )
{
if (PyLong_Check(pyArg)) { *arg = PyLong_AsLong( pyArg ); }
else return false; else return false;
return true; return true;
} }
// Template partial specialization for any type registered in
// the PyTypeManager.
template<typename T>
inline bool pyAs ( PyObject* pyArg, T*& arg )
{
PyTypeManager* manager = PyTypeManager::_get<T>();
if (not manager) {
std::cerr << "Isobar3.pyAs<T>(): Unsupported type \""
<< typeid(T).name() << "\"" << endl;
return false;
}
arg = (T*)( asVPtr( pyArg )->_object );
return true;
}
template<typename T>
inline bool pyAs ( PyVoidPointer* pyArg, T*& arg )
{ return pyAs( (PyObject*)pyArg, arg ); }
2.4 Going From C++ to Python 2.4 Going From C++ to Python
~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~
To convert a Python object into a C++ object, a set of To convert a Python object into a C++ object, a set of
:cb:`objectLink<>` templates functions are supplieds. :cb:`cToPy<>` templates functions are supplieds.
We completely disable the partially specialized templates for We completely disable the partially specialized templates for
objects that are non-POD as the compiler seems to be unable to objects that are non-POD as the compiler seems to be unable to
@ -230,48 +297,65 @@ method.
**why** we need the intermediate ``PyTypeManagerVTrunk<CppT>`` **why** we need the intermediate ``PyTypeManagerVTrunk<CppT>``
template class. template class.
.. note:: **Different C++ templates.** You may notice that the two following templates
may look like specializations of the same one:
* ``template<typename CppT> PyObject* cToPy ( CppT object )``
* ``template<typename CppT> PyObject* cToPy ( CppT* object )``
Which would be illegal (function templates are not allowed to have *partial*
specialization), but they are *not*. The two pairs
``(template parameter,function parameter)``, that is ``(CppT,CppT)`` and
``(CppT,CppT*)`` cannot be made to be a specialization of each other.
.. code-block:: C++ .. code-block:: C++
// The most generic template, should *not* be used. So raise a Python exception. // Generic template for values.
template< typename CppT > template< typename CppT >
inline PyObject* objectLink ( CppT object ) inline PyObject* cToPy ( CppT object )
{ {
std::string message = "Overload for Isobar3::objectLink< " if (not Isobar3::PyTypeManager::hasType<CppT>()) {
+ demangle(typeid(CppT).name()) + " >() is missing."; std::string message = "Overload for Isobar3::cToPy< "
PyErr_SetString( HurricaneError, message.c_str() ); + Hurricane::demangle(typeid(CppT).name()) + " >() Type not registered in the manager.";
return NULL; PyErr_SetString( Isobar3::HurricaneError, message.c_str() );
return NULL;
}
return Isobar3::PyTypeManager::link<CppT>( new CppT (object) );
} }
// Disable this template if "CppT" is an already supported type. // Disabled for POD & STL types, pointer flavor.
template< typename CppT template< typename CppT
, typename std::enable_if< !std::is_same<CppT,bool>::value , typename std::enable_if< !std::is_same<CppT,bool>::value
&& !std::is_same<CppT,int >::value && !std::is_same<CppT,int >::value
&& !std::is_same<CppT,std::string>::value && !std::is_same<CppT,std::string>::value
&& !std::is_same<CppT,const std::string>::value && !std::is_same<CppT,const std::string>::value,bool>::type = true >
,bool>::type = true > inline PyObject* cToPy ( CppT* object )
inline PyObject* objectLink ( CppT* object ) { return Isobar3::PyTypeManager::link<CppT>( object ); }
{ return PyTypeManager::link<CppT>( object ); }
// Disable this template if "CppT" is an already supported type. // Disabled for POD & STL types, const pointer flavor.
template< typename CppT template< typename CppT
, typename std::enable_if< !std::is_same<CppT,bool>::value , typename std::enable_if< !std::is_same<CppT,bool>::value
&& !std::is_same<CppT,int >::value && !std::is_same<CppT,int >::value
&& !std::is_same<CppT,std::string>::value && !std::is_same<CppT,std::string>::value
&& !std::is_same<CppT,const std::string>::value && !std::is_same<CppT,const std::string>::value,bool>::type = true >
,bool>::type = true > inline PyObject* cToPy ( const CppT* object )
inline PyObject* objectLink ( const CppT* object ) { return Isobar3::PyTypeManager::link<CppT>( const_cast<CppT*>( object )); }
{ return PyTypeManager::link<CppT>( const_cast<CppT*>( object )); }
// Specializations for POD type "int" . // Specialization for booleans.
template<> inline PyObject* objectLink<int > ( int i ) template<>
{ return PyInt_FromLong( i ); } inline PyObject* cToPy<bool> ( bool b )
{ if (b) Py_RETURN_TRUE; Py_RETURN_FALSE; }
template<> inline PyObject* objectLink<const int > ( const int i ) // Specialization for STL std::string.
{ return PyInt_FromLong( i ); } template<> inline PyObject* cToPy< std::string> ( std::string s ) { return PyUnicode_FromString( s.c_str() ); }
template<> inline PyObject* cToPy<const std::string > ( const std::string s ) { return PyUnicode_FromString( s.c_str() ); }
template<> inline PyObject* cToPy<const std::string*> ( const std::string* s ) { return PyUnicode_FromString( s->c_str() ); }
template<> inline PyObject* objectLink<const int*> ( const int* i ) // Specialization for POD int.
{ return PyInt_FromLong( *i ); } template<> inline PyObject* cToPy< int > ( int i ) { return PyLong_FromLong( i ); }
template<> inline PyObject* cToPy<const int > ( const int i ) { return PyLong_FromLong( i ); }
template<> inline PyObject* cToPy<const int*> ( const int* i ) { return PyLong_FromLong( *i ); }
2.5 Object Methods Wrappers 2.5 Object Methods Wrappers
@ -309,8 +393,8 @@ Basically, we want to fit:
to a sequence of ``PyObject*`` (that must match the number of arguments to a sequence of ``PyObject*`` (that must match the number of arguments
of it's C++ function conterpart). of it's C++ function conterpart).
So, the problem is to change a tuple which size is only kown at *runtime* So, the problem is to change a Python tuple which size is only kown at
into a list of C/C++ parameters known at *compile time*. *runtime* into a list of C/C++ parameters known at *compile time*.
I am not such an expert in template programming so I can find a *generic* I am not such an expert in template programming so I can find a *generic*
solution able to handle any number of parameters. Instead I did write solution able to handle any number of parameters. Instead I did write
@ -320,7 +404,22 @@ to the C++ function call and the duplicated code needed for each template
is kept to a minimum. is kept to a minimum.
To translate the Python tuple into an ordered list (vector like) of C++ To translate the Python tuple into an ordered list (vector like) of C++
object *of different types*, the obvious choice is C++ ``std::tuple<>``. object *of different types*, the obvious choice should have been ``std::tuple<>``,
but I did encouter problems when the functions signature did contains
references. So to manage that I did implement:
* A ``BaseArg`` class and it's template derived ``Arg<T>`` to hold
one value of a type (more or less like ``std::any<>``).
The type of the value attribute of ``Arg<T>`` is ``T`` *stripped*
from reference and constness. This internal type is accessible
through ``Arg<T>::ValueT``.
* A template list of arguments ``Args<typename... Ts>`` analogous to
``std::tuple<>`` which holds a table of ``BaseArg`` to convert all the
arguments.
* A recursive template converter function ``parse_pyobjects<>``, which is
called through the ``Args<>::parse()`` function.
Another challenge is the return type. I distinguish three flavor of Another challenge is the return type. I distinguish three flavor of
return type: return type:
@ -332,50 +431,96 @@ return type:
To uniformize the return type we create four templates ``_callMethodReturn<>()`` To uniformize the return type we create four templates ``_callMethodReturn<>()``
that takes whatever the C++ return type is, and turn it into a ``PyObject*``. that takes whatever the C++ return type is, and turn it into a ``PyObject*``.
Except for the functions returning ``void``, we call ``objectLink<>()`` to Except for the functions returning ``void``, we call ``cToPy<>()`` to
wrap the value. wrap the value. Given the return type of the method, only one template
will match. But as functions template do not allow partial specialization,
only one must be defined for that method (the one *matching* it's
return type), so we make the template mutually exclusives based on
the ``TR`` type (with the ``std::enable_if<>`` clause).
.. note:: In the various ``_callMethodReturn<>`` we have *two* sets for the
method parameters types : ``TArgsF...`` and ``TArgsW...``. This is to
allow a wider range of matching in the template as the type of the
arguments of the method (``TArgsF...``) may not *exactly* matches the
one passed by the wrapper (``TArgsW...``), typically the method has
a ``const`` parameter which is non-``const`` in the wrapper.
Here is an excerpt of the code: Here is an excerpt of the code:
.. code-block:: C++ .. code-block:: C++
// Flavor for "return by value" (seems to match std::is_object<>)
template< typename TC, typename TR, typename... TArgsF, typename... TArgsW
, typename std::enable_if< !std::is_reference<TR>::value
&& !std::is_pointer <TR>::value
&& !std::is_void <TR>::value,bool>::type = true >
inline PyObject* _callMethodReturn ( TR(TC::* method)(TArgsF...), TC* cppObject, TArgsW... args )
{
TR value = (cppObject->*method)( args... );
return cToPy( value );
}
// Flavor for "return by reference"
template< typename TC, typename TR, typename... TArgsF, typename... TArgsW
, typename std::enable_if< std::is_reference<TR>::value
&& !std::is_pointer <TR>::value
&& !std::is_void <TR>::value,bool>::type = true >
inline PyObject* _callMethodReturn ( TR(TC::* method)(TArgsF...), TC* cppObject, TArgsW... args )
{
TR rvalue = (cppObject->*method)( args... );
return cToPy( rvalue );
}
// Flavor for "return by pointer". // Flavor for "return by pointer".
template< typename TC, typename TR, typename... TArgs template< typename TC, typename TR, typename... TArgsF, typename... TArgsW
, typename std::enable_if<std::is_pointer<TR>::value,bool>::type = true > , typename std::enable_if<std::is_pointer<TR>::value,bool>::type = true >
inline PyObject* _callMethodReturn ( TR(TC::* method)(TArgs...), TC* cppObject, TArgs... args ) inline PyObject* _callMethodReturn ( TR(TC::* method)(TArgsF...), TC* cppObject, TArgsW... args )
{ {
TR pvalue = (cppObject->*method)( args... ); TR pvalue = (cppObject->*method)( args... );
return objectLink( pvalue ); return cToPy( pvalue );
} }
// Flavor for "return void". // Flavor for "return void".
template< typename TC, typename TR, typename... TArgs template< typename TC, typename TR, typename... TArgsF, typename... TArgsW
, typename std::enable_if<std::is_void<TR>::value,bool>::type = true > , typename std::enable_if<std::is_void<TR>::value,bool>::type = true >
inline PyObject* _callMethodReturn ( TR(TC::* method)(TArgs...), TC* cppObject, TArgs... args ) inline PyObject* _callMethodReturn ( TR(TC::* method)(TArgsF...), TC* cppObject, TArgsW... args )
{ {
(cppObject->*method)( args... ); (cppObject->*method)( args... );
Py_RETURN_NONE; Py_RETURN_NONE;
} }
// Function without argument. // Make the translation call for a method without arguments.
template< typename TC, typename TR > template< typename TC, typename TR >
inline PyObject* _callMethod ( TR(TC::* method)(), TC* cppObject, std::tuple<> ) inline PyObject* _callMethod ( TR(TC::* method)(), TC* cppObject, Args<>& )
{ return _callMethodReturn<TC,TR>( method, cppObject ); } { return _callMethodReturn<TC,TR>( method, cppObject ); }
// Function with one argument. // Make the translation call for a method one argument.
template< typename TC, typename TR, typename TA0 > template< typename TC, typename TR, typename TA0 >
inline PyObject* _callMethod ( TR(TC::* method)(TA0), TC* cppObject, std::tuple<TA0> args ) inline PyObject* _callMethod ( TR(TC::* method)(TA0), TC* cppObject, Args<TA0>& args )
{ return _callMethodReturn( method, cppObject, std::get<0>(args) ); } { return _callMethodReturn( method, cppObject, as<TA0>( args[0] ) ); }
// Function with two arguments. // Make the translation call for a method two argument.
template< typename TC, typename TR, typename TA0, typename TA1 > template< typename TC, typename TR, typename TA0, typename TA1 >
PyObject* _callMethod ( TR(TC::* method)(TA0,TA1), TC* cppObject, std::tuple<TA0,TA1> args ) PyObject* _callMethod ( TR(TC::* method)(TA0,TA1), TC* cppObject, Args<TA0,TA1>& args )
{ return _callMethodReturn( method, cppObject, std::get<0>(args), std::get<1>(args) ); } { return _callMethodReturn( method, cppObject, as<TA0>( args[0] ), as<TA1>( args[1] ) ); }
The complete work of translating the Python tuple into a ``std::tuple<>`` and error The complete work of translating the Python tuple into a ``Args<>`` is done inside
handling is done with a dedicated template class ``PyMethodWrapper`` and it's ``call()`` a dedicated template class ``PyWrapper`` and it's ``call()`` method.
method. C++ exceptions are catched and translated into Python ones.
* ``PyWrapper`` the base class wich handle both C++ and Python exceptions.
Provides the ``call()`` function which in turn wraps the ``_call()`` that
must be overloaded in derived classes.
* ``PyFunctionWrapper<>``, template derived class for C/C++ normal functions.
* ``PyMethodWrapper<>``, template derived class for C++ class methods.
Two flavors are supported, the real method and a function build upon a
method (first argument beaing the object itself). The later is used when
we need to desambiguate overloaded functions, we must create one *function*
per overload.
As a class template cannot guess the template parameters, we wrap them into a As a class template cannot guess the template parameters, we wrap them into a
function template which can perform the guess. The ``callMethod<>`` template function. function template which can perform the guess. The ``callMethod<>`` template function.
@ -384,7 +529,7 @@ In the end, what the user can write is simply:
.. code-block:: C++ .. code-block:: C++
static PyObject* PyParameter_addValue ( PyVoidPointer* self, PyObject* args ) static PyObject* PyParameter_addValue ( PyObject* self, PyObject* args )
{ return callMethod("Parameter.addValue",&Parameter::addValue,self,args); } { return callMethod("Parameter.addValue",&Parameter::addValue,self,args); }
PyMethodDef PyParameter_Methods[] = PyMethodDef PyParameter_Methods[] =
@ -400,7 +545,7 @@ In the end, what the user can write is simply:
2.6 Case of C++ overloaded functions 2.6 Case of C++ overloaded functions
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
This apply to both overloaded functions and functions with arguments values. This apply to both overloaded functions and functions with default arguments values.
In that case, the only solution is to create a set of different functions In that case, the only solution is to create a set of different functions
with differents arguments to expose all the various signature of the function. with differents arguments to expose all the various signature of the function.
@ -433,7 +578,7 @@ support both case (through different constructors).
, Parameter::Priority pri ) , Parameter::Priority pri )
{ return self->setString(value,pri,flags); } { return self->setString(value,pri,flags); }
static PyObject* PyParameter_setString ( PyVoidPointer* self, PyObject* args ) static PyObject* PyParameter_setString ( PyObject* self, PyObject* args )
{ {
PyObject* rvalue = callMethod("Parameter.setString",&setString3,self,args); PyObject* rvalue = callMethod("Parameter.setString",&setString3,self,args);
if (not rvalue) rvalue = callMethod("Parameter.setString",&setString2,self,args); if (not rvalue) rvalue = callMethod("Parameter.setString",&setString2,self,args);
@ -484,7 +629,7 @@ the ``Priority`` ``enum``.
.. code-block:: C++ .. code-block:: C++
template<> template<>
inline void pyTypePostModuleInit<Cfg::Parameter> ( PyTypeObject* typeObject ) inline void pyTypePostInit<Cfg::Parameter> ( PyTypeObject* typeObject )
{ {
PyTypeManagerNonDBo<Cfg::Parameter::Priority>::create( (PyObject*)typeObject PyTypeManagerNonDBo<Cfg::Parameter::Priority>::create( (PyObject*)typeObject
, Cfg::PyParameterPriority_Methods , Cfg::PyParameterPriority_Methods
@ -493,7 +638,7 @@ the ``Priority`` ``enum``.
} }
template<> template<>
inline void pyTypePostModuleInit<Cfg::Parameter::Priority> ( PyTypeObject* typeObject ) inline void pyTypePostInit<Cfg::Parameter::Priority> ( PyTypeObject* typeObject )
{ {
// Parameter::Priority enum. // Parameter::Priority enum.
addConstant( typeObject, "UseDefault" , Cfg::Parameter::UseDefault ); addConstant( typeObject, "UseDefault" , Cfg::Parameter::UseDefault );

View File

@ -12,7 +12,7 @@
directly your functions with the original API or any mix between. directly your functions with the original API or any mix between.
You only have to respect some naming convention. You only have to respect some naming convention.
* Coriolis is build against Python 2.7. * Coriolis is build against Python 3.6.
1.1 About Technical Choices 1.1 About Technical Choices
@ -52,10 +52,10 @@ Some would say, why not use *off the shelf* wrappers like ``swig``,
#. **Linking accross modules.** As far as I understand, the wrappers #. **Linking accross modules.** As far as I understand, the wrappers
are for monolithic libraries. That is, you wrap the entire library are for monolithic libraries. That is, you wrap the entire library
in one go. But Hurricane has a modular design, the core database in one go. But Coriolis has a modular design, the core database
then various tools. We do not, and cannot, have one gigantic wrapper 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 that would encompass all the libraries in one go. We do one Python
module for one C++ library. module for each C++ library.
This brings another issue, at Python level this time. The Python This brings another issue, at Python level this time. The Python
modules for the libraries have to share some functions. Python modules for the libraries have to share some functions. Python

View File

@ -153,7 +153,7 @@
the standard Python C/API. That is, you may not use them and write 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. directly your functions with the original API or any mix between.
You only have to respect some naming convention.</li> You only have to respect some naming convention.</li>
<li>Coriolis is build against Python 2.7.</li> <li>Coriolis is build against Python 3.6.</li>
</ul> </ul>
<div class="section" id="about-technical-choices"> <div class="section" id="about-technical-choices">
<h3><a class="toc-backref" href="#id4">1.1 About Technical Choices</a></h3> <h3><a class="toc-backref" href="#id4">1.1 About Technical Choices</a></h3>
@ -189,10 +189,10 @@ by the <tt class="docutils literal"><span class="pre">PyTypeManager::_link()</sp
</li> </li>
<li><p class="first"><strong>Linking accross modules.</strong> As far as I understand, the wrappers <li><p class="first"><strong>Linking accross modules.</strong> As far as I understand, the wrappers
are for monolithic libraries. That is, you wrap the entire library are for monolithic libraries. That is, you wrap the entire library
in one go. But Hurricane has a modular design, the core database in one go. But Coriolis has a modular design, the core database
then various tools. We do not, and cannot, have one gigantic wrapper 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 that would encompass all the libraries in one go. We do one Python
module for one C++ library.</p> module for each C++ library.</p>
<p>This brings another issue, at Python level this time. The Python <p>This brings another issue, at Python level this time. The Python
modules for the libraries have to share some functions. Python modules for the libraries have to share some functions. Python
provides a mechanism to pass C function pointers accross modules, provides a mechanism to pass C function pointers accross modules,
@ -218,83 +218,107 @@ hides the Python/C API. Instead we keep mostly visible the classic structure of
the Python/C API but we provides templates to automate as much as possible the the Python/C API but we provides templates to automate as much as possible the
boring tasks (and code duplication). This way, if we need a very specific boring tasks (and code duplication). This way, if we need a very specific
feature at some point, we can still revert back to the pure Python/C API.</p> feature at some point, we can still revert back to the pure Python/C API.</p>
<p>The key features of our wrapper are:</p> <p>The wrapper basically provides support for three kind of operations:</p>
<ol class="arabic">
<li><p class="first">Encapsulate C++ object <em>in</em> Python ones, done by <tt class="docutils literal"><span class="pre">cToPy&lt;&gt;()</span></tt> template.
Template specializations are defined for the <tt class="docutils literal">POD</tt> and basic <tt class="docutils literal">STL</tt>
types like <tt class="docutils literal"><span class="pre">std::string</span></tt>.</p>
<p>To add more easily new specializations, they resides in the top level
scope (<strong>not</strong> inside <tt class="docutils literal">Isobar</tt>).</p>
</li>
<li><p class="first">Decapsulate a C++ object <em>from</em> a Python one, done by <tt class="docutils literal">pyToC()</tt>.
It's implementation is slightly different from the one of <tt class="docutils literal"><span class="pre">cToPy&lt;&gt;()</span></tt>
in the sense that it is a mix of normal C++ functions and template
functions. I was having trouble inside the <tt class="docutils literal">callMethod()</tt> to access
to templates specialization defined <em>after</em> that point, so function be
it.</p>
<p>There are two mutually exclusives versions of the <tt class="docutils literal"><span class="pre">pyToC&lt;&gt;()</span></tt> for
objects managed through the type manager. One is for value types and
the other for pointer types.</p>
</li>
<li><p class="first">Wrap C/C++ functions &amp; methods inside C-linkage <tt class="docutils literal">PyCFunction</tt>, that is
<tt class="docutils literal">PyOject* <span class="pre">(*)(PyObject*,</span> PyObject*)</tt>. This is done respectively through
<tt class="docutils literal"><span class="pre">callFunction&lt;&gt;()</span></tt> and <tt class="docutils literal"><span class="pre">callMethod&lt;&gt;()</span></tt>. <tt class="docutils literal"><span class="pre">callMethod&lt;&gt;()</span></tt> having
two variants, one for directly calling the function member, if there is
no overload and one for calling one given flavor of the overloaded
function member.</p>
<p>In addition we provides special variants for Python unary, binary and
operator functions.</p>
</li>
</ol>
<p>In addition to those user exposed features, we have:</p>
<ul class="simple"> <ul class="simple">
<li>The <tt class="docutils literal">PyTypeManager</tt> and it's derived classes to store and share informations <li>The <tt class="docutils literal">PyTypeManager</tt> and it's derived classes to store and share informations
about all our newly defined <tt class="docutils literal">PyTypeObjects</tt>.</li> about all our newly defined <tt class="docutils literal">PyTypeObjects</tt>.</li>
<li>The <tt class="docutils literal"><span class="pre">callMethod&lt;&gt;()</span></tt> and <tt class="docutils literal"><span class="pre">callFunction&lt;&gt;()</span></tt> function templates to <li>Various wrapper <em>classes</em> to wrap functions &amp; methods. They are not directly
automatically generate a wrapper around C++ object methods or functions.</li> exposed because the template class intanciation needs the template parameters
<li>The <tt class="docutils literal">PyBoject* objectLink&lt;CppT&gt;( CppT* )</tt> functions templates that provides C++ to Python to be explicitely given, wich is tedious. Instead we create them <em>through</em>
object translation.</li> a function template call, which will perform for us the template type
<li>The <tt class="docutils literal">bool pyAs&lt;CppT&gt;( PyObject*, CppT*&amp; )</tt> functions templates that provides Python to C++ deduction.</li>
object translation.</li>
</ul> </ul>
<p>We creates only two kind of <tt class="docutils literal">PyObject</tt> (but many <tt class="docutils literal">PyTypeObject</tt>):</p> <p>We creates only two kind of <tt class="docutils literal">PyObject</tt> (but many <tt class="docutils literal">PyTypeObject</tt>):</p>
<ul> <ul>
<li><p class="first"><tt class="docutils literal">PyVoidpointer</tt> which encapsulate one void pointer to the C++ associated <li><p class="first"><tt class="docutils literal">PyOneVoid</tt> which encapsulate one void pointer to the C++ associated
object.</p> object.</p>
<div class="highlight"><pre><span></span><span class="n">extern</span> <span class="s2">&quot;C&quot;</span> <span class="p">{</span> <div class="highlight"><pre><span></span><span class="n">extern</span> <span class="s2">&quot;C&quot;</span> <span class="p">{</span>
<span class="n">typedef</span> <span class="n">struct</span> <span class="n">PyVoidPointer</span> <span class="p">{</span> <span class="n">typedef</span> <span class="n">struct</span> <span class="n">PyOneVoid</span> <span class="p">{</span>
<span class="n">PyObject_HEAD</span> <span class="n">PyObject_HEAD</span>
<span class="n">void</span><span class="o">*</span> <span class="n">_object</span><span class="p">;</span> <span class="n">void</span><span class="o">*</span> <span class="n">_object1</span><span class="p">;</span>
<span class="p">};</span> <span class="p">};</span>
<span class="p">}</span> <span class="p">}</span>
</pre></div> </pre></div>
</li> </li>
<li><p class="first"><tt class="docutils literal">PyIteratorPointer</tt> which encapsulate one void pointer to the C++ associated <li><p class="first"><tt class="docutils literal">PyTwoVoid</tt> which encapsulate one void pointer to the C++ associated
iterator and one another to the <tt class="docutils literal">PyObject</tt> of the container.</p> object (an iterator) and one another to the <tt class="docutils literal">PyObject</tt> of the container.</p>
<div class="highlight"><pre><span></span><span class="n">extern</span> <span class="s2">&quot;C&quot;</span> <span class="p">{</span> <div class="highlight"><pre><span></span><span class="n">extern</span> <span class="s2">&quot;C&quot;</span> <span class="p">{</span>
<span class="n">typedef</span> <span class="n">struct</span> <span class="n">PyVoidPointer</span> <span class="p">{</span> <span class="n">typedef</span> <span class="n">struct</span> <span class="n">PyTwoVoid</span> <span class="p">{</span>
<span class="n">PyObject_HEAD</span> <span class="n">PyObject_HEAD</span>
<span class="n">void</span><span class="o">*</span> <span class="n">_object</span><span class="p">;</span> <span class="o">//</span> <span class="n">C</span><span class="o">++</span> <span class="n">iterator</span><span class="o">.</span> <span class="n">void</span><span class="o">*</span> <span class="n">_object1</span><span class="p">;</span> <span class="o">//</span> <span class="n">C</span><span class="o">++</span> <span class="n">iterator</span><span class="o">.</span>
<span class="n">PyVoidPointer</span><span class="o">*</span> <span class="n">_container</span><span class="p">;</span> <span class="o">//</span> <span class="n">Python</span> <span class="n">wrapped</span> <span class="n">container</span><span class="o">.</span> <span class="n">PyObject</span><span class="o">*</span> <span class="n">_object2</span><span class="p">;</span> <span class="o">//</span> <span class="n">Python</span> <span class="n">wrapped</span> <span class="n">container</span><span class="o">.</span>
<span class="p">};</span> <span class="p">};</span>
<span class="p">}</span> <span class="p">}</span>
</pre></div> </pre></div>
</li> </li>
</ul> </ul>
<div class="note">
<p class="first admonition-title">Note</p>
<p class="last">A <tt class="docutils literal">PyTwoVoid</tt> can be casted/accessed as a <tt class="docutils literal">PyOneVoid</tt>.</p>
</div>
<div class="section" id="pytypemanager"> <div class="section" id="pytypemanager">
<h3><a class="toc-backref" href="#id6">2.1 PyTypeManager</a></h3> <h3><a class="toc-backref" href="#id6">2.1 PyTypeManager</a></h3>
<p><tt class="docutils literal">PyTypeManager</tt> has two usage:</p> <p><tt class="docutils literal">PyTypeManager</tt> has two usage:</p>
<ul> <ul class="simple">
<li><p class="first">Act as a registry of all the created <tt class="docutils literal">PyTypeObject</tt>, and serve as a <li>Act as a registry of all the created <tt class="docutils literal">PyTypeObject</tt>, and serve as a
dispatcher for the <tt class="docutils literal">PyTypeObject</tt> <em>tp</em> like methods.</p> dispatcher for the <tt class="docutils literal">PyTypeObject</tt> <em>tp</em> like methods.</li>
</li> <li>Provide a non-template abstract base class for all the derived <tt class="docutils literal">PyTypeObject</tt>.
<li><p class="first">Provide a non-template abstract base class for all the derived <tt class="docutils literal">PyTypeObject</tt>.
As said, it is not a template class but it supplies function member As said, it is not a template class but it supplies function member
template. Derived classes are provides for different kind of C++ template. Derived classes are provided to manage different kind of C++
class.</p> classes.<ul>
<ul> <li><span class="cb">PyTypeManagerVTrunk&lt;CppT&gt;</span>
<li><p class="first"><span class="cb">PyTypeManagerVTrunk&lt;CppT&gt;</span></p> Is an intermediate between the non-template base class and all the
<p>Is an intermediate between the non-template base class and all the templatized others (do <strong>not</strong> use it directly).</li>
templatized others.</p> <li><span class="cb">PyTypeManagerNonDBo&lt;CppT&gt;</span>
</li> Template for standalone C++ classes that are not derived from <tt class="docutils literal">DBo</tt>.
<li><p class="first"><span class="cb">PyTypeManagerNonDBo&lt;CppT&gt;</span></p> For example <tt class="docutils literal">Box</tt> or <tt class="docutils literal">Parameter</tt>.</li>
<p>Template for standalone C++ classes that are not derived from <tt class="docutils literal">DBo</tt>. <li><span class="cb">PyTypeManagerDBo&lt;CppT&gt;</span>
For example <tt class="docutils literal">Box</tt> or <tt class="docutils literal">Parameter</tt>.</p> Template for C++ classes that <em>are</em> not derived from <tt class="docutils literal">DBo</tt>.
</li> For example <tt class="docutils literal">Cell</tt> or <tt class="docutils literal">Instance</tt>.</li>
<li><p class="first"><span class="cb">PyTypeManagerDBo&lt;CppT&gt;</span></p> <li><span class="cb">PyTypeManagerDerivedDBo&lt;CppT,BaseT&gt;</span>
<p>Template for C++ classes that <em>are</em> not derived from <tt class="docutils literal">DBo</tt>. Template for a <tt class="docutils literal">CppT</tt> class derived derived from a <tt class="docutils literal">BaseT</tt> class.
For example <tt class="docutils literal">Cell</tt> or <tt class="docutils literal">Instance</tt>.</p> <tt class="docutils literal">CppT</tt> doesn'y need to be a direct derived of <tt class="docutils literal">BaseT</tt>. <tt class="docutils literal">BaseT</tt>
</li> needs to derive from <tt class="docutils literal">DBo</tt></li>
<li><p class="first"><span class="cb">PyTypeManagerVector&lt;CppT&gt;</span>, template for C++ <tt class="docutils literal"><span class="pre">std::vector&lt;CppT*&gt;</span></tt>.</p> <li><span class="cb">PyTypeManagerVector&lt;CppT&gt;</span>, template for C++ <tt class="docutils literal"><span class="pre">std::vector&lt;CppT*&gt;</span></tt>.</li>
</li> <li><span class="cb">PyTypeManagerVectorIterator&lt;CppT&gt;</span>
<li><p class="first"><span class="cb">PyTypeManagerVectorIterator&lt;CppT&gt;</span></p> Template for C++ <tt class="docutils literal"><span class="pre">std::vector&lt;CppT*&gt;::iterator</span></tt>, automatically created
<p>Template for C++ <tt class="docutils literal"><span class="pre">std::vector&lt;CppT*&gt;::iterator</span></tt>, automatically created from the vector registration.</li>
from the vector registration.</p> <li><span class="cb">PyTypeManagerMap&lt;CppK,CppT&gt;</span>, template for C++ <tt class="docutils literal"><span class="pre">std::map&lt;CppK*,CppT*&gt;</span></tt>.</li>
</li> <li><span class="cb">PyTypeManagerMapIterator&lt;CppK,CppT&gt;</span>
<li><p class="first"><span class="cb">PyTypeManagerMap&lt;CppK,CppT&gt;</span>, template for C++ <tt class="docutils literal"><span class="pre">std::map&lt;CppK*,CppT*&gt;</span></tt>.</p> Template for C++ <tt class="docutils literal"><span class="pre">std::vector&lt;CppK*,CppT*&gt;::iterator</span></tt>, automatically created
</li> from the map registration.</li>
<li><p class="first"><span class="cb">PyTypeManagerMapIterator&lt;CppK,CppT&gt;</span></p> <li><span class="cb">PyTypeManagerCollection&lt;,CppT&gt;</span>, template for C++ <tt class="docutils literal"><span class="pre">Hurricane::Collection&lt;CppT*&gt;</span></tt>.</li>
<p>Template for C++ <tt class="docutils literal"><span class="pre">std::vector&lt;CppK*,CppT*&gt;::iterator</span></tt>, automatically created <li><span class="cb">PyTypeManagerCollectionIterator&lt;,CppT&gt;</span>
from the map registration.</p> Template for C++ <tt class="docutils literal"><span class="pre">Hurricane::Locator&lt;CppT*&gt;</span></tt>, automatically created from
</li> the collection registration.</li>
<li><p class="first"><span class="cb">PyTypeManagerCollection&lt;,CppT&gt;</span>, template for C++ <tt class="docutils literal"><span class="pre">Hurricane::Collection&lt;CppT*&gt;</span></tt>.</p>
</li>
<li><p class="first"><span class="cb">PyTypeManagerCollectionIterator&lt;,CppT&gt;</span></p>
<p>Template for C++ <tt class="docutils literal"><span class="pre">Hurricane::Locator&lt;CppT*&gt;</span></tt>, automatically created from
the collection registration.</p>
</li>
</ul> </ul>
</li> </li>
</ul> </ul>
@ -304,7 +328,7 @@ the collection registration.</p>
<p>Functions of a <tt class="docutils literal">PyTypeObject</tt> like the <em>tp</em> methods (<tt class="docutils literal">tp_alloc</tt>, <tt class="docutils literal">tp_print</tt>, <p>Functions of a <tt class="docutils literal">PyTypeObject</tt> like the <em>tp</em> methods (<tt class="docutils literal">tp_alloc</tt>, <tt class="docutils literal">tp_print</tt>,
<tt class="docutils literal">tp_hash</tt>, ...) must have a C-linkage. So we create <em>one</em> function per slot that <tt class="docutils literal">tp_hash</tt>, ...) must have a C-linkage. So we create <em>one</em> function per slot that
we want to use, set that <em>same</em> function for all the created <tt class="docutils literal">PyTypeObject</tt>, and we want to use, set that <em>same</em> function for all the created <tt class="docutils literal">PyTypeObject</tt>, and
perform a dispact in it. The drawback is that for each access we have to perform perform a dispacth in it. The drawback is that for each access we have to perform
a map lookup. Hope it is fast.</p> a map lookup. Hope it is fast.</p>
<p>Excerpt from the code:</p> <p>Excerpt from the code:</p>
<div class="highlight"><pre><span></span><span class="k">namespace</span> <span class="n">Isobar3</span> <span class="p">{</span> <div class="highlight"><pre><span></span><span class="k">namespace</span> <span class="n">Isobar3</span> <span class="p">{</span>
@ -315,7 +339,7 @@ a map lookup. Hope it is fast.</p>
<span class="k">extern</span> <span class="kt">long</span> <span class="n">_tpHash</span> <span class="p">(</span> <span class="n">PyObject</span><span class="o">*</span> <span class="n">self</span> <span class="p">)</span> <span class="k">extern</span> <span class="kt">long</span> <span class="n">_tpHash</span> <span class="p">(</span> <span class="n">PyObject</span><span class="o">*</span> <span class="n">self</span> <span class="p">)</span>
<span class="p">{</span> <span class="p">{</span>
<span class="c1">// Dispatch towards the relevant class, based on ob_type pointer.</span> <span class="c1">// Dispatch towards the relevant class, based on ob_type pointer.</span>
<span class="k">return</span> <span class="n">PyTypeManager</span><span class="o">::</span><span class="n">get</span><span class="p">(</span> <span class="n">self</span><span class="o">-&gt;</span><span class="n">ob_type</span> <span class="p">)</span><span class="o">-&gt;</span><span class="n">_getTpHash</span><span class="p">(</span> <span class="n">asVPtr</span><span class="p">(</span><span class="n">self</span><span class="p">)</span> <span class="p">);</span> <span class="k">return</span> <span class="n">PyTypeManager</span><span class="o">::</span><span class="n">get</span><span class="p">(</span> <span class="n">Py_TYPE</span><span class="p">(</span><span class="n">self</span><span class="p">)</span><span class="o">-&gt;</span><span class="n">_getTpHash</span><span class="p">(</span> <span class="n">self</span> <span class="p">);</span>
<span class="p">}</span> <span class="p">}</span>
<span class="p">}</span> <span class="p">}</span>
@ -324,7 +348,7 @@ a map lookup. Hope it is fast.</p>
<span class="k">public</span><span class="o">:</span> <span class="k">public</span><span class="o">:</span>
<span class="kt">void</span> <span class="n">PyTypeManager</span><span class="o">::</span><span class="n">_setupPyType</span> <span class="p">()</span> <span class="kt">void</span> <span class="n">PyTypeManager</span><span class="o">::</span><span class="n">_setupPyType</span> <span class="p">()</span>
<span class="c1">// Derived classes must implement it as they see fit.</span> <span class="c1">// Derived classes must implement it as they see fit.</span>
<span class="k">virtual</span> <span class="kt">long</span> <span class="n">_getTpHash</span> <span class="p">(</span> <span class="n">PyVoidPointer</span><span class="o">*</span> <span class="p">)</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="k">virtual</span> <span class="kt">long</span> <span class="n">_getTpHash</span> <span class="p">(</span> <span class="n">PyObject</span><span class="o">*</span> <span class="p">)</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
<span class="k">template</span><span class="o">&lt;</span><span class="k">typename</span> <span class="n">CppT</span><span class="o">&gt;</span> <span class="k">template</span><span class="o">&lt;</span><span class="k">typename</span> <span class="n">CppT</span><span class="o">&gt;</span>
<span class="k">static</span> <span class="n">PyTypeManager</span><span class="o">*</span> <span class="n">_get</span><span class="p">();</span> <span class="k">static</span> <span class="n">PyTypeManager</span><span class="o">*</span> <span class="n">_get</span><span class="p">();</span>
<span class="k">private</span><span class="o">:</span> <span class="k">private</span><span class="o">:</span>
@ -349,57 +373,94 @@ a map lookup. Hope it is fast.</p>
</div> </div>
<div class="section" id="going-from-python-to-c"> <div class="section" id="going-from-python-to-c">
<h3><a class="toc-backref" href="#id8">2.3 Going From Python to C++</a></h3> <h3><a class="toc-backref" href="#id8">2.3 Going From Python to C++</a></h3>
<p>To convert a C++ object (pointer) into a Python object, a set of <p>To convert a C++ object (pointer) into a Python object, a mix of
<span class="cb">pyAs&lt;&gt;</span> templates functions are supplieds.</p> <span class="cb">pyToC&lt;&gt;()</span> templates functions and real functions are supplieds.</p>
<p>Templates functions are used for all types/classes managed through
the <tt class="docutils literal">PyTypeManger</tt>. They come in two flavors:</p>
<ol class="arabic simple" start="2">
<li><strong>Value as pointer version</strong> (C++ argment type is <tt class="docutils literal">T*</tt>):
The encapsulated C++ object is still a pointer,
but to a <em>stand-alone</em> one which has been created for the sole
purpose of this <tt class="docutils literal">PyObject</tt>. Typical example is the <tt class="docutils literal">Box</tt>.
Then, we <em>copy by value</em> the contents of the pointed object into
the contents of the pointer argument that we where given.</li>
<li><strong>Pointer version</strong> (C++ argument type is <tt class="docutils literal">T**</tt>):
The encapsulated C++ object is truly a pointer
to an element of the data-structure, then we just extract the
C++ pointer value.</li>
</ol>
<p>Normal function overload are used for <tt class="docutils literal">POD</tt> types (<tt class="docutils literal">bool</tt>, <tt class="docutils literal">int</tt>,
<tt class="docutils literal">long</tt>, <tt class="docutils literal">double</tt>, ...) and basic <tt class="docutils literal">STL</tt> types (<tt class="docutils literal"><span class="pre">std::string</span></tt>, ...).</p>
<p>Specialization for all POD type that can be directly translated into <p>Specialization for all POD type that can be directly translated into
Python types must be provideds (<tt class="docutils literal">bool</tt>, <tt class="docutils literal">int</tt>, <tt class="docutils literal">long</tt>, <tt class="docutils literal">double</tt>, Python types must be provideds (<tt class="docutils literal">bool</tt>, <tt class="docutils literal">int</tt>, <tt class="docutils literal">long</tt>, <tt class="docutils literal">double</tt>,
<tt class="docutils literal"><span class="pre">std::string</span></tt>, ...).</p> <tt class="docutils literal"><span class="pre">std::string</span></tt>, ...).</p>
<p>Partial specialization for type availables through the <tt class="docutils literal">PyTypeManager</tt> <p>Those templates/functions are the ones the <tt class="docutils literal"><span class="pre">Isobar::parse_objects()</span></tt> recursive
is supplied. It checks the manager for the type's existence.</p> template function call in turn for each <tt class="docutils literal">PyObject*</tt> argument.</p>
<div class="highlight"><pre><span></span><span class="c1">// Most generic template, should *not* be used.</span> <div class="note">
<span class="c1">// So issue an error message and report a failed conversion.</span> <p class="first admonition-title">Note</p>
<span class="k">template</span><span class="o">&lt;</span><span class="k">typename</span> <span class="n">T</span><span class="o">&gt;</span> <p class="last"><tt class="docutils literal"><span class="pre">Hurricane::Name</span></tt> are <em>not</em> exposed to the Python interface, they
<span class="kr">inline</span> <span class="kt">bool</span> <span class="n">pyAs</span> <span class="p">(</span> <span class="n">PyObject</span><span class="o">*</span> <span class="n">pyArg</span><span class="p">,</span> <span class="n">T</span><span class="o">&amp;</span> <span class="n">arg</span> <span class="p">)</span> must be treated as <tt class="docutils literal"><span class="pre">std::string</span></tt>.</p>
</div>
<div class="highlight"><pre><span></span><span class="c1">// Template/Pointer to a value flavor.</span>
<span class="k">template</span><span class="o">&lt;</span> <span class="k">typename</span> <span class="n">T</span>
<span class="p">,</span> <span class="k">typename</span> <span class="n">std</span><span class="o">::</span><span class="n">enable_if</span><span class="o">&lt;</span> <span class="o">!</span><span class="n">std</span><span class="o">::</span><span class="n">is_pointer</span><span class="o">&lt;</span><span class="n">T</span><span class="o">&gt;::</span><span class="n">value</span><span class="p">,</span> <span class="kt">bool</span> <span class="o">&gt;::</span><span class="n">type</span> <span class="o">=</span> <span class="nb">true</span> <span class="o">&gt;</span>
<span class="kr">inline</span> <span class="kt">bool</span> <span class="n">pyToC</span> <span class="p">(</span> <span class="n">PyObject</span><span class="o">*</span> <span class="n">pyArg</span><span class="p">,</span> <span class="n">T</span><span class="o">*</span> <span class="n">arg</span> <span class="p">)</span>
<span class="p">{</span> <span class="p">{</span>
<span class="n">std</span><span class="o">::</span><span class="n">cerr</span> <span class="o">&lt;&lt;</span> <span class="s">&quot;Isobar3.pyAs&lt;T&gt;(): Unsupported type </span><span class="se">\&quot;</span><span class="s">&quot;</span> <span class="k">typedef</span> <span class="k">typename</span> <span class="n">std</span><span class="o">::</span><span class="n">remove_cv</span><span class="o">&lt;</span><span class="n">T</span><span class="o">&gt;::</span><span class="n">type</span> <span class="n">NonConstT</span><span class="p">;</span>
<span class="o">&lt;&lt;</span> <span class="n">demangle</span><span class="p">(</span><span class="k">typeid</span><span class="p">(</span><span class="n">T</span><span class="p">).</span><span class="n">name</span><span class="p">())</span> <span class="o">&lt;&lt;</span> <span class="s">&quot;</span><span class="se">\&quot;</span><span class="s">&quot;</span> <span class="o">&lt;&lt;</span> <span class="n">endl</span><span class="p">;</span> <span class="n">Isobar3</span><span class="o">::</span><span class="n">PyTypeManager</span><span class="o">*</span> <span class="n">manager</span> <span class="o">=</span> <span class="n">Isobar3</span><span class="o">::</span><span class="n">PyTypeManager</span><span class="o">::</span><span class="n">_get</span><span class="o">&lt;</span><span class="n">T</span><span class="o">&gt;</span><span class="p">();</span>
<span class="k">return</span> <span class="nb">false</span><span class="p">;</span> <span class="k">if</span> <span class="p">(</span><span class="n">not</span> <span class="n">manager</span><span class="p">)</span> <span class="p">{</span>
<span class="n">std</span><span class="o">::</span><span class="n">cerr</span> <span class="o">&lt;&lt;</span> <span class="s">&quot;Isobar3::pyToC&lt;&gt;(const T*): Unsupported type.&quot;</span> <span class="o">&lt;&lt;</span> <span class="n">std</span><span class="o">::</span><span class="n">endl</span><span class="p">;</span>
<span class="k">return</span> <span class="nb">false</span><span class="p">;</span>
<span class="p">}</span>
<span class="k">if</span> <span class="p">(</span><span class="n">Py_TYPE</span><span class="p">(</span><span class="n">pyArg</span><span class="p">)</span> <span class="o">!=</span> <span class="n">manager</span><span class="o">-&gt;</span><span class="n">_getTypeObject</span><span class="p">())</span> <span class="k">return</span> <span class="nb">false</span><span class="p">;</span>
<span class="o">*</span><span class="p">(</span><span class="k">const_cast</span><span class="o">&lt;</span> <span class="n">NonConstT</span><span class="o">*</span> <span class="o">&gt;</span><span class="p">(</span><span class="n">arg</span><span class="p">))</span> <span class="o">=</span> <span class="o">*</span><span class="p">((</span> <span class="n">T</span><span class="o">*</span> <span class="p">)(</span> <span class="n">Isobar3</span><span class="o">::</span><span class="n">object1</span><span class="p">(</span> <span class="n">pyArg</span> <span class="p">)));</span>
<span class="k">return</span> <span class="nb">true</span><span class="p">;</span>
<span class="p">}</span> <span class="p">}</span>
<span class="c1">// Template specialization for POD type &quot;int&quot;.</span> <span class="c1">// Template/Pointer to a pointer flavor.</span>
<span class="k">template</span><span class="o">&lt;&gt;</span> <span class="k">template</span><span class="o">&lt;</span><span class="k">typename</span> <span class="n">T</span><span class="o">&gt;</span>
<span class="kr">inline</span> <span class="kt">bool</span> <span class="n">pyAs</span> <span class="p">(</span> <span class="n">PyObject</span><span class="o">*</span> <span class="n">pyArg</span><span class="p">,</span> <span class="kt">int</span><span class="o">&amp;</span> <span class="n">arg</span> <span class="p">)</span> <span class="kr">inline</span> <span class="kt">bool</span> <span class="n">pyToC</span> <span class="p">(</span> <span class="n">PyObject</span><span class="o">*</span> <span class="n">pyArg</span><span class="p">,</span> <span class="n">T</span><span class="o">**</span> <span class="n">arg</span> <span class="p">)</span>
<span class="p">{</span> <span class="p">{</span>
<span class="k">if</span> <span class="p">(</span><span class="n">PyInt_Check</span> <span class="p">(</span><span class="n">pyArg</span><span class="p">))</span> <span class="p">{</span> <span class="n">arg</span> <span class="o">=</span> <span class="n">PyInt_AsLong</span> <span class="p">(</span> <span class="n">pyArg</span> <span class="p">);</span> <span class="p">}</span> <span class="n">Isobar3</span><span class="o">::</span><span class="n">PyTypeManager</span><span class="o">*</span> <span class="n">manager</span> <span class="o">=</span> <span class="n">Isobar3</span><span class="o">::</span><span class="n">PyTypeManager</span><span class="o">::</span><span class="n">_get</span><span class="o">&lt;</span><span class="n">T</span><span class="o">&gt;</span><span class="p">();</span>
<span class="k">else</span> <span class="k">if</span> <span class="p">(</span><span class="n">PyLong_Check</span><span class="p">(</span><span class="n">pyArg</span><span class="p">))</span> <span class="p">{</span> <span class="n">arg</span> <span class="o">=</span> <span class="n">PyLong_AsLong</span><span class="p">(</span> <span class="n">pyArg</span> <span class="p">);</span> <span class="p">}</span> <span class="k">if</span> <span class="p">(</span><span class="n">not</span> <span class="n">manager</span><span class="p">)</span> <span class="p">{</span>
<span class="n">std</span><span class="o">::</span><span class="n">cerr</span> <span class="o">&lt;&lt;</span> <span class="s">&quot;Isobar3::pyToC&lt;T&gt;(T*&amp;): Unsupported type </span><span class="se">\&quot;</span><span class="s">&quot;</span> <span class="o">&lt;&lt;</span> <span class="k">typeid</span><span class="p">(</span><span class="n">T</span><span class="p">).</span><span class="n">name</span><span class="p">()</span> <span class="o">&lt;&lt;</span> <span class="s">&quot;</span><span class="se">\&quot;</span><span class="s">&quot;</span> <span class="o">&lt;&lt;</span> <span class="n">std</span><span class="o">::</span><span class="n">endl</span><span class="p">;</span>
<span class="k">return</span> <span class="nb">false</span><span class="p">;</span>
<span class="p">}</span>
<span class="o">*</span><span class="n">arg</span> <span class="o">=</span> <span class="p">(</span><span class="n">T</span><span class="o">*</span><span class="p">)(</span> <span class="n">Isobar3</span><span class="o">::</span><span class="n">object1</span><span class="p">(</span> <span class="n">pyArg</span> <span class="p">));</span>
<span class="k">return</span> <span class="nb">true</span><span class="p">;</span>
<span class="p">}</span>
<span class="c1">// True function overload for std::string.</span>
<span class="kr">inline</span> <span class="kt">bool</span> <span class="n">pyToC</span> <span class="p">(</span> <span class="n">PyObject</span><span class="o">*</span> <span class="n">pyArg</span><span class="p">,</span> <span class="n">std</span><span class="o">::</span><span class="n">string</span><span class="o">*</span> <span class="n">arg</span> <span class="p">)</span>
<span class="p">{</span>
<span class="k">if</span> <span class="p">(</span><span class="n">not</span> <span class="n">PyUnicode_Check</span><span class="p">(</span><span class="n">pyArg</span><span class="p">))</span> <span class="k">return</span> <span class="nb">false</span><span class="p">;</span>
<span class="n">PyObject</span><span class="o">*</span> <span class="n">pyBytes</span> <span class="o">=</span> <span class="n">PyUnicode_AsASCIIString</span><span class="p">(</span> <span class="n">pyArg</span> <span class="p">);</span>
<span class="o">*</span><span class="n">arg</span> <span class="o">=</span> <span class="n">PyBytes_AsString</span><span class="p">(</span> <span class="n">pyBytes</span> <span class="p">);</span>
<span class="n">Py_DECREF</span><span class="p">(</span> <span class="n">pyBytes</span> <span class="p">);</span>
<span class="k">return</span> <span class="nb">true</span><span class="p">;</span>
<span class="p">}</span>
<span class="c1">// True function overload for bool.</span>
<span class="kr">inline</span> <span class="kt">bool</span> <span class="n">pyToC</span> <span class="p">(</span> <span class="n">PyObject</span><span class="o">*</span> <span class="n">pyArg</span><span class="p">,</span> <span class="kt">bool</span><span class="o">*</span> <span class="n">arg</span> <span class="p">)</span>
<span class="p">{</span>
<span class="k">if</span> <span class="p">(</span><span class="n">not</span> <span class="n">PyBool_Check</span><span class="p">(</span><span class="n">pyArg</span><span class="p">))</span> <span class="k">return</span> <span class="nb">false</span><span class="p">;</span>
<span class="o">*</span><span class="n">arg</span> <span class="o">=</span> <span class="p">(</span><span class="n">pyArg</span> <span class="o">==</span> <span class="n">Py_True</span><span class="p">);</span>
<span class="k">return</span> <span class="nb">true</span><span class="p">;</span>
<span class="p">}</span>
<span class="c1">// True function overload for int.</span>
<span class="kr">inline</span> <span class="kt">bool</span> <span class="n">pyToC</span> <span class="p">(</span> <span class="n">PyObject</span><span class="o">*</span> <span class="n">pyArg</span><span class="p">,</span> <span class="kt">int</span><span class="o">*</span> <span class="n">arg</span> <span class="p">)</span>
<span class="p">{</span>
<span class="k">if</span> <span class="p">(</span><span class="n">PyLong_Check</span><span class="p">(</span><span class="n">pyArg</span><span class="p">))</span> <span class="p">{</span> <span class="o">*</span><span class="n">arg</span> <span class="o">=</span> <span class="n">PyLong_AsLong</span><span class="p">(</span> <span class="n">pyArg</span> <span class="p">);</span> <span class="p">}</span>
<span class="k">else</span> <span class="k">return</span> <span class="nb">false</span><span class="p">;</span> <span class="k">else</span> <span class="k">return</span> <span class="nb">false</span><span class="p">;</span>
<span class="k">return</span> <span class="nb">true</span><span class="p">;</span> <span class="k">return</span> <span class="nb">true</span><span class="p">;</span>
<span class="p">}</span> <span class="p">}</span>
<span class="c1">// Template partial specialization for any type registered in</span>
<span class="c1">// the PyTypeManager.</span>
<span class="k">template</span><span class="o">&lt;</span><span class="k">typename</span> <span class="n">T</span><span class="o">&gt;</span>
<span class="kr">inline</span> <span class="kt">bool</span> <span class="n">pyAs</span> <span class="p">(</span> <span class="n">PyObject</span><span class="o">*</span> <span class="n">pyArg</span><span class="p">,</span> <span class="n">T</span><span class="o">*&amp;</span> <span class="n">arg</span> <span class="p">)</span>
<span class="p">{</span>
<span class="n">PyTypeManager</span><span class="o">*</span> <span class="n">manager</span> <span class="o">=</span> <span class="n">PyTypeManager</span><span class="o">::</span><span class="n">_get</span><span class="o">&lt;</span><span class="n">T</span><span class="o">&gt;</span><span class="p">();</span>
<span class="k">if</span> <span class="p">(</span><span class="n">not</span> <span class="n">manager</span><span class="p">)</span> <span class="p">{</span>
<span class="n">std</span><span class="o">::</span><span class="n">cerr</span> <span class="o">&lt;&lt;</span> <span class="s">&quot;Isobar3.pyAs&lt;T&gt;(): Unsupported type </span><span class="se">\&quot;</span><span class="s">&quot;</span>
<span class="o">&lt;&lt;</span> <span class="k">typeid</span><span class="p">(</span><span class="n">T</span><span class="p">).</span><span class="n">name</span><span class="p">()</span> <span class="o">&lt;&lt;</span> <span class="s">&quot;</span><span class="se">\&quot;</span><span class="s">&quot;</span> <span class="o">&lt;&lt;</span> <span class="n">endl</span><span class="p">;</span>
<span class="k">return</span> <span class="nb">false</span><span class="p">;</span>
<span class="p">}</span>
<span class="n">arg</span> <span class="o">=</span> <span class="p">(</span><span class="n">T</span><span class="o">*</span><span class="p">)(</span> <span class="n">asVPtr</span><span class="p">(</span> <span class="n">pyArg</span> <span class="p">)</span><span class="o">-&gt;</span><span class="n">_object</span> <span class="p">);</span>
<span class="k">return</span> <span class="nb">true</span><span class="p">;</span>
<span class="p">}</span>
<span class="k">template</span><span class="o">&lt;</span><span class="k">typename</span> <span class="n">T</span><span class="o">&gt;</span>
<span class="kr">inline</span> <span class="kt">bool</span> <span class="n">pyAs</span> <span class="p">(</span> <span class="n">PyVoidPointer</span><span class="o">*</span> <span class="n">pyArg</span><span class="p">,</span> <span class="n">T</span><span class="o">*&amp;</span> <span class="n">arg</span> <span class="p">)</span>
<span class="p">{</span> <span class="k">return</span> <span class="n">pyAs</span><span class="p">(</span> <span class="p">(</span><span class="n">PyObject</span><span class="o">*</span><span class="p">)</span><span class="n">pyArg</span><span class="p">,</span> <span class="n">arg</span> <span class="p">);</span> <span class="p">}</span>
</pre></div> </pre></div>
</div> </div>
<div class="section" id="going-from-c-to-python"> <div class="section" id="going-from-c-to-python">
<h3><a class="toc-backref" href="#id9">2.4 Going From C++ to Python</a></h3> <h3><a class="toc-backref" href="#id9">2.4 Going From C++ to Python</a></h3>
<p>To convert a Python object into a C++ object, a set of <p>To convert a Python object into a C++ object, a set of
<span class="cb">objectLink&lt;&gt;</span> templates functions are supplieds.</p> <span class="cb">cToPy&lt;&gt;</span> templates functions are supplieds.</p>
<p>We completely disable the partially specialized templates for <p>We completely disable the partially specialized templates for
objects that are non-POD as the compiler seems to be unable to objects that are non-POD as the compiler seems to be unable to
choose the fully specialized template in this case (or I still choose the fully specialized template in this case (or I still
@ -414,45 +475,64 @@ method.</p>
<strong>why</strong> we need the intermediate <tt class="docutils literal">PyTypeManagerVTrunk&lt;CppT&gt;</tt> <strong>why</strong> we need the intermediate <tt class="docutils literal">PyTypeManagerVTrunk&lt;CppT&gt;</tt>
template class.</p> template class.</p>
</div> </div>
<div class="highlight"><pre><span></span><span class="c1">// The most generic template, should *not* be used. So raise a Python exception.</span> <div class="note">
<p class="first admonition-title">Note</p>
<p><strong>Different C++ templates.</strong> You may notice that the two following templates
may look like specializations of the same one:</p>
<ul class="simple">
<li><tt class="docutils literal">template&lt;typename CppT&gt; PyObject* cToPy ( CppT&nbsp; object )</tt></li>
<li><tt class="docutils literal">template&lt;typename CppT&gt; PyObject* cToPy ( CppT* object )</tt></li>
</ul>
<p class="last">Which would be illegal (function templates are not allowed to have <em>partial</em>
specialization), but they are <em>not</em>. The two pairs
<tt class="docutils literal">(template parameter,function parameter)</tt>, that is <tt class="docutils literal">(CppT,CppT)</tt> and
<tt class="docutils literal">(CppT,CppT*)</tt> cannot be made to be a specialization of each other.</p>
</div>
<div class="highlight"><pre><span></span><span class="c1">// Generic template for values.</span>
<span class="k">template</span><span class="o">&lt;</span> <span class="k">typename</span> <span class="n">CppT</span> <span class="o">&gt;</span> <span class="k">template</span><span class="o">&lt;</span> <span class="k">typename</span> <span class="n">CppT</span> <span class="o">&gt;</span>
<span class="kr">inline</span> <span class="n">PyObject</span><span class="o">*</span> <span class="n">objectLink</span> <span class="p">(</span> <span class="n">CppT</span> <span class="n">object</span> <span class="p">)</span> <span class="kr">inline</span> <span class="n">PyObject</span><span class="o">*</span> <span class="n">cToPy</span> <span class="p">(</span> <span class="n">CppT</span> <span class="n">object</span> <span class="p">)</span>
<span class="p">{</span> <span class="p">{</span>
<span class="n">std</span><span class="o">::</span><span class="n">string</span> <span class="n">message</span> <span class="o">=</span> <span class="s">&quot;Overload for Isobar3::objectLink&lt; &quot;</span> <span class="k">if</span> <span class="p">(</span><span class="n">not</span> <span class="n">Isobar3</span><span class="o">::</span><span class="n">PyTypeManager</span><span class="o">::</span><span class="n">hasType</span><span class="o">&lt;</span><span class="n">CppT</span><span class="o">&gt;</span><span class="p">())</span> <span class="p">{</span>
<span class="o">+</span> <span class="n">demangle</span><span class="p">(</span><span class="k">typeid</span><span class="p">(</span><span class="n">CppT</span><span class="p">).</span><span class="n">name</span><span class="p">())</span> <span class="o">+</span> <span class="s">&quot; &gt;() is missing.&quot;</span><span class="p">;</span> <span class="n">std</span><span class="o">::</span><span class="n">string</span> <span class="n">message</span> <span class="o">=</span> <span class="s">&quot;Overload for Isobar3::cToPy&lt; &quot;</span>
<span class="n">PyErr_SetString</span><span class="p">(</span> <span class="n">HurricaneError</span><span class="p">,</span> <span class="n">message</span><span class="p">.</span><span class="n">c_str</span><span class="p">()</span> <span class="p">);</span> <span class="o">+</span> <span class="n">Hurricane</span><span class="o">::</span><span class="n">demangle</span><span class="p">(</span><span class="k">typeid</span><span class="p">(</span><span class="n">CppT</span><span class="p">).</span><span class="n">name</span><span class="p">())</span> <span class="o">+</span> <span class="s">&quot; &gt;() Type not registered in the manager.&quot;</span><span class="p">;</span>
<span class="k">return</span> <span class="nb">NULL</span><span class="p">;</span> <span class="n">PyErr_SetString</span><span class="p">(</span> <span class="n">Isobar3</span><span class="o">::</span><span class="n">HurricaneError</span><span class="p">,</span> <span class="n">message</span><span class="p">.</span><span class="n">c_str</span><span class="p">()</span> <span class="p">);</span>
<span class="k">return</span> <span class="nb">NULL</span><span class="p">;</span>
<span class="p">}</span>
<span class="k">return</span> <span class="n">Isobar3</span><span class="o">::</span><span class="n">PyTypeManager</span><span class="o">::</span><span class="n">link</span><span class="o">&lt;</span><span class="n">CppT</span><span class="o">&gt;</span><span class="p">(</span> <span class="k">new</span> <span class="n">CppT</span> <span class="p">(</span><span class="n">object</span><span class="p">)</span> <span class="p">);</span>
<span class="p">}</span> <span class="p">}</span>
<span class="c1">// Disable this template if &quot;CppT&quot; is an already supported type.</span> <span class="c1">// Disabled for POD &amp; STL types, pointer flavor.</span>
<span class="k">template</span><span class="o">&lt;</span> <span class="k">typename</span> <span class="n">CppT</span> <span class="k">template</span><span class="o">&lt;</span> <span class="k">typename</span> <span class="n">CppT</span>
<span class="p">,</span> <span class="k">typename</span> <span class="n">std</span><span class="o">::</span><span class="n">enable_if</span><span class="o">&lt;</span> <span class="o">!</span><span class="n">std</span><span class="o">::</span><span class="n">is_same</span><span class="o">&lt;</span><span class="n">CppT</span><span class="p">,</span><span class="kt">bool</span><span class="o">&gt;::</span><span class="n">value</span> <span class="p">,</span> <span class="k">typename</span> <span class="n">std</span><span class="o">::</span><span class="n">enable_if</span><span class="o">&lt;</span> <span class="o">!</span><span class="n">std</span><span class="o">::</span><span class="n">is_same</span><span class="o">&lt;</span><span class="n">CppT</span><span class="p">,</span><span class="kt">bool</span><span class="o">&gt;::</span><span class="n">value</span>
<span class="o">&amp;&amp;</span> <span class="o">!</span><span class="n">std</span><span class="o">::</span><span class="n">is_same</span><span class="o">&lt;</span><span class="n">CppT</span><span class="p">,</span><span class="kt">int</span> <span class="o">&gt;::</span><span class="n">value</span> <span class="o">&amp;&amp;</span> <span class="o">!</span><span class="n">std</span><span class="o">::</span><span class="n">is_same</span><span class="o">&lt;</span><span class="n">CppT</span><span class="p">,</span><span class="kt">int</span> <span class="o">&gt;::</span><span class="n">value</span>
<span class="o">&amp;&amp;</span> <span class="o">!</span><span class="n">std</span><span class="o">::</span><span class="n">is_same</span><span class="o">&lt;</span><span class="n">CppT</span><span class="p">,</span><span class="n">std</span><span class="o">::</span><span class="n">string</span><span class="o">&gt;::</span><span class="n">value</span> <span class="o">&amp;&amp;</span> <span class="o">!</span><span class="n">std</span><span class="o">::</span><span class="n">is_same</span><span class="o">&lt;</span><span class="n">CppT</span><span class="p">,</span><span class="n">std</span><span class="o">::</span><span class="n">string</span><span class="o">&gt;::</span><span class="n">value</span>
<span class="o">&amp;&amp;</span> <span class="o">!</span><span class="n">std</span><span class="o">::</span><span class="n">is_same</span><span class="o">&lt;</span><span class="n">CppT</span><span class="p">,</span><span class="k">const</span> <span class="n">std</span><span class="o">::</span><span class="n">string</span><span class="o">&gt;::</span><span class="n">value</span> <span class="o">&amp;&amp;</span> <span class="o">!</span><span class="n">std</span><span class="o">::</span><span class="n">is_same</span><span class="o">&lt;</span><span class="n">CppT</span><span class="p">,</span><span class="k">const</span> <span class="n">std</span><span class="o">::</span><span class="n">string</span><span class="o">&gt;::</span><span class="n">value</span><span class="p">,</span><span class="kt">bool</span><span class="o">&gt;::</span><span class="n">type</span> <span class="o">=</span> <span class="nb">true</span> <span class="o">&gt;</span>
<span class="p">,</span><span class="kt">bool</span><span class="o">&gt;::</span><span class="n">type</span> <span class="o">=</span> <span class="nb">true</span> <span class="o">&gt;</span> <span class="kr">inline</span> <span class="n">PyObject</span><span class="o">*</span> <span class="n">cToPy</span> <span class="p">(</span> <span class="n">CppT</span><span class="o">*</span> <span class="n">object</span> <span class="p">)</span>
<span class="kr">inline</span> <span class="n">PyObject</span><span class="o">*</span> <span class="n">objectLink</span> <span class="p">(</span> <span class="n">CppT</span><span class="o">*</span> <span class="n">object</span> <span class="p">)</span> <span class="p">{</span> <span class="k">return</span> <span class="n">Isobar3</span><span class="o">::</span><span class="n">PyTypeManager</span><span class="o">::</span><span class="n">link</span><span class="o">&lt;</span><span class="n">CppT</span><span class="o">&gt;</span><span class="p">(</span> <span class="n">object</span> <span class="p">);</span> <span class="p">}</span>
<span class="p">{</span> <span class="k">return</span> <span class="n">PyTypeManager</span><span class="o">::</span><span class="n">link</span><span class="o">&lt;</span><span class="n">CppT</span><span class="o">&gt;</span><span class="p">(</span> <span class="n">object</span> <span class="p">);</span> <span class="p">}</span>
<span class="c1">// Disable this template if &quot;CppT&quot; is an already supported type.</span> <span class="c1">// Disabled for POD &amp; STL types, const pointer flavor.</span>
<span class="k">template</span><span class="o">&lt;</span> <span class="k">typename</span> <span class="n">CppT</span> <span class="k">template</span><span class="o">&lt;</span> <span class="k">typename</span> <span class="n">CppT</span>
<span class="p">,</span> <span class="k">typename</span> <span class="n">std</span><span class="o">::</span><span class="n">enable_if</span><span class="o">&lt;</span> <span class="o">!</span><span class="n">std</span><span class="o">::</span><span class="n">is_same</span><span class="o">&lt;</span><span class="n">CppT</span><span class="p">,</span><span class="kt">bool</span><span class="o">&gt;::</span><span class="n">value</span> <span class="p">,</span> <span class="k">typename</span> <span class="n">std</span><span class="o">::</span><span class="n">enable_if</span><span class="o">&lt;</span> <span class="o">!</span><span class="n">std</span><span class="o">::</span><span class="n">is_same</span><span class="o">&lt;</span><span class="n">CppT</span><span class="p">,</span><span class="kt">bool</span><span class="o">&gt;::</span><span class="n">value</span>
<span class="o">&amp;&amp;</span> <span class="o">!</span><span class="n">std</span><span class="o">::</span><span class="n">is_same</span><span class="o">&lt;</span><span class="n">CppT</span><span class="p">,</span><span class="kt">int</span> <span class="o">&gt;::</span><span class="n">value</span> <span class="o">&amp;&amp;</span> <span class="o">!</span><span class="n">std</span><span class="o">::</span><span class="n">is_same</span><span class="o">&lt;</span><span class="n">CppT</span><span class="p">,</span><span class="kt">int</span> <span class="o">&gt;::</span><span class="n">value</span>
<span class="o">&amp;&amp;</span> <span class="o">!</span><span class="n">std</span><span class="o">::</span><span class="n">is_same</span><span class="o">&lt;</span><span class="n">CppT</span><span class="p">,</span><span class="n">std</span><span class="o">::</span><span class="n">string</span><span class="o">&gt;::</span><span class="n">value</span> <span class="o">&amp;&amp;</span> <span class="o">!</span><span class="n">std</span><span class="o">::</span><span class="n">is_same</span><span class="o">&lt;</span><span class="n">CppT</span><span class="p">,</span><span class="n">std</span><span class="o">::</span><span class="n">string</span><span class="o">&gt;::</span><span class="n">value</span>
<span class="o">&amp;&amp;</span> <span class="o">!</span><span class="n">std</span><span class="o">::</span><span class="n">is_same</span><span class="o">&lt;</span><span class="n">CppT</span><span class="p">,</span><span class="k">const</span> <span class="n">std</span><span class="o">::</span><span class="n">string</span><span class="o">&gt;::</span><span class="n">value</span> <span class="o">&amp;&amp;</span> <span class="o">!</span><span class="n">std</span><span class="o">::</span><span class="n">is_same</span><span class="o">&lt;</span><span class="n">CppT</span><span class="p">,</span><span class="k">const</span> <span class="n">std</span><span class="o">::</span><span class="n">string</span><span class="o">&gt;::</span><span class="n">value</span><span class="p">,</span><span class="kt">bool</span><span class="o">&gt;::</span><span class="n">type</span> <span class="o">=</span> <span class="nb">true</span> <span class="o">&gt;</span>
<span class="p">,</span><span class="kt">bool</span><span class="o">&gt;::</span><span class="n">type</span> <span class="o">=</span> <span class="nb">true</span> <span class="o">&gt;</span> <span class="kr">inline</span> <span class="n">PyObject</span><span class="o">*</span> <span class="n">cToPy</span> <span class="p">(</span> <span class="k">const</span> <span class="n">CppT</span><span class="o">*</span> <span class="n">object</span> <span class="p">)</span>
<span class="kr">inline</span> <span class="n">PyObject</span><span class="o">*</span> <span class="n">objectLink</span> <span class="p">(</span> <span class="k">const</span> <span class="n">CppT</span><span class="o">*</span> <span class="n">object</span> <span class="p">)</span> <span class="p">{</span> <span class="k">return</span> <span class="n">Isobar3</span><span class="o">::</span><span class="n">PyTypeManager</span><span class="o">::</span><span class="n">link</span><span class="o">&lt;</span><span class="n">CppT</span><span class="o">&gt;</span><span class="p">(</span> <span class="k">const_cast</span><span class="o">&lt;</span><span class="n">CppT</span><span class="o">*&gt;</span><span class="p">(</span> <span class="n">object</span> <span class="p">));</span> <span class="p">}</span>
<span class="p">{</span> <span class="k">return</span> <span class="n">PyTypeManager</span><span class="o">::</span><span class="n">link</span><span class="o">&lt;</span><span class="n">CppT</span><span class="o">&gt;</span><span class="p">(</span> <span class="k">const_cast</span><span class="o">&lt;</span><span class="n">CppT</span><span class="o">*&gt;</span><span class="p">(</span> <span class="n">object</span> <span class="p">));</span> <span class="p">}</span>
<span class="c1">// Specializations for POD type &quot;int&quot; .</span> <span class="c1">// Specialization for booleans.</span>
<span class="k">template</span><span class="o">&lt;&gt;</span> <span class="kr">inline</span> <span class="n">PyObject</span><span class="o">*</span> <span class="n">objectLink</span><span class="o">&lt;</span><span class="kt">int</span> <span class="o">&gt;</span> <span class="p">(</span> <span class="kt">int</span> <span class="n">i</span> <span class="p">)</span> <span class="k">template</span><span class="o">&lt;&gt;</span>
<span class="p">{</span> <span class="k">return</span> <span class="n">PyInt_FromLong</span><span class="p">(</span> <span class="n">i</span> <span class="p">);</span> <span class="p">}</span> <span class="kr">inline</span> <span class="n">PyObject</span><span class="o">*</span> <span class="n">cToPy</span><span class="o">&lt;</span><span class="kt">bool</span><span class="o">&gt;</span> <span class="p">(</span> <span class="kt">bool</span> <span class="n">b</span> <span class="p">)</span>
<span class="p">{</span> <span class="k">if</span> <span class="p">(</span><span class="n">b</span><span class="p">)</span> <span class="n">Py_RETURN_TRUE</span><span class="p">;</span> <span class="n">Py_RETURN_FALSE</span><span class="p">;</span> <span class="p">}</span>
<span class="k">template</span><span class="o">&lt;&gt;</span> <span class="kr">inline</span> <span class="n">PyObject</span><span class="o">*</span> <span class="n">objectLink</span><span class="o">&lt;</span><span class="k">const</span> <span class="kt">int</span> <span class="o">&gt;</span> <span class="p">(</span> <span class="k">const</span> <span class="kt">int</span> <span class="n">i</span> <span class="p">)</span> <span class="c1">// Specialization for STL std::string.</span>
<span class="p">{</span> <span class="k">return</span> <span class="n">PyInt_FromLong</span><span class="p">(</span> <span class="n">i</span> <span class="p">);</span> <span class="p">}</span> <span class="k">template</span><span class="o">&lt;&gt;</span> <span class="kr">inline</span> <span class="n">PyObject</span><span class="o">*</span> <span class="n">cToPy</span><span class="o">&lt;</span> <span class="n">std</span><span class="o">::</span><span class="n">string</span><span class="o">&gt;</span> <span class="p">(</span> <span class="n">std</span><span class="o">::</span><span class="n">string</span> <span class="n">s</span> <span class="p">)</span> <span class="p">{</span> <span class="k">return</span> <span class="n">PyUnicode_FromString</span><span class="p">(</span> <span class="n">s</span><span class="p">.</span><span class="n">c_str</span><span class="p">()</span> <span class="p">);</span> <span class="p">}</span>
<span class="k">template</span><span class="o">&lt;&gt;</span> <span class="kr">inline</span> <span class="n">PyObject</span><span class="o">*</span> <span class="n">cToPy</span><span class="o">&lt;</span><span class="k">const</span> <span class="n">std</span><span class="o">::</span><span class="n">string</span> <span class="o">&gt;</span> <span class="p">(</span> <span class="k">const</span> <span class="n">std</span><span class="o">::</span><span class="n">string</span> <span class="n">s</span> <span class="p">)</span> <span class="p">{</span> <span class="k">return</span> <span class="n">PyUnicode_FromString</span><span class="p">(</span> <span class="n">s</span><span class="p">.</span><span class="n">c_str</span><span class="p">()</span> <span class="p">);</span> <span class="p">}</span>
<span class="k">template</span><span class="o">&lt;&gt;</span> <span class="kr">inline</span> <span class="n">PyObject</span><span class="o">*</span> <span class="n">cToPy</span><span class="o">&lt;</span><span class="k">const</span> <span class="n">std</span><span class="o">::</span><span class="n">string</span><span class="o">*&gt;</span> <span class="p">(</span> <span class="k">const</span> <span class="n">std</span><span class="o">::</span><span class="n">string</span><span class="o">*</span> <span class="n">s</span> <span class="p">)</span> <span class="p">{</span> <span class="k">return</span> <span class="n">PyUnicode_FromString</span><span class="p">(</span> <span class="n">s</span><span class="o">-&gt;</span><span class="n">c_str</span><span class="p">()</span> <span class="p">);</span> <span class="p">}</span>
<span class="k">template</span><span class="o">&lt;&gt;</span> <span class="kr">inline</span> <span class="n">PyObject</span><span class="o">*</span> <span class="n">objectLink</span><span class="o">&lt;</span><span class="k">const</span> <span class="kt">int</span><span class="o">*&gt;</span> <span class="p">(</span> <span class="k">const</span> <span class="kt">int</span><span class="o">*</span> <span class="n">i</span> <span class="p">)</span> <span class="c1">// Specialization for POD int.</span>
<span class="p">{</span> <span class="k">return</span> <span class="n">PyInt_FromLong</span><span class="p">(</span> <span class="o">*</span><span class="n">i</span> <span class="p">);</span> <span class="p">}</span> <span class="k">template</span><span class="o">&lt;&gt;</span> <span class="kr">inline</span> <span class="n">PyObject</span><span class="o">*</span> <span class="n">cToPy</span><span class="o">&lt;</span> <span class="kt">int</span> <span class="o">&gt;</span> <span class="p">(</span> <span class="kt">int</span> <span class="n">i</span> <span class="p">)</span> <span class="p">{</span> <span class="k">return</span> <span class="n">PyLong_FromLong</span><span class="p">(</span> <span class="n">i</span> <span class="p">);</span> <span class="p">}</span>
<span class="k">template</span><span class="o">&lt;&gt;</span> <span class="kr">inline</span> <span class="n">PyObject</span><span class="o">*</span> <span class="n">cToPy</span><span class="o">&lt;</span><span class="k">const</span> <span class="kt">int</span> <span class="o">&gt;</span> <span class="p">(</span> <span class="k">const</span> <span class="kt">int</span> <span class="n">i</span> <span class="p">)</span> <span class="p">{</span> <span class="k">return</span> <span class="n">PyLong_FromLong</span><span class="p">(</span> <span class="n">i</span> <span class="p">);</span> <span class="p">}</span>
<span class="k">template</span><span class="o">&lt;&gt;</span> <span class="kr">inline</span> <span class="n">PyObject</span><span class="o">*</span> <span class="n">cToPy</span><span class="o">&lt;</span><span class="k">const</span> <span class="kt">int</span><span class="o">*&gt;</span> <span class="p">(</span> <span class="k">const</span> <span class="kt">int</span><span class="o">*</span> <span class="n">i</span> <span class="p">)</span> <span class="p">{</span> <span class="k">return</span> <span class="n">PyLong_FromLong</span><span class="p">(</span> <span class="o">*</span><span class="n">i</span> <span class="p">);</span> <span class="p">}</span>
</pre></div> </pre></div>
</div> </div>
<div class="section" id="object-methods-wrappers"> <div class="section" id="object-methods-wrappers">
@ -484,8 +564,8 @@ to a sequence of <tt class="docutils literal">PyObject*</tt> (that must match th
of it's C++ function conterpart).</p> of it's C++ function conterpart).</p>
</li> </li>
</ul> </ul>
<p>So, the problem is to change a tuple which size is only kown at <em>runtime</em> <p>So, the problem is to change a Python tuple which size is only kown at
into a list of C/C++ parameters known at <em>compile time</em>.</p> <em>runtime</em> into a list of C/C++ parameters known at <em>compile time</em>.</p>
<p>I am not such an expert in template programming so I can find a <em>generic</em> <p>I am not such an expert in template programming so I can find a <em>generic</em>
solution able to handle any number of parameters. Instead I did write solution able to handle any number of parameters. Instead I did write
a set of templates managing the translation from zero to ten parameters. a set of templates managing the translation from zero to ten parameters.
@ -493,7 +573,21 @@ I did delay that translation as much as possible so it happens very close
to the C++ function call and the duplicated code needed for each template to the C++ function call and the duplicated code needed for each template
is kept to a minimum.</p> is kept to a minimum.</p>
<p>To translate the Python tuple into an ordered list (vector like) of C++ <p>To translate the Python tuple into an ordered list (vector like) of C++
object <em>of different types</em>, the obvious choice is C++ <tt class="docutils literal"><span class="pre">std::tuple&lt;&gt;</span></tt>.</p> object <em>of different types</em>, the obvious choice should have been <tt class="docutils literal"><span class="pre">std::tuple&lt;&gt;</span></tt>,
but I did encouter problems when the functions signature did contains
references. So to manage that I did implement:</p>
<ul class="simple">
<li>A <tt class="docutils literal">BaseArg</tt> class and it's template derived <tt class="docutils literal">Arg&lt;T&gt;</tt> to hold
one value of a type (more or less like <tt class="docutils literal"><span class="pre">std::any&lt;&gt;</span></tt>).
The type of the value attribute of <tt class="docutils literal">Arg&lt;T&gt;</tt> is <tt class="docutils literal">T</tt> <em>stripped</em>
from reference and constness. This internal type is accessible
through <tt class="docutils literal"><span class="pre">Arg&lt;T&gt;::ValueT</span></tt>.</li>
<li>A template list of arguments <tt class="docutils literal"><span class="pre">Args&lt;typename...</span> Ts&gt;</tt> analogous to
<tt class="docutils literal"><span class="pre">std::tuple&lt;&gt;</span></tt> which holds a table of <tt class="docutils literal">BaseArg</tt> to convert all the
arguments.</li>
<li>A recursive template converter function <tt class="docutils literal">parse_pyobjects&lt;&gt;</tt>, which is
called through the <tt class="docutils literal"><span class="pre">Args&lt;&gt;::parse()</span></tt> function.</li>
</ul>
<p>Another challenge is the return type. I distinguish three flavor of <p>Another challenge is the return type. I distinguish three flavor of
return type:</p> return type:</p>
<ul class="simple"> <ul class="simple">
@ -504,49 +598,95 @@ return type:</p>
</ul> </ul>
<p>To uniformize the return type we create four templates <tt class="docutils literal"><span class="pre">_callMethodReturn&lt;&gt;()</span></tt> <p>To uniformize the return type we create four templates <tt class="docutils literal"><span class="pre">_callMethodReturn&lt;&gt;()</span></tt>
that takes whatever the C++ return type is, and turn it into a <tt class="docutils literal">PyObject*</tt>. that takes whatever the C++ return type is, and turn it into a <tt class="docutils literal">PyObject*</tt>.
Except for the functions returning <tt class="docutils literal">void</tt>, we call <tt class="docutils literal"><span class="pre">objectLink&lt;&gt;()</span></tt> to Except for the functions returning <tt class="docutils literal">void</tt>, we call <tt class="docutils literal"><span class="pre">cToPy&lt;&gt;()</span></tt> to
wrap the value.</p> wrap the value. Given the return type of the method, only one template
will match. But as functions template do not allow partial specialization,
only one must be defined for that method (the one <em>matching</em> it's
return type), so we make the template mutually exclusives based on
the <tt class="docutils literal">TR</tt> type (with the <tt class="docutils literal"><span class="pre">std::enable_if&lt;&gt;</span></tt> clause).</p>
<div class="note">
<p class="first admonition-title">Note</p>
<p class="last">In the various <tt class="docutils literal">_callMethodReturn&lt;&gt;</tt> we have <em>two</em> sets for the
method parameters types : <tt class="docutils literal"><span class="pre">TArgsF...</span></tt> and <tt class="docutils literal"><span class="pre">TArgsW...</span></tt>. This is to
allow a wider range of matching in the template as the type of the
arguments of the method (<tt class="docutils literal"><span class="pre">TArgsF...</span></tt>) may not <em>exactly</em> matches the
one passed by the wrapper (<tt class="docutils literal"><span class="pre">TArgsW...</span></tt>), typically the method has
a <tt class="docutils literal">const</tt> parameter which is non-<tt class="docutils literal">const</tt> in the wrapper.</p>
</div>
<p>Here is an excerpt of the code:</p> <p>Here is an excerpt of the code:</p>
<div class="highlight"><pre><span></span><span class="c1">// Flavor for &quot;return by pointer&quot;.</span> <div class="highlight"><pre><span></span><span class="c1">// Flavor for &quot;return by value&quot; (seems to match std::is_object&lt;&gt;)</span>
<span class="k">template</span><span class="o">&lt;</span> <span class="k">typename</span> <span class="n">TC</span><span class="p">,</span> <span class="k">typename</span> <span class="n">TR</span><span class="p">,</span> <span class="k">typename</span><span class="p">...</span> <span class="n">TArgs</span> <span class="k">template</span><span class="o">&lt;</span> <span class="k">typename</span> <span class="n">TC</span><span class="p">,</span> <span class="k">typename</span> <span class="n">TR</span><span class="p">,</span> <span class="k">typename</span><span class="p">...</span> <span class="n">TArgsF</span><span class="p">,</span> <span class="k">typename</span><span class="p">...</span> <span class="n">TArgsW</span>
<span class="p">,</span> <span class="k">typename</span> <span class="n">std</span><span class="o">::</span><span class="n">enable_if</span><span class="o">&lt;</span> <span class="o">!</span><span class="n">std</span><span class="o">::</span><span class="n">is_reference</span><span class="o">&lt;</span><span class="n">TR</span><span class="o">&gt;::</span><span class="n">value</span>
<span class="o">&amp;&amp;</span> <span class="o">!</span><span class="n">std</span><span class="o">::</span><span class="n">is_pointer</span> <span class="o">&lt;</span><span class="n">TR</span><span class="o">&gt;::</span><span class="n">value</span>
<span class="o">&amp;&amp;</span> <span class="o">!</span><span class="n">std</span><span class="o">::</span><span class="n">is_void</span> <span class="o">&lt;</span><span class="n">TR</span><span class="o">&gt;::</span><span class="n">value</span><span class="p">,</span><span class="kt">bool</span><span class="o">&gt;::</span><span class="n">type</span> <span class="o">=</span> <span class="nb">true</span> <span class="o">&gt;</span>
<span class="kr">inline</span> <span class="n">PyObject</span><span class="o">*</span> <span class="n">_callMethodReturn</span> <span class="p">(</span> <span class="n">TR</span><span class="p">(</span><span class="n">TC</span><span class="o">::*</span> <span class="n">method</span><span class="p">)(</span><span class="n">TArgsF</span><span class="p">...),</span> <span class="n">TC</span><span class="o">*</span> <span class="n">cppObject</span><span class="p">,</span> <span class="n">TArgsW</span><span class="p">...</span> <span class="n">args</span> <span class="p">)</span>
<span class="p">{</span>
<span class="n">TR</span> <span class="n">value</span> <span class="o">=</span> <span class="p">(</span><span class="n">cppObject</span><span class="o">-&gt;*</span><span class="n">method</span><span class="p">)(</span> <span class="n">args</span><span class="p">...</span> <span class="p">);</span>
<span class="k">return</span> <span class="nf">cToPy</span><span class="p">(</span> <span class="n">value</span> <span class="p">);</span>
<span class="p">}</span>
<span class="c1">// Flavor for &quot;return by reference&quot;</span>
<span class="k">template</span><span class="o">&lt;</span> <span class="k">typename</span> <span class="n">TC</span><span class="p">,</span> <span class="k">typename</span> <span class="n">TR</span><span class="p">,</span> <span class="k">typename</span><span class="p">...</span> <span class="n">TArgsF</span><span class="p">,</span> <span class="k">typename</span><span class="p">...</span> <span class="n">TArgsW</span>
<span class="p">,</span> <span class="k">typename</span> <span class="n">std</span><span class="o">::</span><span class="n">enable_if</span><span class="o">&lt;</span> <span class="n">std</span><span class="o">::</span><span class="n">is_reference</span><span class="o">&lt;</span><span class="n">TR</span><span class="o">&gt;::</span><span class="n">value</span>
<span class="o">&amp;&amp;</span> <span class="o">!</span><span class="n">std</span><span class="o">::</span><span class="n">is_pointer</span> <span class="o">&lt;</span><span class="n">TR</span><span class="o">&gt;::</span><span class="n">value</span>
<span class="o">&amp;&amp;</span> <span class="o">!</span><span class="n">std</span><span class="o">::</span><span class="n">is_void</span> <span class="o">&lt;</span><span class="n">TR</span><span class="o">&gt;::</span><span class="n">value</span><span class="p">,</span><span class="kt">bool</span><span class="o">&gt;::</span><span class="n">type</span> <span class="o">=</span> <span class="nb">true</span> <span class="o">&gt;</span>
<span class="kr">inline</span> <span class="n">PyObject</span><span class="o">*</span> <span class="n">_callMethodReturn</span> <span class="p">(</span> <span class="n">TR</span><span class="p">(</span><span class="n">TC</span><span class="o">::*</span> <span class="n">method</span><span class="p">)(</span><span class="n">TArgsF</span><span class="p">...),</span> <span class="n">TC</span><span class="o">*</span> <span class="n">cppObject</span><span class="p">,</span> <span class="n">TArgsW</span><span class="p">...</span> <span class="n">args</span> <span class="p">)</span>
<span class="p">{</span>
<span class="n">TR</span> <span class="n">rvalue</span> <span class="o">=</span> <span class="p">(</span><span class="n">cppObject</span><span class="o">-&gt;*</span><span class="n">method</span><span class="p">)(</span> <span class="n">args</span><span class="p">...</span> <span class="p">);</span>
<span class="k">return</span> <span class="nf">cToPy</span><span class="p">(</span> <span class="n">rvalue</span> <span class="p">);</span>
<span class="p">}</span>
<span class="c1">// Flavor for &quot;return by pointer&quot;.</span>
<span class="k">template</span><span class="o">&lt;</span> <span class="k">typename</span> <span class="n">TC</span><span class="p">,</span> <span class="k">typename</span> <span class="n">TR</span><span class="p">,</span> <span class="k">typename</span><span class="p">...</span> <span class="n">TArgsF</span><span class="p">,</span> <span class="k">typename</span><span class="p">...</span> <span class="n">TArgsW</span>
<span class="p">,</span> <span class="k">typename</span> <span class="n">std</span><span class="o">::</span><span class="n">enable_if</span><span class="o">&lt;</span><span class="n">std</span><span class="o">::</span><span class="n">is_pointer</span><span class="o">&lt;</span><span class="n">TR</span><span class="o">&gt;::</span><span class="n">value</span><span class="p">,</span><span class="kt">bool</span><span class="o">&gt;::</span><span class="n">type</span> <span class="o">=</span> <span class="nb">true</span> <span class="o">&gt;</span> <span class="p">,</span> <span class="k">typename</span> <span class="n">std</span><span class="o">::</span><span class="n">enable_if</span><span class="o">&lt;</span><span class="n">std</span><span class="o">::</span><span class="n">is_pointer</span><span class="o">&lt;</span><span class="n">TR</span><span class="o">&gt;::</span><span class="n">value</span><span class="p">,</span><span class="kt">bool</span><span class="o">&gt;::</span><span class="n">type</span> <span class="o">=</span> <span class="nb">true</span> <span class="o">&gt;</span>
<span class="kr">inline</span> <span class="n">PyObject</span><span class="o">*</span> <span class="n">_callMethodReturn</span> <span class="p">(</span> <span class="n">TR</span><span class="p">(</span><span class="n">TC</span><span class="o">::*</span> <span class="n">method</span><span class="p">)(</span><span class="n">TArgs</span><span class="p">...),</span> <span class="n">TC</span><span class="o">*</span> <span class="n">cppObject</span><span class="p">,</span> <span class="n">TArgs</span><span class="p">...</span> <span class="n">args</span> <span class="p">)</span> <span class="kr">inline</span> <span class="n">PyObject</span><span class="o">*</span> <span class="n">_callMethodReturn</span> <span class="p">(</span> <span class="n">TR</span><span class="p">(</span><span class="n">TC</span><span class="o">::*</span> <span class="n">method</span><span class="p">)(</span><span class="n">TArgsF</span><span class="p">...),</span> <span class="n">TC</span><span class="o">*</span> <span class="n">cppObject</span><span class="p">,</span> <span class="n">TArgsW</span><span class="p">...</span> <span class="n">args</span> <span class="p">)</span>
<span class="p">{</span> <span class="p">{</span>
<span class="n">TR</span> <span class="n">pvalue</span> <span class="o">=</span> <span class="p">(</span><span class="n">cppObject</span><span class="o">-&gt;*</span><span class="n">method</span><span class="p">)(</span> <span class="n">args</span><span class="p">...</span> <span class="p">);</span> <span class="n">TR</span> <span class="n">pvalue</span> <span class="o">=</span> <span class="p">(</span><span class="n">cppObject</span><span class="o">-&gt;*</span><span class="n">method</span><span class="p">)(</span> <span class="n">args</span><span class="p">...</span> <span class="p">);</span>
<span class="k">return</span> <span class="nf">objectLink</span><span class="p">(</span> <span class="n">pvalue</span> <span class="p">);</span> <span class="k">return</span> <span class="nf">cToPy</span><span class="p">(</span> <span class="n">pvalue</span> <span class="p">);</span>
<span class="p">}</span> <span class="p">}</span>
<span class="c1">// Flavor for &quot;return void&quot;.</span> <span class="c1">// Flavor for &quot;return void&quot;.</span>
<span class="k">template</span><span class="o">&lt;</span> <span class="k">typename</span> <span class="n">TC</span><span class="p">,</span> <span class="k">typename</span> <span class="n">TR</span><span class="p">,</span> <span class="k">typename</span><span class="p">...</span> <span class="n">TArgs</span> <span class="k">template</span><span class="o">&lt;</span> <span class="k">typename</span> <span class="n">TC</span><span class="p">,</span> <span class="k">typename</span> <span class="n">TR</span><span class="p">,</span> <span class="k">typename</span><span class="p">...</span> <span class="n">TArgsF</span><span class="p">,</span> <span class="k">typename</span><span class="p">...</span> <span class="n">TArgsW</span>
<span class="p">,</span> <span class="k">typename</span> <span class="n">std</span><span class="o">::</span><span class="n">enable_if</span><span class="o">&lt;</span><span class="n">std</span><span class="o">::</span><span class="n">is_void</span><span class="o">&lt;</span><span class="n">TR</span><span class="o">&gt;::</span><span class="n">value</span><span class="p">,</span><span class="kt">bool</span><span class="o">&gt;::</span><span class="n">type</span> <span class="o">=</span> <span class="nb">true</span> <span class="o">&gt;</span> <span class="p">,</span> <span class="k">typename</span> <span class="n">std</span><span class="o">::</span><span class="n">enable_if</span><span class="o">&lt;</span><span class="n">std</span><span class="o">::</span><span class="n">is_void</span><span class="o">&lt;</span><span class="n">TR</span><span class="o">&gt;::</span><span class="n">value</span><span class="p">,</span><span class="kt">bool</span><span class="o">&gt;::</span><span class="n">type</span> <span class="o">=</span> <span class="nb">true</span> <span class="o">&gt;</span>
<span class="kr">inline</span> <span class="n">PyObject</span><span class="o">*</span> <span class="n">_callMethodReturn</span> <span class="p">(</span> <span class="n">TR</span><span class="p">(</span><span class="n">TC</span><span class="o">::*</span> <span class="n">method</span><span class="p">)(</span><span class="n">TArgs</span><span class="p">...),</span> <span class="n">TC</span><span class="o">*</span> <span class="n">cppObject</span><span class="p">,</span> <span class="n">TArgs</span><span class="p">...</span> <span class="n">args</span> <span class="p">)</span> <span class="kr">inline</span> <span class="n">PyObject</span><span class="o">*</span> <span class="n">_callMethodReturn</span> <span class="p">(</span> <span class="n">TR</span><span class="p">(</span><span class="n">TC</span><span class="o">::*</span> <span class="n">method</span><span class="p">)(</span><span class="n">TArgsF</span><span class="p">...),</span> <span class="n">TC</span><span class="o">*</span> <span class="n">cppObject</span><span class="p">,</span> <span class="n">TArgsW</span><span class="p">...</span> <span class="n">args</span> <span class="p">)</span>
<span class="p">{</span> <span class="p">{</span>
<span class="p">(</span><span class="n">cppObject</span><span class="o">-&gt;*</span><span class="n">method</span><span class="p">)(</span> <span class="n">args</span><span class="p">...</span> <span class="p">);</span> <span class="p">(</span><span class="n">cppObject</span><span class="o">-&gt;*</span><span class="n">method</span><span class="p">)(</span> <span class="n">args</span><span class="p">...</span> <span class="p">);</span>
<span class="n">Py_RETURN_NONE</span><span class="p">;</span> <span class="n">Py_RETURN_NONE</span><span class="p">;</span>
<span class="p">}</span> <span class="p">}</span>
<span class="c1">// Function without argument.</span> <span class="c1">// Make the translation call for a method without arguments.</span>
<span class="k">template</span><span class="o">&lt;</span> <span class="k">typename</span> <span class="n">TC</span><span class="p">,</span> <span class="k">typename</span> <span class="n">TR</span> <span class="o">&gt;</span> <span class="k">template</span><span class="o">&lt;</span> <span class="k">typename</span> <span class="n">TC</span><span class="p">,</span> <span class="k">typename</span> <span class="n">TR</span> <span class="o">&gt;</span>
<span class="kr">inline</span> <span class="n">PyObject</span><span class="o">*</span> <span class="n">_callMethod</span> <span class="p">(</span> <span class="n">TR</span><span class="p">(</span><span class="n">TC</span><span class="o">::*</span> <span class="n">method</span><span class="p">)(),</span> <span class="n">TC</span><span class="o">*</span> <span class="n">cppObject</span><span class="p">,</span> <span class="n">std</span><span class="o">::</span><span class="n">tuple</span><span class="o">&lt;&gt;</span> <span class="p">)</span> <span class="kr">inline</span> <span class="n">PyObject</span><span class="o">*</span> <span class="n">_callMethod</span> <span class="p">(</span> <span class="n">TR</span><span class="p">(</span><span class="n">TC</span><span class="o">::*</span> <span class="n">method</span><span class="p">)(),</span> <span class="n">TC</span><span class="o">*</span> <span class="n">cppObject</span><span class="p">,</span> <span class="n">Args</span><span class="o">&lt;&gt;&amp;</span> <span class="p">)</span>
<span class="p">{</span> <span class="k">return</span> <span class="n">_callMethodReturn</span><span class="o">&lt;</span><span class="n">TC</span><span class="p">,</span><span class="n">TR</span><span class="o">&gt;</span><span class="p">(</span> <span class="n">method</span><span class="p">,</span> <span class="n">cppObject</span> <span class="p">);</span> <span class="p">}</span> <span class="p">{</span> <span class="k">return</span> <span class="n">_callMethodReturn</span><span class="o">&lt;</span><span class="n">TC</span><span class="p">,</span><span class="n">TR</span><span class="o">&gt;</span><span class="p">(</span> <span class="n">method</span><span class="p">,</span> <span class="n">cppObject</span> <span class="p">);</span> <span class="p">}</span>
<span class="c1">// Function with one argument.</span> <span class="c1">// Make the translation call for a method one argument.</span>
<span class="k">template</span><span class="o">&lt;</span> <span class="k">typename</span> <span class="n">TC</span><span class="p">,</span> <span class="k">typename</span> <span class="n">TR</span><span class="p">,</span> <span class="k">typename</span> <span class="n">TA0</span> <span class="o">&gt;</span> <span class="k">template</span><span class="o">&lt;</span> <span class="k">typename</span> <span class="n">TC</span><span class="p">,</span> <span class="k">typename</span> <span class="n">TR</span><span class="p">,</span> <span class="k">typename</span> <span class="n">TA0</span> <span class="o">&gt;</span>
<span class="kr">inline</span> <span class="n">PyObject</span><span class="o">*</span> <span class="n">_callMethod</span> <span class="p">(</span> <span class="n">TR</span><span class="p">(</span><span class="n">TC</span><span class="o">::*</span> <span class="n">method</span><span class="p">)(</span><span class="n">TA0</span><span class="p">),</span> <span class="n">TC</span><span class="o">*</span> <span class="n">cppObject</span><span class="p">,</span> <span class="n">std</span><span class="o">::</span><span class="n">tuple</span><span class="o">&lt;</span><span class="n">TA0</span><span class="o">&gt;</span> <span class="n">args</span> <span class="p">)</span> <span class="kr">inline</span> <span class="n">PyObject</span><span class="o">*</span> <span class="n">_callMethod</span> <span class="p">(</span> <span class="n">TR</span><span class="p">(</span><span class="n">TC</span><span class="o">::*</span> <span class="n">method</span><span class="p">)(</span><span class="n">TA0</span><span class="p">),</span> <span class="n">TC</span><span class="o">*</span> <span class="n">cppObject</span><span class="p">,</span> <span class="n">Args</span><span class="o">&lt;</span><span class="n">TA0</span><span class="o">&gt;&amp;</span> <span class="n">args</span> <span class="p">)</span>
<span class="p">{</span> <span class="k">return</span> <span class="n">_callMethodReturn</span><span class="p">(</span> <span class="n">method</span><span class="p">,</span> <span class="n">cppObject</span><span class="p">,</span> <span class="n">std</span><span class="o">::</span><span class="n">get</span><span class="o">&lt;</span><span class="mi">0</span><span class="o">&gt;</span><span class="p">(</span><span class="n">args</span><span class="p">)</span> <span class="p">);</span> <span class="p">}</span> <span class="p">{</span> <span class="k">return</span> <span class="n">_callMethodReturn</span><span class="p">(</span> <span class="n">method</span><span class="p">,</span> <span class="n">cppObject</span><span class="p">,</span> <span class="n">as</span><span class="o">&lt;</span><span class="n">TA0</span><span class="o">&gt;</span><span class="p">(</span> <span class="n">args</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span> <span class="p">)</span> <span class="p">);</span> <span class="p">}</span>
<span class="c1">// Function with two arguments.</span> <span class="c1">// Make the translation call for a method two argument.</span>
<span class="k">template</span><span class="o">&lt;</span> <span class="k">typename</span> <span class="n">TC</span><span class="p">,</span> <span class="k">typename</span> <span class="n">TR</span><span class="p">,</span> <span class="k">typename</span> <span class="n">TA0</span><span class="p">,</span> <span class="k">typename</span> <span class="n">TA1</span> <span class="o">&gt;</span> <span class="k">template</span><span class="o">&lt;</span> <span class="k">typename</span> <span class="n">TC</span><span class="p">,</span> <span class="k">typename</span> <span class="n">TR</span><span class="p">,</span> <span class="k">typename</span> <span class="n">TA0</span><span class="p">,</span> <span class="k">typename</span> <span class="n">TA1</span> <span class="o">&gt;</span>
<span class="n">PyObject</span><span class="o">*</span> <span class="n">_callMethod</span> <span class="p">(</span> <span class="n">TR</span><span class="p">(</span><span class="n">TC</span><span class="o">::*</span> <span class="n">method</span><span class="p">)(</span><span class="n">TA0</span><span class="p">,</span><span class="n">TA1</span><span class="p">),</span> <span class="n">TC</span><span class="o">*</span> <span class="n">cppObject</span><span class="p">,</span> <span class="n">std</span><span class="o">::</span><span class="n">tuple</span><span class="o">&lt;</span><span class="n">TA0</span><span class="p">,</span><span class="n">TA1</span><span class="o">&gt;</span> <span class="n">args</span> <span class="p">)</span> <span class="n">PyObject</span><span class="o">*</span> <span class="n">_callMethod</span> <span class="p">(</span> <span class="n">TR</span><span class="p">(</span><span class="n">TC</span><span class="o">::*</span> <span class="n">method</span><span class="p">)(</span><span class="n">TA0</span><span class="p">,</span><span class="n">TA1</span><span class="p">),</span> <span class="n">TC</span><span class="o">*</span> <span class="n">cppObject</span><span class="p">,</span> <span class="n">Args</span><span class="o">&lt;</span><span class="n">TA0</span><span class="p">,</span><span class="n">TA1</span><span class="o">&gt;&amp;</span> <span class="n">args</span> <span class="p">)</span>
<span class="p">{</span> <span class="k">return</span> <span class="n">_callMethodReturn</span><span class="p">(</span> <span class="n">method</span><span class="p">,</span> <span class="n">cppObject</span><span class="p">,</span> <span class="n">std</span><span class="o">::</span><span class="n">get</span><span class="o">&lt;</span><span class="mi">0</span><span class="o">&gt;</span><span class="p">(</span><span class="n">args</span><span class="p">),</span> <span class="n">std</span><span class="o">::</span><span class="n">get</span><span class="o">&lt;</span><span class="mi">1</span><span class="o">&gt;</span><span class="p">(</span><span class="n">args</span><span class="p">)</span> <span class="p">);</span> <span class="p">}</span> <span class="p">{</span> <span class="k">return</span> <span class="n">_callMethodReturn</span><span class="p">(</span> <span class="n">method</span><span class="p">,</span> <span class="n">cppObject</span><span class="p">,</span> <span class="n">as</span><span class="o">&lt;</span><span class="n">TA0</span><span class="o">&gt;</span><span class="p">(</span> <span class="n">args</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span> <span class="p">),</span> <span class="n">as</span><span class="o">&lt;</span><span class="n">TA1</span><span class="o">&gt;</span><span class="p">(</span> <span class="n">args</span><span class="p">[</span><span class="mi">1</span><span class="p">]</span> <span class="p">)</span> <span class="p">);</span> <span class="p">}</span>
</pre></div> </pre></div>
<p>The complete work of translating the Python tuple into a <tt class="docutils literal"><span class="pre">std::tuple&lt;&gt;</span></tt> and error <p>The complete work of translating the Python tuple into a <tt class="docutils literal">Args&lt;&gt;</tt> is done inside
handling is done with a dedicated template class <tt class="docutils literal">PyMethodWrapper</tt> and it's <tt class="docutils literal">call()</tt> a dedicated template class <tt class="docutils literal">PyWrapper</tt> and it's <tt class="docutils literal">call()</tt> method.
method.</p> C++ exceptions are catched and translated into Python ones.</p>
<ul class="simple">
<li><tt class="docutils literal">PyWrapper</tt> the base class wich handle both C++ and Python exceptions.
Provides the <tt class="docutils literal">call()</tt> function which in turn wraps the <tt class="docutils literal">_call()</tt> that
must be overloaded in derived classes.</li>
<li><tt class="docutils literal">PyFunctionWrapper&lt;&gt;</tt>, template derived class for C/C++ normal functions.</li>
<li><tt class="docutils literal">PyMethodWrapper&lt;&gt;</tt>, template derived class for C++ class methods.
Two flavors are supported, the real method and a function build upon a
method (first argument beaing the object itself). The later is used when
we need to desambiguate overloaded functions, we must create one <em>function</em>
per overload.</li>
</ul>
<p>As a class template cannot guess the template parameters, we wrap them into a <p>As a class template cannot guess the template parameters, we wrap them into a
function template which can perform the guess. The <tt class="docutils literal">callMethod&lt;&gt;</tt> template function.</p> function template which can perform the guess. The <tt class="docutils literal">callMethod&lt;&gt;</tt> template function.</p>
<p>In the end, what the user can write is simply:</p> <p>In the end, what the user can write is simply:</p>
<div class="highlight"><pre><span></span><span class="k">static</span> <span class="n">PyObject</span><span class="o">*</span> <span class="nf">PyParameter_addValue</span> <span class="p">(</span> <span class="n">PyVoidPointer</span><span class="o">*</span> <span class="n">self</span><span class="p">,</span> <span class="n">PyObject</span><span class="o">*</span> <span class="n">args</span> <span class="p">)</span> <div class="highlight"><pre><span></span><span class="k">static</span> <span class="n">PyObject</span><span class="o">*</span> <span class="nf">PyParameter_addValue</span> <span class="p">(</span> <span class="n">PyObject</span><span class="o">*</span> <span class="n">self</span><span class="p">,</span> <span class="n">PyObject</span><span class="o">*</span> <span class="n">args</span> <span class="p">)</span>
<span class="p">{</span> <span class="k">return</span> <span class="n">callMethod</span><span class="p">(</span><span class="s">&quot;Parameter.addValue&quot;</span><span class="p">,</span><span class="o">&amp;</span><span class="n">Parameter</span><span class="o">::</span><span class="n">addValue</span><span class="p">,</span><span class="n">self</span><span class="p">,</span><span class="n">args</span><span class="p">);</span> <span class="p">}</span> <span class="p">{</span> <span class="k">return</span> <span class="n">callMethod</span><span class="p">(</span><span class="s">&quot;Parameter.addValue&quot;</span><span class="p">,</span><span class="o">&amp;</span><span class="n">Parameter</span><span class="o">::</span><span class="n">addValue</span><span class="p">,</span><span class="n">self</span><span class="p">,</span><span class="n">args</span><span class="p">);</span> <span class="p">}</span>
<span class="n">PyMethodDef</span> <span class="n">PyParameter_Methods</span><span class="p">[]</span> <span class="o">=</span> <span class="n">PyMethodDef</span> <span class="n">PyParameter_Methods</span><span class="p">[]</span> <span class="o">=</span>
@ -561,7 +701,7 @@ function template which can perform the guess. The <tt class="docutils literal">
</div> </div>
<div class="section" id="case-of-c-overloaded-functions"> <div class="section" id="case-of-c-overloaded-functions">
<h3><a class="toc-backref" href="#id11">2.6 Case of C++ overloaded functions</a></h3> <h3><a class="toc-backref" href="#id11">2.6 Case of C++ overloaded functions</a></h3>
<p>This apply to both overloaded functions and functions with arguments values.</p> <p>This apply to both overloaded functions and functions with default arguments values.</p>
<p>In that case, the only solution is to create a set of different functions <p>In that case, the only solution is to create a set of different functions
with differents arguments to expose all the various signature of the function. with differents arguments to expose all the various signature of the function.
We then create a function wrapper that calls them in decreasing number of We then create a function wrapper that calls them in decreasing number of
@ -589,7 +729,7 @@ support both case (through different constructors).</p>
<span class="p">,</span> <span class="n">Parameter</span><span class="o">::</span><span class="n">Priority</span> <span class="n">pri</span> <span class="p">)</span> <span class="p">,</span> <span class="n">Parameter</span><span class="o">::</span><span class="n">Priority</span> <span class="n">pri</span> <span class="p">)</span>
<span class="p">{</span> <span class="k">return</span> <span class="n">self</span><span class="o">-&gt;</span><span class="n">setString</span><span class="p">(</span><span class="n">value</span><span class="p">,</span><span class="n">pri</span><span class="p">,</span><span class="n">flags</span><span class="p">);</span> <span class="p">}</span> <span class="p">{</span> <span class="k">return</span> <span class="n">self</span><span class="o">-&gt;</span><span class="n">setString</span><span class="p">(</span><span class="n">value</span><span class="p">,</span><span class="n">pri</span><span class="p">,</span><span class="n">flags</span><span class="p">);</span> <span class="p">}</span>
<span class="k">static</span> <span class="n">PyObject</span><span class="o">*</span> <span class="nf">PyParameter_setString</span> <span class="p">(</span> <span class="n">PyVoidPointer</span><span class="o">*</span> <span class="n">self</span><span class="p">,</span> <span class="n">PyObject</span><span class="o">*</span> <span class="n">args</span> <span class="p">)</span> <span class="k">static</span> <span class="n">PyObject</span><span class="o">*</span> <span class="nf">PyParameter_setString</span> <span class="p">(</span> <span class="n">PyObject</span><span class="o">*</span> <span class="n">self</span><span class="p">,</span> <span class="n">PyObject</span><span class="o">*</span> <span class="n">args</span> <span class="p">)</span>
<span class="p">{</span> <span class="p">{</span>
<span class="n">PyObject</span><span class="o">*</span> <span class="n">rvalue</span> <span class="o">=</span> <span class="n">callMethod</span><span class="p">(</span><span class="s">&quot;Parameter.setString&quot;</span><span class="p">,</span><span class="o">&amp;</span><span class="n">setString3</span><span class="p">,</span><span class="n">self</span><span class="p">,</span><span class="n">args</span><span class="p">);</span> <span class="n">PyObject</span><span class="o">*</span> <span class="n">rvalue</span> <span class="o">=</span> <span class="n">callMethod</span><span class="p">(</span><span class="s">&quot;Parameter.setString&quot;</span><span class="p">,</span><span class="o">&amp;</span><span class="n">setString3</span><span class="p">,</span><span class="n">self</span><span class="p">,</span><span class="n">args</span><span class="p">);</span>
<span class="k">if</span> <span class="p">(</span><span class="n">not</span> <span class="n">rvalue</span><span class="p">)</span> <span class="n">rvalue</span> <span class="o">=</span> <span class="n">callMethod</span><span class="p">(</span><span class="s">&quot;Parameter.setString&quot;</span><span class="p">,</span><span class="o">&amp;</span><span class="n">setString2</span><span class="p">,</span><span class="n">self</span><span class="p">,</span><span class="n">args</span><span class="p">);</span> <span class="k">if</span> <span class="p">(</span><span class="n">not</span> <span class="n">rvalue</span><span class="p">)</span> <span class="n">rvalue</span> <span class="o">=</span> <span class="n">callMethod</span><span class="p">(</span><span class="s">&quot;Parameter.setString&quot;</span><span class="p">,</span><span class="o">&amp;</span><span class="n">setString2</span><span class="p">,</span><span class="n">self</span><span class="p">,</span><span class="n">args</span><span class="p">);</span>
@ -631,7 +771,7 @@ or constants values.</p>
set some constant values in <tt class="docutils literal">Priority</tt>. This was we emulate the behavior of set some constant values in <tt class="docutils literal">Priority</tt>. This was we emulate the behavior of
the <tt class="docutils literal">Priority</tt> <tt class="docutils literal">enum</tt>.</p> the <tt class="docutils literal">Priority</tt> <tt class="docutils literal">enum</tt>.</p>
<div class="highlight"><pre><span></span><span class="k">template</span><span class="o">&lt;&gt;</span> <div class="highlight"><pre><span></span><span class="k">template</span><span class="o">&lt;&gt;</span>
<span class="kr">inline</span> <span class="kt">void</span> <span class="n">pyTypePostModuleInit</span><span class="o">&lt;</span><span class="n">Cfg</span><span class="o">::</span><span class="n">Parameter</span><span class="o">&gt;</span> <span class="p">(</span> <span class="n">PyTypeObject</span><span class="o">*</span> <span class="n">typeObject</span> <span class="p">)</span> <span class="kr">inline</span> <span class="kt">void</span> <span class="n">pyTypePostInit</span><span class="o">&lt;</span><span class="n">Cfg</span><span class="o">::</span><span class="n">Parameter</span><span class="o">&gt;</span> <span class="p">(</span> <span class="n">PyTypeObject</span><span class="o">*</span> <span class="n">typeObject</span> <span class="p">)</span>
<span class="p">{</span> <span class="p">{</span>
<span class="n">PyTypeManagerNonDBo</span><span class="o">&lt;</span><span class="n">Cfg</span><span class="o">::</span><span class="n">Parameter</span><span class="o">::</span><span class="n">Priority</span><span class="o">&gt;::</span><span class="n">create</span><span class="p">(</span> <span class="p">(</span><span class="n">PyObject</span><span class="o">*</span><span class="p">)</span><span class="n">typeObject</span> <span class="n">PyTypeManagerNonDBo</span><span class="o">&lt;</span><span class="n">Cfg</span><span class="o">::</span><span class="n">Parameter</span><span class="o">::</span><span class="n">Priority</span><span class="o">&gt;::</span><span class="n">create</span><span class="p">(</span> <span class="p">(</span><span class="n">PyObject</span><span class="o">*</span><span class="p">)</span><span class="n">typeObject</span>
<span class="p">,</span> <span class="n">Cfg</span><span class="o">::</span><span class="n">PyParameterPriority_Methods</span> <span class="p">,</span> <span class="n">Cfg</span><span class="o">::</span><span class="n">PyParameterPriority_Methods</span>
@ -640,7 +780,7 @@ the <tt class="docutils literal">Priority</tt> <tt class="docutils literal">enum
<span class="p">}</span> <span class="p">}</span>
<span class="k">template</span><span class="o">&lt;&gt;</span> <span class="k">template</span><span class="o">&lt;&gt;</span>
<span class="kr">inline</span> <span class="kt">void</span> <span class="n">pyTypePostModuleInit</span><span class="o">&lt;</span><span class="n">Cfg</span><span class="o">::</span><span class="n">Parameter</span><span class="o">::</span><span class="n">Priority</span><span class="o">&gt;</span> <span class="p">(</span> <span class="n">PyTypeObject</span><span class="o">*</span> <span class="n">typeObject</span> <span class="p">)</span> <span class="kr">inline</span> <span class="kt">void</span> <span class="n">pyTypePostInit</span><span class="o">&lt;</span><span class="n">Cfg</span><span class="o">::</span><span class="n">Parameter</span><span class="o">::</span><span class="n">Priority</span><span class="o">&gt;</span> <span class="p">(</span> <span class="n">PyTypeObject</span><span class="o">*</span> <span class="n">typeObject</span> <span class="p">)</span>
<span class="p">{</span> <span class="p">{</span>
<span class="c1">// Parameter::Priority enum.</span> <span class="c1">// Parameter::Priority enum.</span>
<span class="n">addConstant</span><span class="p">(</span> <span class="n">typeObject</span><span class="p">,</span> <span class="s">&quot;UseDefault&quot;</span> <span class="p">,</span> <span class="n">Cfg</span><span class="o">::</span><span class="n">Parameter</span><span class="o">::</span><span class="n">UseDefault</span> <span class="p">);</span> <span class="n">addConstant</span><span class="p">(</span> <span class="n">typeObject</span><span class="p">,</span> <span class="s">&quot;UseDefault&quot;</span> <span class="p">,</span> <span class="n">Cfg</span><span class="o">::</span><span class="n">Parameter</span><span class="o">::</span><span class="n">UseDefault</span> <span class="p">);</span>

Binary file not shown.

View File

@ -224,95 +224,95 @@ namespace Isobar3 {
{ } { }
PyObject* PyWrapper::call ( PyObject* self, PyObject* args ) PyObject* PyWrapper::_call ( PyObject* self, PyObject* args )
{ {
throw Error( "PyWrapper::call(PyObject*,PyObject*): Base class method must never be called." ); throw Error( "PyWrapper::_call(PyObject*,PyObject*): Base class method must never be called." );
return NULL; return NULL;
} }
PyObject* PyWrapper::call ( PyObject* args ) PyObject* PyWrapper::_call ( PyObject* args )
{ {
throw Error( "PyWrapper::call(PyObject*): Base class method must never be called." ); throw Error( "PyWrapper::_call(PyObject*): Base class method must never be called." );
return NULL; return NULL;
} }
int PyWrapper::predicate ( PyObject* args ) int PyWrapper::_predicate ( PyObject* args )
{ {
throw Error( "PyWrapper::predicate(PyObject*): Base class method must never be called." ); throw Error( "PyWrapper::_predicate(PyObject*): Base class method must never be called." );
return 0; return 0;
} }
PyObject* exceptionWrapper ( PyWrapper& wrapper, PyObject* self, PyObject* args ) PyObject* exceptionWrapper ( PyWrapper* wrapper, PyObject* self, PyObject* args )
{ {
try { try {
return wrapper.call( self, args ); return wrapper->_call( self, args );
} catch ( const Warning& w ) { } catch ( const Warning& w ) {
wrapper.message() += "\n" + getString(w); wrapper->message() += "\n" + getString(w);
} catch ( const Error& e ) { } catch ( const Error& e ) {
wrapper.message() += "\n" + getString(e); wrapper->message() += "\n" + getString(e);
if (not e.where().empty()) wrapper.message() += "\n" + e.where(); if (not e.where().empty()) wrapper->message() += "\n" + e.where();
} catch ( const Bug& e ) { } catch ( const Bug& e ) {
wrapper.message() += "\n" + getString(e); wrapper->message() += "\n" + getString(e);
} catch ( const Exception& e ) { } catch ( const Exception& e ) {
wrapper.message() += "\nUnknown Hurricane::Exception"; wrapper->message() += "\nUnknown Hurricane::Exception";
} catch ( const std::exception& e ) { } catch ( const std::exception& e ) {
wrapper.message() += "\n" + std::string(e.what()); wrapper->message() += "\n" + std::string(e.what());
} catch ( ... ) { } catch ( ... ) {
wrapper.message() += "\nUnmanaged exception, neither a Hurricane::Error nor" wrapper->message() += "\nUnmanaged exception, neither a Hurricane::Error nor"
" a std::exception."; " a std::exception.";
} }
PyErr_SetString( HurricaneError, wrapper.message().c_str() ); PyErr_SetString( HurricaneError, wrapper->message().c_str() );
return NULL; return NULL;
} }
PyObject* exceptionWrapper ( PyWrapper& wrapper, PyObject* args ) PyObject* exceptionWrapper ( PyWrapper* wrapper, PyObject* args )
{ {
try { try {
return wrapper.call( args ); return wrapper->_call( args );
} catch ( const Warning& w ) { } catch ( const Warning& w ) {
wrapper.message() += "\n" + getString(w); wrapper->message() += "\n" + getString(w);
} catch ( const Error& e ) { } catch ( const Error& e ) {
wrapper.message() += "\n" + getString(e); wrapper->message() += "\n" + getString(e);
if (not e.where().empty()) wrapper.message() += "\n" + e.where(); if (not e.where().empty()) wrapper->message() += "\n" + e.where();
} catch ( const Bug& e ) { } catch ( const Bug& e ) {
wrapper.message() += "\n" + getString(e); wrapper->message() += "\n" + getString(e);
} catch ( const Exception& e ) { } catch ( const Exception& e ) {
wrapper.message() += "\nUnknown Hurricane::Exception"; wrapper->message() += "\nUnknown Hurricane::Exception";
} catch ( const std::exception& e ) { } catch ( const std::exception& e ) {
wrapper.message() += "\n" + std::string(e.what()); wrapper->message() += "\n" + std::string(e.what());
} catch ( ... ) { } catch ( ... ) {
wrapper.message() += "\nUnmanaged exception, neither a Hurricane::Error nor" wrapper->message() += "\nUnmanaged exception, neither a Hurricane::Error nor"
" a std::exception."; " a std::exception.";
} }
PyErr_SetString( HurricaneError, wrapper.message().c_str() ); PyErr_SetString( HurricaneError, wrapper->message().c_str() );
return NULL; return NULL;
} }
int exceptionPredicateWrapper ( PyWrapper& wrapper, PyObject* self ) int exceptionPredicateWrapper ( PyWrapper* wrapper, PyObject* self )
{ {
try { try {
return wrapper.predicate( self ); return wrapper->_predicate( self );
} catch ( const Warning& w ) { } catch ( const Warning& w ) {
wrapper.message() += "\n" + getString(w); wrapper->message() += "\n" + getString(w);
} catch ( const Error& e ) { } catch ( const Error& e ) {
wrapper.message() += "\n" + getString(e); wrapper->message() += "\n" + getString(e);
if (not e.where().empty()) wrapper.message() += "\n" + e.where(); if (not e.where().empty()) wrapper->message() += "\n" + e.where();
} catch ( const Bug& e ) { } catch ( const Bug& e ) {
wrapper.message() += "\n" + getString(e); wrapper->message() += "\n" + getString(e);
} catch ( const Exception& e ) { } catch ( const Exception& e ) {
wrapper.message() += "\nUnknown Hurricane::Exception"; wrapper->message() += "\nUnknown Hurricane::Exception";
} catch ( const std::exception& e ) { } catch ( const std::exception& e ) {
wrapper.message() += "\n" + std::string(e.what()); wrapper->message() += "\n" + std::string(e.what());
} catch ( ... ) { } catch ( ... ) {
wrapper.message() += "\nUnmanaged exception, neither a Hurricane::Error nor" wrapper->message() += "\nUnmanaged exception, neither a Hurricane::Error nor"
" a std::exception."; " a std::exception.";
} }
PyErr_SetString( HurricaneError, wrapper.message().c_str() ); PyErr_SetString( HurricaneError, wrapper->message().c_str() );
return 0; return 0;
} }

View File

@ -1576,28 +1576,6 @@ namespace Isobar3 {
// Standard Python to C types converters. // Standard Python to C types converters.
template< typename T
, typename std::enable_if< std::is_pointer<T>::value,bool >::type = true >
inline bool pyToC ( PyObject* pyArg, T* arg )
{
typedef typename std::remove_pointer<T>::type ValueT;
if (std::is_pointer<ValueT>::value) {
Isobar3::PyTypeManager* manager = Isobar3::PyTypeManager::_get<ValueT>();
if (not manager) {
std::cerr << "Isobar3::pyToC<>(T**): Unsupported type." << std::endl;
return false;
}
if (Py_TYPE(pyArg) != manager->_getTypeObject()) return false;
*arg = (T)( Isobar3::object1( pyArg ));
return true;
}
std::cerr << "Isobar3::pyToC<T>(T*): Unsupported type \""
<< Hurricane::demangle(typeid(T).name()) << "\"" << std::endl;
return false;
}
template< typename T template< typename T
, typename std::enable_if< !std::is_pointer<T>::value, bool >::type = true > , typename std::enable_if< !std::is_pointer<T>::value, bool >::type = true >
inline bool pyToC ( PyObject* pyArg, T* arg ) inline bool pyToC ( PyObject* pyArg, T* arg )
@ -1754,23 +1732,28 @@ namespace Isobar3 {
inline PyWrapper (); inline PyWrapper ();
virtual ~PyWrapper (); virtual ~PyWrapper ();
inline std::string& message (); inline std::string& message ();
virtual PyObject* call ( PyObject* self, PyObject* args ); inline PyObject* call ( PyObject* self, PyObject* args );
virtual PyObject* call ( PyObject* args ); inline PyObject* call ( PyObject* args );
virtual int predicate ( PyObject* self ); inline int predicate ( PyObject* self );
virtual PyObject* _call ( PyObject* self, PyObject* args );
virtual PyObject* _call ( PyObject* args );
virtual int _predicate ( PyObject* self );
inline void setMessage ( std::string header ); inline void setMessage ( std::string header );
private: private:
std::string _message; std::string _message;
}; };
extern PyObject* exceptionWrapper ( PyWrapper* wrapper, PyObject* self, PyObject* args );
extern PyObject* exceptionWrapper ( PyWrapper* wrapper, PyObject* args );
extern int exceptionPredicateWrapper ( PyWrapper* wrapper, PyObject* self );
inline PyWrapper::PyWrapper () : _message("Wrapper(): Base class.") {} inline PyWrapper::PyWrapper () : _message("Wrapper(): Base class.") {}
inline std::string& PyWrapper::message () { return _message; } inline std::string& PyWrapper::message () { return _message; }
inline void PyWrapper::setMessage ( std::string header ) { _message = header; } inline void PyWrapper::setMessage ( std::string header ) { _message = header; }
inline PyObject* PyWrapper::call ( PyObject* self, PyObject* args ) { return exceptionWrapper( this, self, args ); }
inline PyObject* PyWrapper::call ( PyObject* args ) { return exceptionWrapper( this, args ); }
extern PyObject* exceptionWrapper ( PyWrapper& wrapper, PyObject* self, PyObject* args ); inline int PyWrapper::predicate ( PyObject* self ) { return exceptionPredicateWrapper( this, self ); }
extern PyObject* exceptionWrapper ( PyWrapper& wrapper, PyObject* args );
extern int exceptionPredicateWrapper ( PyWrapper& wrapper, PyObject* self );
// ------------------------------------------------------------------- // -------------------------------------------------------------------
@ -1785,7 +1768,7 @@ namespace Isobar3 {
inline PyFunctionWrapper ( std::string fname, FunctionType method ) inline PyFunctionWrapper ( std::string fname, FunctionType method )
: PyWrapper(), _funcName(fname), _method(method) { }; : PyWrapper(), _funcName(fname), _method(method) { };
inline std::string funcName () const { return _funcName; }; inline std::string funcName () const { return _funcName; };
virtual PyObject* call ( PyObject* fargs ); virtual PyObject* _call ( PyObject* fargs );
private: private:
std::string _funcName; std::string _funcName;
FunctionType _method; FunctionType _method;
@ -1793,7 +1776,7 @@ namespace Isobar3 {
template< typename TR, typename... TArgs > template< typename TR, typename... TArgs >
PyObject* PyFunctionWrapper<TR,TArgs...>::call ( PyObject* fargs ) PyObject* PyFunctionWrapper<TR,TArgs...>::_call ( PyObject* fargs )
{ {
PyErr_Clear(); PyErr_Clear();
//std::cerr << "_call() " << demangle(typeid(FunctionType).name()) << std::endl; //std::cerr << "_call() " << demangle(typeid(FunctionType).name()) << std::endl;
@ -1855,7 +1838,7 @@ namespace Isobar3 {
, _fMethod(method) , _fMethod(method)
{ }; { };
inline std::string funcName () const { return _funcName; }; inline std::string funcName () const { return _funcName; };
virtual PyObject* call ( PyObject* self, PyObject* fargs ); virtual PyObject* _call ( PyObject* self, PyObject* fargs );
private: private:
std::string _funcName; std::string _funcName;
OMethodType _oMethod; OMethodType _oMethod;
@ -1864,7 +1847,7 @@ namespace Isobar3 {
template< typename TC, typename TR, typename... TArgs > template< typename TC, typename TR, typename... TArgs >
inline PyObject* PyMethodWrapper<TC,TR,TArgs...>::call ( PyObject* self, PyObject* fargs ) inline PyObject* PyMethodWrapper<TC,TR,TArgs...>::_call ( PyObject* self, PyObject* fargs )
{ {
PyErr_Clear(); PyErr_Clear();
size_t nargs = sizeof...(TArgs); size_t nargs = sizeof...(TArgs);
@ -1925,7 +1908,7 @@ namespace Isobar3 {
, _oMethod(mbinary) , _oMethod(mbinary)
{ }; { };
inline std::string funcName () const { return _funcName; }; inline std::string funcName () const { return _funcName; };
virtual PyObject* call ( PyObject* self, PyObject* arg ); virtual PyObject* _call ( PyObject* self, PyObject* arg );
private: private:
std::string _funcName; std::string _funcName;
OMethodType _oMethod; OMethodType _oMethod;
@ -1933,7 +1916,7 @@ namespace Isobar3 {
template< typename TC, typename TArg > template< typename TC, typename TArg >
inline PyObject* PyMBinaryWrapper<TC,TArg>::call ( PyObject* pyObject, PyObject* pyArg ) inline PyObject* PyMBinaryWrapper<TC,TArg>::_call ( PyObject* pyObject, PyObject* pyArg )
{ {
PyErr_Clear(); PyErr_Clear();
setMessage( funcName() + "(): " ); setMessage( funcName() + "(): " );
@ -1962,14 +1945,14 @@ namespace Isobar3 {
, _funcName (fname) , _funcName (fname)
{ }; { };
inline std::string funcName () const { return _funcName; }; inline std::string funcName () const { return _funcName; };
virtual PyObject* call ( PyObject* self, PyObject* arg ); virtual PyObject* _call ( PyObject* self, PyObject* arg );
private: private:
std::string _funcName; std::string _funcName;
}; };
template< typename TC, template<typename> class OperatorT > template< typename TC, template<typename> class OperatorT >
inline PyObject* PyOperatorWrapper<TC,OperatorT>::call ( PyObject* pyObject, PyObject* pyArg ) inline PyObject* PyOperatorWrapper<TC,OperatorT>::_call ( PyObject* pyObject, PyObject* pyArg )
{ {
PyErr_Clear(); PyErr_Clear();
setMessage( funcName() + "(): " ); setMessage( funcName() + "(): " );
@ -2002,7 +1985,7 @@ namespace Isobar3 {
, _oInPlace(minplace) , _oInPlace(minplace)
{ }; { };
inline std::string funcName () const { return _funcName; }; inline std::string funcName () const { return _funcName; };
virtual PyObject* call ( PyObject* self, PyObject* arg ); virtual PyObject* _call ( PyObject* self, PyObject* arg );
private: private:
std::string _funcName; std::string _funcName;
OInPlaceType _oInPlace; OInPlaceType _oInPlace;
@ -2010,7 +1993,7 @@ namespace Isobar3 {
template< typename TC, typename TArg > template< typename TC, typename TArg >
inline PyObject* PyInPlaceOperatorWrapper<TC,TArg>::call ( PyObject* pyObject, PyObject* pyArg ) inline PyObject* PyInPlaceOperatorWrapper<TC,TArg>::_call ( PyObject* pyObject, PyObject* pyArg )
{ {
PyErr_Clear(); PyErr_Clear();
setMessage( funcName() + "(): " ); setMessage( funcName() + "(): " );
@ -2044,7 +2027,7 @@ namespace Isobar3 {
, _oPredicate(pred) , _oPredicate(pred)
{ }; { };
inline std::string funcName () const { return _funcName; }; inline std::string funcName () const { return _funcName; };
virtual int predicate ( PyObject* self ); virtual int _predicate ( PyObject* self );
private: private:
std::string _funcName; std::string _funcName;
OPredicateType _oPredicate; OPredicateType _oPredicate;
@ -2052,7 +2035,7 @@ namespace Isobar3 {
template< typename TC > template< typename TC >
inline int PyPredicateWrapper<TC>::predicate ( PyObject* self ) inline int PyPredicateWrapper<TC>::_predicate ( PyObject* self )
{ {
PyErr_Clear(); PyErr_Clear();
setMessage( funcName() + "(): " ); setMessage( funcName() + "(): " );