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:
parent
a53281cdb3
commit
9c71ce8a03
|
@ -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
|
||||
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
|
||||
about all our newly defined ``PyTypeObjects``.
|
||||
|
||||
* The ``callMethod<>()`` and ``callFunction<>()`` function templates to
|
||||
automatically generate a wrapper around C++ object methods or functions.
|
||||
|
||||
* The ``PyBoject* objectLink<CppT>( CppT* )`` functions templates that provides C++ to Python
|
||||
object translation.
|
||||
|
||||
* The ``bool pyAs<CppT>( PyObject*, CppT*& )`` functions templates that provides Python to C++
|
||||
object translation.
|
||||
* Various wrapper *classes* to wrap functions & methods. They are not directly
|
||||
exposed because the template class intanciation needs the template parameters
|
||||
to be explicitely given, wich is tedious. Instead we create them *through*
|
||||
a function template call, which will perform for us the template type
|
||||
deduction.
|
||||
|
||||
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.
|
||||
|
||||
.. code-block:: Python
|
||||
|
||||
extern "C" {
|
||||
typedef struct PyVoidPointer {
|
||||
typedef struct PyOneVoid {
|
||||
PyObject_HEAD
|
||||
void* _object;
|
||||
void* _object1;
|
||||
};
|
||||
}
|
||||
|
||||
* ``PyIteratorPointer`` which encapsulate one void pointer to the C++ associated
|
||||
iterator and one another to the ``PyObject`` of the container.
|
||||
* ``PyTwoVoid`` which encapsulate one void pointer to the C++ associated
|
||||
object (an iterator) and one another to the ``PyObject`` of the container.
|
||||
|
||||
.. code-block:: Python
|
||||
|
||||
extern "C" {
|
||||
typedef struct PyVoidPointer {
|
||||
typedef struct PyTwoVoid {
|
||||
PyObject_HEAD
|
||||
void* _object; // C++ iterator.
|
||||
PyVoidPointer* _container; // Python wrapped container.
|
||||
void* _object1; // C++ iterator.
|
||||
PyObject* _object2; // Python wrapped container.
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
.. note:: A ``PyTwoVoid`` can be casted/accessed as a ``PyOneVoid``.
|
||||
|
||||
|
||||
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``.
|
||||
As said, it is not a template class but it supplies function member
|
||||
template. Derived classes are provides for different kind of C++
|
||||
class.
|
||||
template. Derived classes are provided to manage different kind of C++
|
||||
classes.
|
||||
|
||||
* :cb:`PyTypeManagerVTrunk<CppT>`
|
||||
|
||||
Is an intermediate between the non-template base class and all the
|
||||
templatized others.
|
||||
templatized others (do **not** use it directly).
|
||||
|
||||
* :cb:`PyTypeManagerNonDBo<CppT>`
|
||||
|
||||
Template for standalone C++ classes that are not derived from ``DBo``.
|
||||
For example ``Box`` or ``Parameter``.
|
||||
|
||||
* :cb:`PyTypeManagerDBo<CppT>`
|
||||
|
||||
Template for C++ classes that *are* not derived from ``DBo``.
|
||||
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:`PyTypeManagerVectorIterator<CppT>`
|
||||
|
||||
Template for C++ ``std::vector<CppT*>::iterator``, automatically created
|
||||
from the vector registration.
|
||||
|
||||
* :cb:`PyTypeManagerMap<CppK,CppT>`, template for C++ ``std::map<CppK*,CppT*>``.
|
||||
|
||||
* :cb:`PyTypeManagerMapIterator<CppK,CppT>`
|
||||
|
||||
Template for C++ ``std::vector<CppK*,CppT*>::iterator``, automatically created
|
||||
from the map registration.
|
||||
|
||||
* :cb:`PyTypeManagerCollection<,CppT>`, template for C++ ``Hurricane::Collection<CppT*>``.
|
||||
|
||||
* :cb:`PyTypeManagerCollectionIterator<,CppT>`
|
||||
|
||||
Template for C++ ``Hurricane::Locator<CppT*>``, automatically created from
|
||||
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``,
|
||||
``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
|
||||
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.
|
||||
|
||||
Excerpt from the code:
|
||||
|
@ -123,7 +152,7 @@ Excerpt from the code:
|
|||
extern long _tpHash ( PyObject* self )
|
||||
{
|
||||
// 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:
|
||||
void PyTypeManager::_setupPyType ()
|
||||
// Derived classes must implement it as they see fit.
|
||||
virtual long _getTpHash ( PyVoidPointer* ) = 0;
|
||||
virtual long _getTpHash ( PyObject* ) = 0;
|
||||
template<typename CppT>
|
||||
static PyTypeManager* _get();
|
||||
private:
|
||||
|
@ -158,63 +187,101 @@ Excerpt from the code:
|
|||
2.3 Going From Python to C++
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
To convert a C++ object (pointer) into a Python object, a set of
|
||||
:cb:`pyAs<>` templates functions are supplieds.
|
||||
To convert a C++ object (pointer) into a Python object, a mix of
|
||||
: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
|
||||
Python types must be provideds (``bool``, ``int``, ``long``, ``double``,
|
||||
``std::string``, ...).
|
||||
|
||||
Partial specialization for type availables through the ``PyTypeManager``
|
||||
is supplied. It checks the manager for the type's existence.
|
||||
Those templates/functions are the ones the ``Isobar::parse_objects()`` recursive
|
||||
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++
|
||||
|
||||
// Most generic template, should *not* be used.
|
||||
// So issue an error message and report a failed conversion.
|
||||
template<typename T>
|
||||
inline bool pyAs ( PyObject* pyArg, T& arg )
|
||||
// Template/Pointer to a value flavor.
|
||||
template< typename T
|
||||
, typename std::enable_if< !std::is_pointer<T>::value, bool >::type = true >
|
||||
inline bool pyToC ( PyObject* pyArg, T* arg )
|
||||
{
|
||||
std::cerr << "Isobar3.pyAs<T>(): Unsupported type \""
|
||||
<< demangle(typeid(T).name()) << "\"" << endl;
|
||||
return false;
|
||||
typedef typename std::remove_cv<T>::type NonConstT;
|
||||
Isobar3::PyTypeManager* manager = Isobar3::PyTypeManager::_get<T>();
|
||||
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<>
|
||||
inline bool pyAs ( PyObject* pyArg, int& arg )
|
||||
// Template/Pointer to a pointer flavor.
|
||||
template<typename T>
|
||||
inline bool pyToC ( PyObject* pyArg, T** arg )
|
||||
{
|
||||
if (PyInt_Check (pyArg)) { arg = PyInt_AsLong ( pyArg ); }
|
||||
else if (PyLong_Check(pyArg)) { arg = PyLong_AsLong( pyArg ); }
|
||||
Isobar3::PyTypeManager* manager = Isobar3::PyTypeManager::_get<T>();
|
||||
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;
|
||||
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
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
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
|
||||
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>``
|
||||
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++
|
||||
|
||||
// The most generic template, should *not* be used. So raise a Python exception.
|
||||
// Generic template for values.
|
||||
template< typename CppT >
|
||||
inline PyObject* objectLink ( CppT object )
|
||||
inline PyObject* cToPy ( CppT object )
|
||||
{
|
||||
std::string message = "Overload for Isobar3::objectLink< "
|
||||
+ demangle(typeid(CppT).name()) + " >() is missing.";
|
||||
PyErr_SetString( HurricaneError, message.c_str() );
|
||||
return NULL;
|
||||
if (not Isobar3::PyTypeManager::hasType<CppT>()) {
|
||||
std::string message = "Overload for Isobar3::cToPy< "
|
||||
+ Hurricane::demangle(typeid(CppT).name()) + " >() Type not registered in the manager.";
|
||||
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
|
||||
, typename std::enable_if< !std::is_same<CppT,bool>::value
|
||||
&& !std::is_same<CppT,int >::value
|
||||
&& !std::is_same<CppT,std::string>::value
|
||||
&& !std::is_same<CppT,const std::string>::value
|
||||
,bool>::type = true >
|
||||
inline PyObject* objectLink ( CppT* object )
|
||||
{ return PyTypeManager::link<CppT>( object ); }
|
||||
&& !std::is_same<CppT,const std::string>::value,bool>::type = true >
|
||||
inline PyObject* cToPy ( CppT* object )
|
||||
{ return Isobar3::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
|
||||
, typename std::enable_if< !std::is_same<CppT,bool>::value
|
||||
&& !std::is_same<CppT,int >::value
|
||||
&& !std::is_same<CppT,std::string>::value
|
||||
&& !std::is_same<CppT,const std::string>::value
|
||||
,bool>::type = true >
|
||||
inline PyObject* objectLink ( const CppT* object )
|
||||
{ return PyTypeManager::link<CppT>( const_cast<CppT*>( object )); }
|
||||
&& !std::is_same<CppT,const std::string>::value,bool>::type = true >
|
||||
inline PyObject* cToPy ( const CppT* object )
|
||||
{ return Isobar3::PyTypeManager::link<CppT>( const_cast<CppT*>( object )); }
|
||||
|
||||
// Specializations for POD type "int" .
|
||||
template<> inline PyObject* objectLink<int > ( int i )
|
||||
{ return PyInt_FromLong( i ); }
|
||||
// Specialization for booleans.
|
||||
template<>
|
||||
inline PyObject* cToPy<bool> ( bool b )
|
||||
{ if (b) Py_RETURN_TRUE; Py_RETURN_FALSE; }
|
||||
|
||||
template<> inline PyObject* objectLink<const int > ( const int i )
|
||||
{ return PyInt_FromLong( i ); }
|
||||
// Specialization for STL std::string.
|
||||
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 )
|
||||
{ return PyInt_FromLong( *i ); }
|
||||
// Specialization for POD int.
|
||||
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
|
||||
|
@ -309,8 +393,8 @@ Basically, we want to fit:
|
|||
to a sequence of ``PyObject*`` (that must match the number of arguments
|
||||
of it's C++ function conterpart).
|
||||
|
||||
So, the problem is to change a tuple which size is only kown at *runtime*
|
||||
into a list of C/C++ parameters known at *compile time*.
|
||||
So, the problem is to change a Python tuple which size is only kown at
|
||||
*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*
|
||||
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.
|
||||
|
||||
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
|
||||
return type:
|
||||
|
@ -332,50 +431,96 @@ return type:
|
|||
|
||||
To uniformize the return type we create four templates ``_callMethodReturn<>()``
|
||||
that takes whatever the C++ return type is, and turn it into a ``PyObject*``.
|
||||
Except for the functions returning ``void``, we call ``objectLink<>()`` to
|
||||
wrap the value.
|
||||
Except for the functions returning ``void``, we call ``cToPy<>()`` to
|
||||
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:
|
||||
|
||||
.. 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".
|
||||
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 >
|
||||
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... );
|
||||
return objectLink( pvalue );
|
||||
return cToPy( pvalue );
|
||||
}
|
||||
|
||||
// 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 >
|
||||
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... );
|
||||
Py_RETURN_NONE;
|
||||
}
|
||||
|
||||
// Function without argument.
|
||||
// Make the translation call for a method without arguments.
|
||||
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 ); }
|
||||
|
||||
// Function with one argument.
|
||||
// Make the translation call for a method one argument.
|
||||
template< typename TC, typename TR, typename TA0 >
|
||||
inline PyObject* _callMethod ( TR(TC::* method)(TA0), TC* cppObject, std::tuple<TA0> args )
|
||||
{ return _callMethodReturn( method, cppObject, std::get<0>(args) ); }
|
||||
inline PyObject* _callMethod ( TR(TC::* method)(TA0), TC* cppObject, Args<TA0>& 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 >
|
||||
PyObject* _callMethod ( TR(TC::* method)(TA0,TA1), TC* cppObject, std::tuple<TA0,TA1> args )
|
||||
{ return _callMethodReturn( method, cppObject, std::get<0>(args), std::get<1>(args) ); }
|
||||
PyObject* _callMethod ( TR(TC::* method)(TA0,TA1), TC* cppObject, Args<TA0,TA1>& 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
|
||||
handling is done with a dedicated template class ``PyMethodWrapper`` and it's ``call()``
|
||||
method.
|
||||
The complete work of translating the Python tuple into a ``Args<>`` is done inside
|
||||
a dedicated template class ``PyWrapper`` and it's ``call()`` 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
|
||||
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++
|
||||
|
||||
static PyObject* PyParameter_addValue ( PyVoidPointer* self, PyObject* args )
|
||||
static PyObject* PyParameter_addValue ( PyObject* self, PyObject* args )
|
||||
{ return callMethod("Parameter.addValue",&Parameter::addValue,self,args); }
|
||||
|
||||
PyMethodDef PyParameter_Methods[] =
|
||||
|
@ -400,7 +545,7 @@ In the end, what the user can write is simply:
|
|||
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
|
||||
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 )
|
||||
{ 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);
|
||||
if (not rvalue) rvalue = callMethod("Parameter.setString",&setString2,self,args);
|
||||
|
@ -484,7 +629,7 @@ the ``Priority`` ``enum``.
|
|||
.. code-block:: C++
|
||||
|
||||
template<>
|
||||
inline void pyTypePostModuleInit<Cfg::Parameter> ( PyTypeObject* typeObject )
|
||||
inline void pyTypePostInit<Cfg::Parameter> ( PyTypeObject* typeObject )
|
||||
{
|
||||
PyTypeManagerNonDBo<Cfg::Parameter::Priority>::create( (PyObject*)typeObject
|
||||
, Cfg::PyParameterPriority_Methods
|
||||
|
@ -493,7 +638,7 @@ the ``Priority`` ``enum``.
|
|||
}
|
||||
|
||||
template<>
|
||||
inline void pyTypePostModuleInit<Cfg::Parameter::Priority> ( PyTypeObject* typeObject )
|
||||
inline void pyTypePostInit<Cfg::Parameter::Priority> ( PyTypeObject* typeObject )
|
||||
{
|
||||
// Parameter::Priority enum.
|
||||
addConstant( typeObject, "UseDefault" , Cfg::Parameter::UseDefault );
|
||||
|
|
|
@ -12,7 +12,7 @@
|
|||
directly your functions with the original API or any mix between.
|
||||
You only have to respect some naming convention.
|
||||
|
||||
* Coriolis is build against Python 2.7.
|
||||
* Coriolis is build against Python 3.6.
|
||||
|
||||
|
||||
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
|
||||
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
|
||||
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
|
||||
modules for the libraries have to share some functions. Python
|
||||
|
|
|
@ -153,7 +153,7 @@
|
|||
the standard Python C/API. That is, you may not use them and write
|
||||
directly your functions with the original API or any mix between.
|
||||
You only have to respect some naming convention.</li>
|
||||
<li>Coriolis is build against Python 2.7.</li>
|
||||
<li>Coriolis is build against Python 3.6.</li>
|
||||
</ul>
|
||||
<div class="section" id="about-technical-choices">
|
||||
<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><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
|
||||
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
|
||||
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
|
||||
modules for the libraries have to share some functions. Python
|
||||
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
|
||||
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>
|
||||
<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<>()</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<>()</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<>()</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 & 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<>()</span></tt> and <tt class="docutils literal"><span class="pre">callMethod<>()</span></tt>. <tt class="docutils literal"><span class="pre">callMethod<>()</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">
|
||||
<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>
|
||||
<li>The <tt class="docutils literal"><span class="pre">callMethod<>()</span></tt> and <tt class="docutils literal"><span class="pre">callFunction<>()</span></tt> function templates to
|
||||
automatically generate a wrapper around C++ object methods or functions.</li>
|
||||
<li>The <tt class="docutils literal">PyBoject* objectLink<CppT>( CppT* )</tt> functions templates that provides C++ to Python
|
||||
object translation.</li>
|
||||
<li>The <tt class="docutils literal">bool pyAs<CppT>( PyObject*, CppT*& )</tt> functions templates that provides Python to C++
|
||||
object translation.</li>
|
||||
<li>Various wrapper <em>classes</em> to wrap functions & methods. They are not directly
|
||||
exposed because the template class intanciation needs the template parameters
|
||||
to be explicitely given, wich is tedious. Instead we create them <em>through</em>
|
||||
a function template call, which will perform for us the template type
|
||||
deduction.</li>
|
||||
</ul>
|
||||
<p>We creates only two kind of <tt class="docutils literal">PyObject</tt> (but many <tt class="docutils literal">PyTypeObject</tt>):</p>
|
||||
<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>
|
||||
<div class="highlight"><pre><span></span><span class="n">extern</span> <span class="s2">"C"</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">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>
|
||||
</pre></div>
|
||||
</li>
|
||||
<li><p class="first"><tt class="docutils literal">PyIteratorPointer</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>
|
||||
<li><p class="first"><tt class="docutils literal">PyTwoVoid</tt> which encapsulate one void pointer to the C++ associated
|
||||
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">"C"</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">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">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">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">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>
|
||||
</pre></div>
|
||||
</li>
|
||||
</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">
|
||||
<h3><a class="toc-backref" href="#id6">2.1 PyTypeManager</a></h3>
|
||||
<p><tt class="docutils literal">PyTypeManager</tt> has two usage:</p>
|
||||
<ul>
|
||||
<li><p class="first">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>
|
||||
</li>
|
||||
<li><p class="first">Provide a non-template abstract base class for all the derived <tt class="docutils literal">PyTypeObject</tt>.
|
||||
<ul class="simple">
|
||||
<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.</li>
|
||||
<li>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
|
||||
template. Derived classes are provides for different kind of C++
|
||||
class.</p>
|
||||
<ul>
|
||||
<li><p class="first"><span class="cb">PyTypeManagerVTrunk<CppT></span></p>
|
||||
<p>Is an intermediate between the non-template base class and all the
|
||||
templatized others.</p>
|
||||
</li>
|
||||
<li><p class="first"><span class="cb">PyTypeManagerNonDBo<CppT></span></p>
|
||||
<p>Template for standalone C++ classes that are not derived from <tt class="docutils literal">DBo</tt>.
|
||||
For example <tt class="docutils literal">Box</tt> or <tt class="docutils literal">Parameter</tt>.</p>
|
||||
</li>
|
||||
<li><p class="first"><span class="cb">PyTypeManagerDBo<CppT></span></p>
|
||||
<p>Template for C++ classes that <em>are</em> not derived from <tt class="docutils literal">DBo</tt>.
|
||||
For example <tt class="docutils literal">Cell</tt> or <tt class="docutils literal">Instance</tt>.</p>
|
||||
</li>
|
||||
<li><p class="first"><span class="cb">PyTypeManagerVector<CppT></span>, template for C++ <tt class="docutils literal"><span class="pre">std::vector<CppT*></span></tt>.</p>
|
||||
</li>
|
||||
<li><p class="first"><span class="cb">PyTypeManagerVectorIterator<CppT></span></p>
|
||||
<p>Template for C++ <tt class="docutils literal"><span class="pre">std::vector<CppT*>::iterator</span></tt>, automatically created
|
||||
from the vector registration.</p>
|
||||
</li>
|
||||
<li><p class="first"><span class="cb">PyTypeManagerMap<CppK,CppT></span>, template for C++ <tt class="docutils literal"><span class="pre">std::map<CppK*,CppT*></span></tt>.</p>
|
||||
</li>
|
||||
<li><p class="first"><span class="cb">PyTypeManagerMapIterator<CppK,CppT></span></p>
|
||||
<p>Template for C++ <tt class="docutils literal"><span class="pre">std::vector<CppK*,CppT*>::iterator</span></tt>, automatically created
|
||||
from the map registration.</p>
|
||||
</li>
|
||||
<li><p class="first"><span class="cb">PyTypeManagerCollection<,CppT></span>, template for C++ <tt class="docutils literal"><span class="pre">Hurricane::Collection<CppT*></span></tt>.</p>
|
||||
</li>
|
||||
<li><p class="first"><span class="cb">PyTypeManagerCollectionIterator<,CppT></span></p>
|
||||
<p>Template for C++ <tt class="docutils literal"><span class="pre">Hurricane::Locator<CppT*></span></tt>, automatically created from
|
||||
the collection registration.</p>
|
||||
</li>
|
||||
template. Derived classes are provided to manage different kind of C++
|
||||
classes.<ul>
|
||||
<li><span class="cb">PyTypeManagerVTrunk<CppT></span>
|
||||
Is an intermediate between the non-template base class and all the
|
||||
templatized others (do <strong>not</strong> use it directly).</li>
|
||||
<li><span class="cb">PyTypeManagerNonDBo<CppT></span>
|
||||
Template for standalone C++ classes that are not derived from <tt class="docutils literal">DBo</tt>.
|
||||
For example <tt class="docutils literal">Box</tt> or <tt class="docutils literal">Parameter</tt>.</li>
|
||||
<li><span class="cb">PyTypeManagerDBo<CppT></span>
|
||||
Template for C++ classes that <em>are</em> not derived from <tt class="docutils literal">DBo</tt>.
|
||||
For example <tt class="docutils literal">Cell</tt> or <tt class="docutils literal">Instance</tt>.</li>
|
||||
<li><span class="cb">PyTypeManagerDerivedDBo<CppT,BaseT></span>
|
||||
Template for a <tt class="docutils literal">CppT</tt> class derived derived from a <tt class="docutils literal">BaseT</tt> class.
|
||||
<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>
|
||||
needs to derive from <tt class="docutils literal">DBo</tt></li>
|
||||
<li><span class="cb">PyTypeManagerVector<CppT></span>, template for C++ <tt class="docutils literal"><span class="pre">std::vector<CppT*></span></tt>.</li>
|
||||
<li><span class="cb">PyTypeManagerVectorIterator<CppT></span>
|
||||
Template for C++ <tt class="docutils literal"><span class="pre">std::vector<CppT*>::iterator</span></tt>, automatically created
|
||||
from the vector registration.</li>
|
||||
<li><span class="cb">PyTypeManagerMap<CppK,CppT></span>, template for C++ <tt class="docutils literal"><span class="pre">std::map<CppK*,CppT*></span></tt>.</li>
|
||||
<li><span class="cb">PyTypeManagerMapIterator<CppK,CppT></span>
|
||||
Template for C++ <tt class="docutils literal"><span class="pre">std::vector<CppK*,CppT*>::iterator</span></tt>, automatically created
|
||||
from the map registration.</li>
|
||||
<li><span class="cb">PyTypeManagerCollection<,CppT></span>, template for C++ <tt class="docutils literal"><span class="pre">Hurricane::Collection<CppT*></span></tt>.</li>
|
||||
<li><span class="cb">PyTypeManagerCollectionIterator<,CppT></span>
|
||||
Template for C++ <tt class="docutils literal"><span class="pre">Hurricane::Locator<CppT*></span></tt>, automatically created from
|
||||
the collection registration.</li>
|
||||
</ul>
|
||||
</li>
|
||||
</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>,
|
||||
<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
|
||||
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>
|
||||
<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>
|
||||
|
@ -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="p">{</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">-></span><span class="n">ob_type</span> <span class="p">)</span><span class="o">-></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">-></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>
|
||||
|
@ -324,7 +348,7 @@ a map lookup. Hope it is fast.</p>
|
|||
<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="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"><</span><span class="k">typename</span> <span class="n">CppT</span><span class="o">></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>
|
||||
|
@ -349,57 +373,94 @@ a map lookup. Hope it is fast.</p>
|
|||
</div>
|
||||
<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>
|
||||
<p>To convert a C++ object (pointer) into a Python object, a set of
|
||||
<span class="cb">pyAs<></span> templates functions are supplieds.</p>
|
||||
<p>To convert a C++ object (pointer) into a Python object, a mix of
|
||||
<span class="cb">pyToC<>()</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
|
||||
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>
|
||||
<p>Partial specialization for type availables through the <tt class="docutils literal">PyTypeManager</tt>
|
||||
is supplied. It checks the manager for the type's existence.</p>
|
||||
<div class="highlight"><pre><span></span><span class="c1">// Most generic template, should *not* be used.</span>
|
||||
<span class="c1">// So issue an error message and report a failed conversion.</span>
|
||||
<span class="k">template</span><span class="o"><</span><span class="k">typename</span> <span class="n">T</span><span class="o">></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">&</span> <span class="n">arg</span> <span class="p">)</span>
|
||||
<p>Those templates/functions are the ones the <tt class="docutils literal"><span class="pre">Isobar::parse_objects()</span></tt> recursive
|
||||
template function call in turn for each <tt class="docutils literal">PyObject*</tt> argument.</p>
|
||||
<div class="note">
|
||||
<p class="first admonition-title">Note</p>
|
||||
<p class="last"><tt class="docutils literal"><span class="pre">Hurricane::Name</span></tt> are <em>not</em> exposed to the Python interface, they
|
||||
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"><</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"><</span> <span class="o">!</span><span class="n">std</span><span class="o">::</span><span class="n">is_pointer</span><span class="o"><</span><span class="n">T</span><span class="o">>::</span><span class="n">value</span><span class="p">,</span> <span class="kt">bool</span> <span class="o">>::</span><span class="n">type</span> <span class="o">=</span> <span class="nb">true</span> <span class="o">></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="n">std</span><span class="o">::</span><span class="n">cerr</span> <span class="o"><<</span> <span class="s">"Isobar3.pyAs<T>(): Unsupported type </span><span class="se">\"</span><span class="s">"</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">T</span><span class="p">).</span><span class="n">name</span><span class="p">())</span> <span class="o"><<</span> <span class="s">"</span><span class="se">\"</span><span class="s">"</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="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"><</span><span class="n">T</span><span class="o">>::</span><span class="n">type</span> <span class="n">NonConstT</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"><</span><span class="n">T</span><span class="o">></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"><<</span> <span class="s">"Isobar3::pyToC<>(const T*): Unsupported type."</span> <span class="o"><<</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">-></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"><</span> <span class="n">NonConstT</span><span class="o">*</span> <span class="o">></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="c1">// Template specialization for POD type "int".</span>
|
||||
<span class="k">template</span><span class="o"><></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">&</span> <span class="n">arg</span> <span class="p">)</span>
|
||||
<span class="c1">// Template/Pointer to a pointer flavor.</span>
|
||||
<span class="k">template</span><span class="o"><</span><span class="k">typename</span> <span class="n">T</span><span class="o">></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="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="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="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"><</span><span class="n">T</span><span class="o">></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"><<</span> <span class="s">"Isobar3::pyToC<T>(T*&): Unsupported type </span><span class="se">\"</span><span class="s">"</span> <span class="o"><<</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"><<</span> <span class="s">"</span><span class="se">\"</span><span class="s">"</span> <span class="o"><<</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">return</span> <span class="nb">true</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"><</span><span class="k">typename</span> <span class="n">T</span><span class="o">></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">*&</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"><</span><span class="n">T</span><span class="o">></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"><<</span> <span class="s">"Isobar3.pyAs<T>(): Unsupported type </span><span class="se">\"</span><span class="s">"</span>
|
||||
<span class="o"><<</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"><<</span> <span class="s">"</span><span class="se">\"</span><span class="s">"</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="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">-></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"><</span><span class="k">typename</span> <span class="n">T</span><span class="o">></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">*&</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>
|
||||
</div>
|
||||
<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>
|
||||
<p>To convert a Python object into a C++ object, a set of
|
||||
<span class="cb">objectLink<></span> templates functions are supplieds.</p>
|
||||
<span class="cb">cToPy<></span> templates functions are supplieds.</p>
|
||||
<p>We completely disable the partially specialized templates for
|
||||
objects that are non-POD as the compiler seems to be unable to
|
||||
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<CppT></tt>
|
||||
template class.</p>
|
||||
</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<typename CppT> PyObject* cToPy ( CppT object )</tt></li>
|
||||
<li><tt class="docutils literal">template<typename CppT> 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"><</span> <span class="k">typename</span> <span class="n">CppT</span> <span class="o">></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="n">std</span><span class="o">::</span><span class="n">string</span> <span class="n">message</span> <span class="o">=</span> <span class="s">"Overload for Isobar3::objectLink< "</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">" >() is missing."</span><span class="p">;</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="k">return</span> <span class="nb">NULL</span><span class="p">;</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"><</span><span class="n">CppT</span><span class="o">></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">"Overload for Isobar3::cToPy< "</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">" >() Type not registered in the manager."</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"><</span><span class="n">CppT</span><span class="o">></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="c1">// Disable this template if "CppT" is an already supported type.</span>
|
||||
<span class="c1">// Disabled for POD & STL types, pointer flavor.</span>
|
||||
<span class="k">template</span><span class="o"><</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"><</span> <span class="o">!</span><span class="n">std</span><span class="o">::</span><span class="n">is_same</span><span class="o"><</span><span class="n">CppT</span><span class="p">,</span><span class="kt">bool</span><span class="o">>::</span><span class="n">value</span>
|
||||
<span class="o">&&</span> <span class="o">!</span><span class="n">std</span><span class="o">::</span><span class="n">is_same</span><span class="o"><</span><span class="n">CppT</span><span class="p">,</span><span class="kt">int</span> <span class="o">>::</span><span class="n">value</span>
|
||||
<span class="o">&&</span> <span class="o">!</span><span class="n">std</span><span class="o">::</span><span class="n">is_same</span><span class="o"><</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">>::</span><span class="n">value</span>
|
||||
<span class="o">&&</span> <span class="o">!</span><span class="n">std</span><span class="o">::</span><span class="n">is_same</span><span class="o"><</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">>::</span><span class="n">value</span>
|
||||
<span class="p">,</span><span class="kt">bool</span><span class="o">>::</span><span class="n">type</span> <span class="o">=</span> <span class="nb">true</span> <span class="o">></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">PyTypeManager</span><span class="o">::</span><span class="n">link</span><span class="o"><</span><span class="n">CppT</span><span class="o">></span><span class="p">(</span> <span class="n">object</span> <span class="p">);</span> <span class="p">}</span>
|
||||
<span class="o">&&</span> <span class="o">!</span><span class="n">std</span><span class="o">::</span><span class="n">is_same</span><span class="o"><</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">>::</span><span class="n">value</span><span class="p">,</span><span class="kt">bool</span><span class="o">>::</span><span class="n">type</span> <span class="o">=</span> <span class="nb">true</span> <span class="o">></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="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"><</span><span class="n">CppT</span><span class="o">></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 "CppT" is an already supported type.</span>
|
||||
<span class="c1">// Disabled for POD & STL types, const pointer flavor.</span>
|
||||
<span class="k">template</span><span class="o"><</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"><</span> <span class="o">!</span><span class="n">std</span><span class="o">::</span><span class="n">is_same</span><span class="o"><</span><span class="n">CppT</span><span class="p">,</span><span class="kt">bool</span><span class="o">>::</span><span class="n">value</span>
|
||||
<span class="o">&&</span> <span class="o">!</span><span class="n">std</span><span class="o">::</span><span class="n">is_same</span><span class="o"><</span><span class="n">CppT</span><span class="p">,</span><span class="kt">int</span> <span class="o">>::</span><span class="n">value</span>
|
||||
<span class="o">&&</span> <span class="o">!</span><span class="n">std</span><span class="o">::</span><span class="n">is_same</span><span class="o"><</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">>::</span><span class="n">value</span>
|
||||
<span class="o">&&</span> <span class="o">!</span><span class="n">std</span><span class="o">::</span><span class="n">is_same</span><span class="o"><</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">>::</span><span class="n">value</span>
|
||||
<span class="p">,</span><span class="kt">bool</span><span class="o">>::</span><span class="n">type</span> <span class="o">=</span> <span class="nb">true</span> <span class="o">></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">PyTypeManager</span><span class="o">::</span><span class="n">link</span><span class="o"><</span><span class="n">CppT</span><span class="o">></span><span class="p">(</span> <span class="k">const_cast</span><span class="o"><</span><span class="n">CppT</span><span class="o">*></span><span class="p">(</span> <span class="n">object</span> <span class="p">));</span> <span class="p">}</span>
|
||||
<span class="o">&&</span> <span class="o">!</span><span class="n">std</span><span class="o">::</span><span class="n">is_same</span><span class="o"><</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">>::</span><span class="n">value</span><span class="p">,</span><span class="kt">bool</span><span class="o">>::</span><span class="n">type</span> <span class="o">=</span> <span class="nb">true</span> <span class="o">></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="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"><</span><span class="n">CppT</span><span class="o">></span><span class="p">(</span> <span class="k">const_cast</span><span class="o"><</span><span class="n">CppT</span><span class="o">*></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 "int" .</span>
|
||||
<span class="k">template</span><span class="o"><></span> <span class="kr">inline</span> <span class="n">PyObject</span><span class="o">*</span> <span class="n">objectLink</span><span class="o"><</span><span class="kt">int</span> <span class="o">></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">PyInt_FromLong</span><span class="p">(</span> <span class="n">i</span> <span class="p">);</span> <span class="p">}</span>
|
||||
<span class="c1">// Specialization for booleans.</span>
|
||||
<span class="k">template</span><span class="o"><></span>
|
||||
<span class="kr">inline</span> <span class="n">PyObject</span><span class="o">*</span> <span class="n">cToPy</span><span class="o"><</span><span class="kt">bool</span><span class="o">></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"><></span> <span class="kr">inline</span> <span class="n">PyObject</span><span class="o">*</span> <span class="n">objectLink</span><span class="o"><</span><span class="k">const</span> <span class="kt">int</span> <span class="o">></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">PyInt_FromLong</span><span class="p">(</span> <span class="n">i</span> <span class="p">);</span> <span class="p">}</span>
|
||||
<span class="c1">// Specialization for STL std::string.</span>
|
||||
<span class="k">template</span><span class="o"><></span> <span class="kr">inline</span> <span class="n">PyObject</span><span class="o">*</span> <span class="n">cToPy</span><span class="o"><</span> <span class="n">std</span><span class="o">::</span><span class="n">string</span><span class="o">></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"><></span> <span class="kr">inline</span> <span class="n">PyObject</span><span class="o">*</span> <span class="n">cToPy</span><span class="o"><</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="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"><></span> <span class="kr">inline</span> <span class="n">PyObject</span><span class="o">*</span> <span class="n">cToPy</span><span class="o"><</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="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">-></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"><></span> <span class="kr">inline</span> <span class="n">PyObject</span><span class="o">*</span> <span class="n">objectLink</span><span class="o"><</span><span class="k">const</span> <span class="kt">int</span><span class="o">*></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">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="c1">// Specialization for POD int.</span>
|
||||
<span class="k">template</span><span class="o"><></span> <span class="kr">inline</span> <span class="n">PyObject</span><span class="o">*</span> <span class="n">cToPy</span><span class="o"><</span> <span class="kt">int</span> <span class="o">></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"><></span> <span class="kr">inline</span> <span class="n">PyObject</span><span class="o">*</span> <span class="n">cToPy</span><span class="o"><</span><span class="k">const</span> <span class="kt">int</span> <span class="o">></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"><></span> <span class="kr">inline</span> <span class="n">PyObject</span><span class="o">*</span> <span class="n">cToPy</span><span class="o"><</span><span class="k">const</span> <span class="kt">int</span><span class="o">*></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>
|
||||
</div>
|
||||
<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>
|
||||
</li>
|
||||
</ul>
|
||||
<p>So, the problem is to change a tuple which size is only kown at <em>runtime</em>
|
||||
into a list of C/C++ parameters known at <em>compile time</em>.</p>
|
||||
<p>So, the problem is to change a Python tuple which size is only kown at
|
||||
<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>
|
||||
solution able to handle any number of parameters. Instead I did write
|
||||
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
|
||||
is kept to a minimum.</p>
|
||||
<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<></span></tt>.</p>
|
||||
object <em>of different types</em>, the obvious choice should have been <tt class="docutils literal"><span class="pre">std::tuple<></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<T></tt> to hold
|
||||
one value of a type (more or less like <tt class="docutils literal"><span class="pre">std::any<></span></tt>).
|
||||
The type of the value attribute of <tt class="docutils literal">Arg<T></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<T>::ValueT</span></tt>.</li>
|
||||
<li>A template list of arguments <tt class="docutils literal"><span class="pre">Args<typename...</span> Ts></tt> analogous to
|
||||
<tt class="docutils literal"><span class="pre">std::tuple<></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<></tt>, which is
|
||||
called through the <tt class="docutils literal"><span class="pre">Args<>::parse()</span></tt> function.</li>
|
||||
</ul>
|
||||
<p>Another challenge is the return type. I distinguish three flavor of
|
||||
return type:</p>
|
||||
<ul class="simple">
|
||||
|
@ -504,49 +598,95 @@ return type:</p>
|
|||
</ul>
|
||||
<p>To uniformize the return type we create four templates <tt class="docutils literal"><span class="pre">_callMethodReturn<>()</span></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<>()</span></tt> to
|
||||
wrap the value.</p>
|
||||
Except for the functions returning <tt class="docutils literal">void</tt>, we call <tt class="docutils literal"><span class="pre">cToPy<>()</span></tt> to
|
||||
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<></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<></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>
|
||||
<div class="highlight"><pre><span></span><span class="c1">// Flavor for "return by pointer".</span>
|
||||
<span class="k">template</span><span class="o"><</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>
|
||||
<div class="highlight"><pre><span></span><span class="c1">// Flavor for "return by value" (seems to match std::is_object<>)</span>
|
||||
<span class="k">template</span><span class="o"><</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"><</span> <span class="o">!</span><span class="n">std</span><span class="o">::</span><span class="n">is_reference</span><span class="o"><</span><span class="n">TR</span><span class="o">>::</span><span class="n">value</span>
|
||||
<span class="o">&&</span> <span class="o">!</span><span class="n">std</span><span class="o">::</span><span class="n">is_pointer</span> <span class="o"><</span><span class="n">TR</span><span class="o">>::</span><span class="n">value</span>
|
||||
<span class="o">&&</span> <span class="o">!</span><span class="n">std</span><span class="o">::</span><span class="n">is_void</span> <span class="o"><</span><span class="n">TR</span><span class="o">>::</span><span class="n">value</span><span class="p">,</span><span class="kt">bool</span><span class="o">>::</span><span class="n">type</span> <span class="o">=</span> <span class="nb">true</span> <span class="o">></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">->*</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 "return by reference"</span>
|
||||
<span class="k">template</span><span class="o"><</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"><</span> <span class="n">std</span><span class="o">::</span><span class="n">is_reference</span><span class="o"><</span><span class="n">TR</span><span class="o">>::</span><span class="n">value</span>
|
||||
<span class="o">&&</span> <span class="o">!</span><span class="n">std</span><span class="o">::</span><span class="n">is_pointer</span> <span class="o"><</span><span class="n">TR</span><span class="o">>::</span><span class="n">value</span>
|
||||
<span class="o">&&</span> <span class="o">!</span><span class="n">std</span><span class="o">::</span><span class="n">is_void</span> <span class="o"><</span><span class="n">TR</span><span class="o">>::</span><span class="n">value</span><span class="p">,</span><span class="kt">bool</span><span class="o">>::</span><span class="n">type</span> <span class="o">=</span> <span class="nb">true</span> <span class="o">></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">->*</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 "return by pointer".</span>
|
||||
<span class="k">template</span><span class="o"><</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"><</span><span class="n">std</span><span class="o">::</span><span class="n">is_pointer</span><span class="o"><</span><span class="n">TR</span><span class="o">>::</span><span class="n">value</span><span class="p">,</span><span class="kt">bool</span><span class="o">>::</span><span class="n">type</span> <span class="o">=</span> <span class="nb">true</span> <span class="o">></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="n">TR</span> <span class="n">pvalue</span> <span class="o">=</span> <span class="p">(</span><span class="n">cppObject</span><span class="o">->*</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="c1">// Flavor for "return void".</span>
|
||||
<span class="k">template</span><span class="o"><</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"><</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"><</span><span class="n">std</span><span class="o">::</span><span class="n">is_void</span><span class="o"><</span><span class="n">TR</span><span class="o">>::</span><span class="n">value</span><span class="p">,</span><span class="kt">bool</span><span class="o">>::</span><span class="n">type</span> <span class="o">=</span> <span class="nb">true</span> <span class="o">></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">cppObject</span><span class="o">->*</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="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"><</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">></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"><></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"><>&</span> <span class="p">)</span>
|
||||
<span class="p">{</span> <span class="k">return</span> <span class="n">_callMethodReturn</span><span class="o"><</span><span class="n">TC</span><span class="p">,</span><span class="n">TR</span><span class="o">></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"><</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">></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"><</span><span class="n">TA0</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">_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"><</span><span class="mi">0</span><span class="o">></span><span class="p">(</span><span class="n">args</span><span class="p">)</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">_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"><</span><span class="n">TA0</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">_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"><</span><span class="n">TA0</span><span class="o">></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"><</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">></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"><</span><span class="n">TA0</span><span class="p">,</span><span class="n">TA1</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">_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"><</span><span class="mi">0</span><span class="o">></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"><</span><span class="mi">1</span><span class="o">></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">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"><</span><span class="n">TA0</span><span class="p">,</span><span class="n">TA1</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">_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"><</span><span class="n">TA0</span><span class="o">></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"><</span><span class="n">TA1</span><span class="o">></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>
|
||||
<p>The complete work of translating the Python tuple into a <tt class="docutils literal"><span class="pre">std::tuple<></span></tt> and error
|
||||
handling is done with a dedicated template class <tt class="docutils literal">PyMethodWrapper</tt> and it's <tt class="docutils literal">call()</tt>
|
||||
method.</p>
|
||||
<p>The complete work of translating the Python tuple into a <tt class="docutils literal">Args<></tt> is done inside
|
||||
a dedicated template class <tt class="docutils literal">PyWrapper</tt> and it's <tt class="docutils literal">call()</tt> method.
|
||||
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<></tt>, template derived class for C/C++ normal functions.</li>
|
||||
<li><tt class="docutils literal">PyMethodWrapper<></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
|
||||
function template which can perform the guess. The <tt class="docutils literal">callMethod<></tt> template function.</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">"Parameter.addValue"</span><span class="p">,</span><span class="o">&</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>
|
||||
|
@ -561,7 +701,7 @@ function template which can perform the guess. The <tt class="docutils literal">
|
|||
</div>
|
||||
<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>
|
||||
<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
|
||||
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
|
||||
|
@ -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="k">return</span> <span class="n">self</span><span class="o">-></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="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">"Parameter.setString"</span><span class="p">,</span><span class="o">&</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">"Parameter.setString"</span><span class="p">,</span><span class="o">&</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
|
||||
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"><></span>
|
||||
<span class="kr">inline</span> <span class="kt">void</span> <span class="n">pyTypePostModuleInit</span><span class="o"><</span><span class="n">Cfg</span><span class="o">::</span><span class="n">Parameter</span><span class="o">></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"><</span><span class="n">Cfg</span><span class="o">::</span><span class="n">Parameter</span><span class="o">></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="n">PyTypeManagerNonDBo</span><span class="o"><</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">>::</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>
|
||||
|
@ -640,7 +780,7 @@ the <tt class="docutils literal">Priority</tt> <tt class="docutils literal">enum
|
|||
<span class="p">}</span>
|
||||
|
||||
<span class="k">template</span><span class="o"><></span>
|
||||
<span class="kr">inline</span> <span class="kt">void</span> <span class="n">pyTypePostModuleInit</span><span class="o"><</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">></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"><</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">></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="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">"UseDefault"</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.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
@ -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;
|
||||
}
|
||||
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
|
||||
PyObject* exceptionWrapper ( PyWrapper& wrapper, PyObject* self, PyObject* args )
|
||||
PyObject* exceptionWrapper ( PyWrapper* wrapper, PyObject* self, PyObject* args )
|
||||
{
|
||||
try {
|
||||
return wrapper.call( self, args );
|
||||
return wrapper->_call( self, args );
|
||||
} catch ( const Warning& w ) {
|
||||
wrapper.message() += "\n" + getString(w);
|
||||
wrapper->message() += "\n" + getString(w);
|
||||
} catch ( const Error& e ) {
|
||||
wrapper.message() += "\n" + getString(e);
|
||||
if (not e.where().empty()) wrapper.message() += "\n" + e.where();
|
||||
wrapper->message() += "\n" + getString(e);
|
||||
if (not e.where().empty()) wrapper->message() += "\n" + e.where();
|
||||
} catch ( const Bug& e ) {
|
||||
wrapper.message() += "\n" + getString(e);
|
||||
wrapper->message() += "\n" + getString(e);
|
||||
} catch ( const Exception& e ) {
|
||||
wrapper.message() += "\nUnknown Hurricane::Exception";
|
||||
wrapper->message() += "\nUnknown Hurricane::Exception";
|
||||
} catch ( const std::exception& e ) {
|
||||
wrapper.message() += "\n" + std::string(e.what());
|
||||
wrapper->message() += "\n" + std::string(e.what());
|
||||
} catch ( ... ) {
|
||||
wrapper.message() += "\nUnmanaged exception, neither a Hurricane::Error nor"
|
||||
" a std::exception.";
|
||||
wrapper->message() += "\nUnmanaged exception, neither a Hurricane::Error nor"
|
||||
" a std::exception.";
|
||||
}
|
||||
PyErr_SetString( HurricaneError, wrapper.message().c_str() );
|
||||
PyErr_SetString( HurricaneError, wrapper->message().c_str() );
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
PyObject* exceptionWrapper ( PyWrapper& wrapper, PyObject* args )
|
||||
PyObject* exceptionWrapper ( PyWrapper* wrapper, PyObject* args )
|
||||
{
|
||||
try {
|
||||
return wrapper.call( args );
|
||||
return wrapper->_call( args );
|
||||
} catch ( const Warning& w ) {
|
||||
wrapper.message() += "\n" + getString(w);
|
||||
wrapper->message() += "\n" + getString(w);
|
||||
} catch ( const Error& e ) {
|
||||
wrapper.message() += "\n" + getString(e);
|
||||
if (not e.where().empty()) wrapper.message() += "\n" + e.where();
|
||||
wrapper->message() += "\n" + getString(e);
|
||||
if (not e.where().empty()) wrapper->message() += "\n" + e.where();
|
||||
} catch ( const Bug& e ) {
|
||||
wrapper.message() += "\n" + getString(e);
|
||||
wrapper->message() += "\n" + getString(e);
|
||||
} catch ( const Exception& e ) {
|
||||
wrapper.message() += "\nUnknown Hurricane::Exception";
|
||||
wrapper->message() += "\nUnknown Hurricane::Exception";
|
||||
} catch ( const std::exception& e ) {
|
||||
wrapper.message() += "\n" + std::string(e.what());
|
||||
wrapper->message() += "\n" + std::string(e.what());
|
||||
} catch ( ... ) {
|
||||
wrapper.message() += "\nUnmanaged exception, neither a Hurricane::Error nor"
|
||||
" a std::exception.";
|
||||
wrapper->message() += "\nUnmanaged exception, neither a Hurricane::Error nor"
|
||||
" a std::exception.";
|
||||
}
|
||||
PyErr_SetString( HurricaneError, wrapper.message().c_str() );
|
||||
PyErr_SetString( HurricaneError, wrapper->message().c_str() );
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
int exceptionPredicateWrapper ( PyWrapper& wrapper, PyObject* self )
|
||||
int exceptionPredicateWrapper ( PyWrapper* wrapper, PyObject* self )
|
||||
{
|
||||
try {
|
||||
return wrapper.predicate( self );
|
||||
return wrapper->_predicate( self );
|
||||
} catch ( const Warning& w ) {
|
||||
wrapper.message() += "\n" + getString(w);
|
||||
wrapper->message() += "\n" + getString(w);
|
||||
} catch ( const Error& e ) {
|
||||
wrapper.message() += "\n" + getString(e);
|
||||
if (not e.where().empty()) wrapper.message() += "\n" + e.where();
|
||||
wrapper->message() += "\n" + getString(e);
|
||||
if (not e.where().empty()) wrapper->message() += "\n" + e.where();
|
||||
} catch ( const Bug& e ) {
|
||||
wrapper.message() += "\n" + getString(e);
|
||||
wrapper->message() += "\n" + getString(e);
|
||||
} catch ( const Exception& e ) {
|
||||
wrapper.message() += "\nUnknown Hurricane::Exception";
|
||||
wrapper->message() += "\nUnknown Hurricane::Exception";
|
||||
} catch ( const std::exception& e ) {
|
||||
wrapper.message() += "\n" + std::string(e.what());
|
||||
wrapper->message() += "\n" + std::string(e.what());
|
||||
} catch ( ... ) {
|
||||
wrapper.message() += "\nUnmanaged exception, neither a Hurricane::Error nor"
|
||||
" a std::exception.";
|
||||
wrapper->message() += "\nUnmanaged exception, neither a Hurricane::Error nor"
|
||||
" a std::exception.";
|
||||
}
|
||||
PyErr_SetString( HurricaneError, wrapper.message().c_str() );
|
||||
PyErr_SetString( HurricaneError, wrapper->message().c_str() );
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
|
@ -1576,28 +1576,6 @@ namespace Isobar3 {
|
|||
// 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
|
||||
, typename std::enable_if< !std::is_pointer<T>::value, bool >::type = true >
|
||||
inline bool pyToC ( PyObject* pyArg, T* arg )
|
||||
|
@ -1754,23 +1732,28 @@ namespace Isobar3 {
|
|||
inline PyWrapper ();
|
||||
virtual ~PyWrapper ();
|
||||
inline std::string& message ();
|
||||
virtual PyObject* call ( PyObject* self, PyObject* args );
|
||||
virtual PyObject* call ( PyObject* args );
|
||||
virtual int predicate ( PyObject* self );
|
||||
inline PyObject* call ( PyObject* self, PyObject* args );
|
||||
inline PyObject* call ( PyObject* args );
|
||||
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 );
|
||||
private:
|
||||
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 std::string& PyWrapper::message () { return _message; }
|
||||
inline void PyWrapper::setMessage ( std::string header ) { _message = header; }
|
||||
|
||||
|
||||
extern PyObject* exceptionWrapper ( PyWrapper& wrapper, PyObject* self, PyObject* args );
|
||||
extern PyObject* exceptionWrapper ( PyWrapper& wrapper, PyObject* args );
|
||||
extern int exceptionPredicateWrapper ( PyWrapper& wrapper, PyObject* self );
|
||||
inline PyObject* PyWrapper::call ( PyObject* self, PyObject* args ) { return exceptionWrapper( this, self, args ); }
|
||||
inline PyObject* PyWrapper::call ( PyObject* args ) { return exceptionWrapper( this, args ); }
|
||||
inline int PyWrapper::predicate ( PyObject* self ) { return exceptionPredicateWrapper( this, self ); }
|
||||
|
||||
|
||||
// -------------------------------------------------------------------
|
||||
|
@ -1785,7 +1768,7 @@ namespace Isobar3 {
|
|||
inline PyFunctionWrapper ( std::string fname, FunctionType method )
|
||||
: PyWrapper(), _funcName(fname), _method(method) { };
|
||||
inline std::string funcName () const { return _funcName; };
|
||||
virtual PyObject* call ( PyObject* fargs );
|
||||
virtual PyObject* _call ( PyObject* fargs );
|
||||
private:
|
||||
std::string _funcName;
|
||||
FunctionType _method;
|
||||
|
@ -1793,7 +1776,7 @@ namespace Isobar3 {
|
|||
|
||||
|
||||
template< typename TR, typename... TArgs >
|
||||
PyObject* PyFunctionWrapper<TR,TArgs...>::call ( PyObject* fargs )
|
||||
PyObject* PyFunctionWrapper<TR,TArgs...>::_call ( PyObject* fargs )
|
||||
{
|
||||
PyErr_Clear();
|
||||
//std::cerr << "_call() " << demangle(typeid(FunctionType).name()) << std::endl;
|
||||
|
@ -1855,7 +1838,7 @@ namespace Isobar3 {
|
|||
, _fMethod(method)
|
||||
{ };
|
||||
inline std::string funcName () const { return _funcName; };
|
||||
virtual PyObject* call ( PyObject* self, PyObject* fargs );
|
||||
virtual PyObject* _call ( PyObject* self, PyObject* fargs );
|
||||
private:
|
||||
std::string _funcName;
|
||||
OMethodType _oMethod;
|
||||
|
@ -1864,7 +1847,7 @@ namespace Isobar3 {
|
|||
|
||||
|
||||
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();
|
||||
size_t nargs = sizeof...(TArgs);
|
||||
|
@ -1925,7 +1908,7 @@ namespace Isobar3 {
|
|||
, _oMethod(mbinary)
|
||||
{ };
|
||||
inline std::string funcName () const { return _funcName; };
|
||||
virtual PyObject* call ( PyObject* self, PyObject* arg );
|
||||
virtual PyObject* _call ( PyObject* self, PyObject* arg );
|
||||
private:
|
||||
std::string _funcName;
|
||||
OMethodType _oMethod;
|
||||
|
@ -1933,7 +1916,7 @@ namespace Isobar3 {
|
|||
|
||||
|
||||
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();
|
||||
setMessage( funcName() + "(): " );
|
||||
|
@ -1962,14 +1945,14 @@ namespace Isobar3 {
|
|||
, _funcName (fname)
|
||||
{ };
|
||||
inline std::string funcName () const { return _funcName; };
|
||||
virtual PyObject* call ( PyObject* self, PyObject* arg );
|
||||
virtual PyObject* _call ( PyObject* self, PyObject* arg );
|
||||
private:
|
||||
std::string _funcName;
|
||||
};
|
||||
|
||||
|
||||
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();
|
||||
setMessage( funcName() + "(): " );
|
||||
|
@ -2002,7 +1985,7 @@ namespace Isobar3 {
|
|||
, _oInPlace(minplace)
|
||||
{ };
|
||||
inline std::string funcName () const { return _funcName; };
|
||||
virtual PyObject* call ( PyObject* self, PyObject* arg );
|
||||
virtual PyObject* _call ( PyObject* self, PyObject* arg );
|
||||
private:
|
||||
std::string _funcName;
|
||||
OInPlaceType _oInPlace;
|
||||
|
@ -2010,7 +1993,7 @@ namespace Isobar3 {
|
|||
|
||||
|
||||
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();
|
||||
setMessage( funcName() + "(): " );
|
||||
|
@ -2044,7 +2027,7 @@ namespace Isobar3 {
|
|||
, _oPredicate(pred)
|
||||
{ };
|
||||
inline std::string funcName () const { return _funcName; };
|
||||
virtual int predicate ( PyObject* self );
|
||||
virtual int _predicate ( PyObject* self );
|
||||
private:
|
||||
std::string _funcName;
|
||||
OPredicateType _oPredicate;
|
||||
|
@ -2052,7 +2035,7 @@ namespace Isobar3 {
|
|||
|
||||
|
||||
template< typename TC >
|
||||
inline int PyPredicateWrapper<TC>::predicate ( PyObject* self )
|
||||
inline int PyPredicateWrapper<TC>::_predicate ( PyObject* self )
|
||||
{
|
||||
PyErr_Clear();
|
||||
setMessage( funcName() + "(): " );
|
||||
|
|
Loading…
Reference in New Issue