833 lines
91 KiB
HTML
833 lines
91 KiB
HTML
<!DOCTYPE html>
|
|
<html lang="en">
|
|
<head>
|
|
<meta charset="utf-8">
|
|
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
<meta name="description" content="Printable version of this Document Contents Printable version of this Document 1. Introduction 1.1 About Technical Choices 2. Implementation 2.1 PyTypeManager 2.2 Highjacking the tp methods 2.3...">
|
|
<meta name="keywords" content="">
|
|
<link rel="icon" href="../favicon.ico">
|
|
|
|
<title>Hurricane Python/C++ API Tutorial (template) - Coriolis VLSI CAD Tools [offline]</title>
|
|
|
|
<!-- Stylesheets -->
|
|
<link href="../theme/css/bootstrap.css" rel="stylesheet">
|
|
<link href="../theme/css/fonts.css" rel="stylesheet">
|
|
<link href="../theme/css/nest.css" rel="stylesheet">
|
|
<link href="../theme/css/pygment.css" rel="stylesheet">
|
|
<link href="../theme/css/coriolis.css" rel="stylesheet">
|
|
<!-- /Stylesheets -->
|
|
|
|
<script src="../theme/js/jquery.min.js"></script>
|
|
<script src="../theme/js/bootstrap.min.js"></script>
|
|
|
|
<!-- RSS Feeds -->
|
|
<!-- /RSS Feeds -->
|
|
|
|
<!-- HTML5 shim and Respond.js for IE8 support of HTML5 elements and media queries -->
|
|
<!--[if lt IE 9]>
|
|
<script src="https://oss.maxcdn.com/html5shiv/3.7.2/html5shiv.min.js"></script>
|
|
<script src="https://oss.maxcdn.com/respond/1.4.2/respond.min.js"></script>
|
|
<![endif]-->
|
|
|
|
|
|
|
|
</head>
|
|
|
|
<body>
|
|
<!-- Header -->
|
|
<div class="header-container" style="background: linear-gradient(rgba(0, 0, 0, 0.2), rgba(0, 0, 0, 0.2)), url('../images/common/layout-motif-faded-4.png'); background-position: center; ">
|
|
|
|
|
|
<!--
|
|
<div class="container">
|
|
<nav class="navbar navbar-default">
|
|
<div class="container-fluid">
|
|
<div class="navbar-header">
|
|
<a class="navbar-brand" href="../"><img class="mr20" src="../images/common/Coriolis-logo-white-4-small.png" alt="logo">Coriolis VLSI CAD Tools [offline]</a>
|
|
</div>
|
|
<ul class="nav navbar-nav">
|
|
<li><a href="../pages/gitlab.html">Git</a></li>
|
|
<li><a href="../pages/documentation.html">Documentation</a></li>
|
|
<li class="dropdown">
|
|
<button class="btn btn-default dropdown-toggle" type="button" data-toggle="dropdown" aria-haspopup="true" aria-expanded="true">
|
|
<span class="caret"></span>Topics
|
|
</button>
|
|
<ul class="dropdown-menu">
|
|
<li><a class="dropdown-item " href="../pages/.html"></a></li>
|
|
<li><a class="dropdown-item " href="../pages/homepage.html">Coriolis <span class="sc">vlsi</span> Backend Tools</a></li>
|
|
<li><a class="dropdown-item " href="../pages/symbolic-layout.html">Symbolic Layout</a></li>
|
|
</ul>
|
|
</li>
|
|
</ul>
|
|
</div>
|
|
</nav>
|
|
</div> <!-- navbar container -->
|
|
-->
|
|
|
|
<!-- Static navbar -->
|
|
<div class="container">
|
|
<div class="header-nav">
|
|
<div class="header-logo">
|
|
<a class="pull-left" href="../"><img class="mr20" src="../images/common/Coriolis-logo-white-4-small.png" alt="logo">Coriolis VLSI CAD Tools [offline]</a>
|
|
</div>
|
|
<div class="nav pull-right">
|
|
<a href="../pages/gitlab.html">Git</a>
|
|
<a href="../pages/documentation.html">Documentation</a>
|
|
</div>
|
|
<div class="nav pull-right">
|
|
<div class="dropdown">
|
|
<button class="btn btn-default dropdown-toggle" type="button" data-toggle="dropdown" aria-haspopup="true" aria-expanded="true">
|
|
<span class="caret"></span>Topics
|
|
</button>
|
|
<ul class="dropdown-menu">
|
|
<li><a class="dropdown-item " href="../pages/.html"></a></li>
|
|
<li><a class="dropdown-item " href="../pages/homepage.html">Coriolis <span class="sc">vlsi</span> Backend Tools</a></li>
|
|
<li><a class="dropdown-item " href="../pages/symbolic-layout.html">Symbolic Layout</a></li>
|
|
</ul>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<!-- /Static navbar -->
|
|
|
|
<!-- Header -->
|
|
<div class="container header-wrapper">
|
|
<div class="row">
|
|
<div class="col-lg-12">
|
|
<div class="header-content">
|
|
<a href="https://www.lip6.fr/"><img class="mr20" height="60px" src="../images/common/LogoLIP6Blanc.png" alt="LIP6"></a>
|
|
<a href="https://www.sorbonne-universite.fr/"><img class="mr20" height="60px" src="../images/common/logo-SU-blanc-700px.png" alt="Sorbonne Universite"></a>
|
|
<a href="https://www.cnrs.fr/"><img class="mr20" height="60px" src="../images/common/LOGO-cnrs-white-large.png" alt="CNRS"></a>
|
|
<h1 class="header-title text-uppercase">Hurricane Python/C++ API Tutorial (template)</h1>
|
|
<div class="header-underline"></div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<!-- /Header -->
|
|
|
|
</div>
|
|
<!-- /Header -->
|
|
|
|
|
|
<!-- Content -->
|
|
<div class="container content">
|
|
<!-- -*- mode: rst; explicit-buffer-name: "PythonCppNew_HTML.rst<pelican>" -*- -->
|
|
<!-- -*- Mode: rst; explicit-buffer-name: "definition.rst<documentation/etc>" -*- -->
|
|
<!-- HTML/LaTeX backends mixed macros. -->
|
|
<!-- Acronyms & names. -->
|
|
<!-- URLs -->
|
|
<!-- Standard CAO/VLSI Concepts. -->
|
|
<!-- Alliance & MBK Concepts -->
|
|
<!-- Hurricane Concepts. -->
|
|
<div class="section" id="printable-version-of-this-document">
|
|
<h2><a class="toc-backref" href="#id2">Printable version of this Document</a></h2>
|
|
<div class="contents topic" id="contents">
|
|
<p class="topic-title">Contents</p>
|
|
<ul class="simple">
|
|
<li><a class="reference internal" href="#printable-version-of-this-document" id="id2">Printable version of this Document</a></li>
|
|
<li><a class="reference internal" href="#introduction" id="id3">1. Introduction</a><ul>
|
|
<li><a class="reference internal" href="#about-technical-choices" id="id4">1.1 About Technical Choices</a></li>
|
|
</ul>
|
|
</li>
|
|
<li><a class="reference internal" href="#implementation" id="id5">2. Implementation</a><ul>
|
|
<li><a class="reference internal" href="#pytypemanager" id="id6">2.1 PyTypeManager</a></li>
|
|
<li><a class="reference internal" href="#highjacking-the-tp-methods" id="id7">2.2 Highjacking the <em>tp</em> methods</a></li>
|
|
<li><a class="reference internal" href="#going-from-python-to-c" id="id8">2.3 Going From Python to C++</a></li>
|
|
<li><a class="reference internal" href="#going-from-c-to-python" id="id9">2.4 Going From C++ to Python</a></li>
|
|
<li><a class="reference internal" href="#object-methods-wrappers" id="id10">2.5 Object Methods Wrappers</a></li>
|
|
<li><a class="reference internal" href="#case-of-c-overloaded-functions" id="id11">2.6 Case of C++ overloaded functions</a></li>
|
|
<li><a class="reference internal" href="#wrapper-for-ordinary-functions" id="id12">2.7 Wrapper for ordinary functions</a></li>
|
|
<li><a class="reference internal" href="#object-post-create-hook" id="id13">2.8 Object post-create hook</a></li>
|
|
</ul>
|
|
</li>
|
|
</ul>
|
|
</div>
|
|
<!-- -*- Mode: rst -*- -->
|
|
</div>
|
|
<div class="section" id="introduction">
|
|
<h2><a class="toc-backref" href="#id3">1. Introduction</a></h2>
|
|
<ul class="simple">
|
|
<li>This document is written for people already familiar with the
|
|
<a class="reference external" href="https://docs.python.org/2/c-api/index.html">Python/C API Reference Manual</a>.</li>
|
|
<li>The macros provided by the Hurricane Python/C API are written using
|
|
the standard Python C/API. That is, you may not use them and write
|
|
directly your functions with the original API or any mix between.
|
|
You only have to respect some naming convention.</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>
|
|
<p>Some would say, why not use <em>off the shelf</em> wrappers like <tt class="docutils literal">swig</tt>,
|
|
<tt class="docutils literal"><span class="pre">boost::python</span></tt> or <tt class="docutils literal">pybind11</tt>, here are some clues.</p>
|
|
<ol class="arabic">
|
|
<li><p class="first"><strong>Partial exposure of the C++ class tree.</strong> We expose at Python level
|
|
C++ base classes, only if they provides common methods that we want
|
|
to see. Otherwise, we just show them as base classes under Python.
|
|
For instance <tt class="docutils literal">Library</tt> is derived from <tt class="docutils literal">DBo</tt>, but we won't see
|
|
it under Python.</p>
|
|
</li>
|
|
<li><p class="first"><strong>Bi-directional communication.</strong> When a Python object is deleted, the
|
|
wrapper obviously has a pointer toward the underlying C++ object and
|
|
is able to delete it. But, the reverse case can occurs, meaning that
|
|
you have a C++ object wrapped in Python and the database delete the
|
|
underlying object. The wrapped Python object <em>must</em> be informed that
|
|
it no longer refer a valid C++ one. Moreover, as we do not control
|
|
when Python objects gets deleteds (that is, when their reference count
|
|
reaches zero), we can have valid Python object with a dangling
|
|
C++ pointer. So our Python objects can be warned by the C++ objects
|
|
that they are no longer valid and any other operation than the
|
|
deletion should result in a severe non-blocking error.</p>
|
|
<p>To be precise, this apply to persistent object in the C++ database,
|
|
like <tt class="docutils literal">Cell</tt>, <tt class="docutils literal">Net</tt>, <tt class="docutils literal">Instance</tt> or <tt class="docutils literal">Component</tt>. Short lived
|
|
objects like <tt class="docutils literal">Box</tt> or <tt class="docutils literal">Point</tt> retains the classic Python behavior.</p>
|
|
<p>Another aspect is that, for all derived <tt class="docutils literal">DBo</tt> objects, one and only
|
|
one Python object is associated. For one given <tt class="docutils literal">Instance</tt> object we
|
|
will always return the <em>same</em> <tt class="docutils literal">PyInstance</tt> object, thanks to the
|
|
bi-directional link. Obviously, the <em>reference count</em> of the
|
|
<tt class="docutils literal">PyInstance</tt> is managed accordingly. This mechanism is implemented
|
|
by the <tt class="docutils literal"><span class="pre">PyTypeManager::_link()</span></tt> method.</p>
|
|
</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 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 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,
|
|
(<tt class="docutils literal">Capsule</tt>) but I did not fully understand it.</p>
|
|
<p>Instead, we register all the newly defined Python type object
|
|
in the <tt class="docutils literal">PyTypeManager</tt> and we link the associated C++ library
|
|
into all Python modules. So all types and ancillary functions can
|
|
easily be seen accross modules.</p>
|
|
<p>This way, we do not rely upon a pointer transmission through Python
|
|
modules, but directly uses linker capabilities.</p>
|
|
<p><strong>The PyTypeManager</strong> approach also suppress the need to <em>split</em> into
|
|
two libraries the Python modules like in the C-Macros implementation,
|
|
and the double compilation pass.</p>
|
|
</li>
|
|
</ol>
|
|
<!-- -*- Mode: rst -*- -->
|
|
</div>
|
|
</div>
|
|
<div class="section" id="implementation">
|
|
<h2><a class="toc-backref" href="#id5">2. Implementation</a></h2>
|
|
<p>We do not try to provides an interface as sleek as <tt class="docutils literal">pybind11</tt> that completely
|
|
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 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>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">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">PyOneVoid</span> <span class="p">{</span>
|
|
<span class="n">PyObject_HEAD</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">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">PyTwoVoid</span> <span class="p">{</span>
|
|
<span class="n">PyObject_HEAD</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="admonition 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 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 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>
|
|
</div>
|
|
<div class="section" id="highjacking-the-tp-methods">
|
|
<h3><a class="toc-backref" href="#id7">2.2 Highjacking the <em>tp</em> methods</a></h3>
|
|
<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 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>
|
|
|
|
<span class="k">extern</span> <span class="s">"C"</span> <span class="p">{</span>
|
|
|
|
<span class="c1">// Here we have C-linkage.</span>
|
|
<span class="k">extern</span> <span class="kt">long</span> <span class="n">_tpHash</span> <span class="p">(</span> <span class="n">PyObject</span><span class="o">*</span> <span class="n">self</span> <span class="p">)</span>
|
|
<span class="p">{</span>
|
|
<span class="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">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>
|
|
|
|
<span class="k">class</span> <span class="nc">PyTypeManager</span> <span class="p">{</span>
|
|
<span class="k">public</span><span class="o">:</span>
|
|
<span class="kt">void</span> <span class="n">PyTypeManager</span><span class="o">::</span><span class="n">_setupPyType</span> <span class="p">()</span>
|
|
<span class="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">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="nc">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>
|
|
<span class="n">PyTypeObject</span> <span class="n">_typeObject</span><span class="p">;</span>
|
|
<span class="p">};</span>
|
|
|
|
<span class="kt">void</span> <span class="n">PyTypeManager</span><span class="o">::</span><span class="n">_setupPyType</span> <span class="p">()</span>
|
|
<span class="p">{</span>
|
|
<span class="n">PyTypeObject</span><span class="o">*</span> <span class="n">ob_type</span> <span class="o">=</span> <span class="n">_getTypeObject</span><span class="p">();</span>
|
|
<span class="n">ob_type</span><span class="o">-></span><span class="n">tp_name</span> <span class="o">=</span> <span class="n">_getPyTypeName</span><span class="p">().</span><span class="n">c_str</span><span class="p">();</span>
|
|
<span class="n">ob_type</span><span class="o">-></span><span class="n">tp_dealloc</span> <span class="o">=</span> <span class="p">(</span><span class="n">destructor</span><span class="p">)</span><span class="o">&::</span><span class="n">Isobar3</span><span class="o">::</span><span class="n">_tpDeAlloc</span><span class="p">;</span>
|
|
<span class="n">ob_type</span><span class="o">-></span><span class="n">tp_str</span> <span class="o">=</span> <span class="p">(</span><span class="n">reprfunc</span><span class="p">)</span> <span class="o">&::</span><span class="n">Isobar3</span><span class="o">::</span><span class="n">_tpStr</span><span class="p">;</span>
|
|
<span class="c1">// All Python Type will call the same _tpHash().</span>
|
|
<span class="n">ob_type</span><span class="o">-></span><span class="n">tp_hash</span> <span class="o">=</span> <span class="p">(</span><span class="n">hashfunc</span><span class="p">)</span> <span class="o">&::</span><span class="n">Isobar3</span><span class="o">::</span><span class="n">_tpHash</span><span class="p">;</span>
|
|
<span class="n">ob_type</span><span class="o">-></span><span class="n">tp_compare</span> <span class="o">=</span> <span class="p">(</span><span class="n">cmpfunc</span><span class="p">)</span> <span class="o">&::</span><span class="n">Isobar3</span><span class="o">::</span><span class="n">_getTpCompare</span><span class="p">;</span>
|
|
<span class="n">ob_type</span><span class="o">-></span><span class="n">tp_methods</span> <span class="o">=</span> <span class="n">_getMethods</span><span class="p">();</span>
|
|
<span class="n">ob_type</span><span class="o">-></span><span class="n">tp_getset</span> <span class="o">=</span> <span class="n">_getGetsets</span><span class="p">();</span>
|
|
<span class="p">}</span>
|
|
|
|
<span class="p">}</span> <span class="c1">// Isobar3 namespace.</span>
|
|
</pre></div>
|
|
</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 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>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="admonition 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="nc">T</span>
|
|
<span class="p">,</span> <span class="k">typename</span> <span class="nc">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="k">typedef</span> <span class="k">typename</span> <span class="nc">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/Pointer to a pointer flavor.</span>
|
|
<span class="k">template</span><span class="o"><</span><span class="k">typename</span> <span class="nc">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="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>
|
|
</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">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
|
|
misunderstood the template resolution mechanism).</p>
|
|
<p>In the case of object registered in <tt class="docutils literal">PyTypeManager</tt>, we delegate
|
|
the <tt class="docutils literal">PyObject</tt> creation to the <tt class="docutils literal"><span class="pre">PyTypeManager::link()</span></tt> template
|
|
function, which in turn, can call the right <tt class="docutils literal"><span class="pre">PyTypeManagerVTrunk<CppT>::_link()</span></tt>
|
|
method.</p>
|
|
<div class="admonition note">
|
|
<p class="first admonition-title">Note</p>
|
|
<p class="last">The <tt class="docutils literal"><span class="pre">PyTypeManagerVTrunk<CppT>::_link()</span></tt> method is the reason
|
|
<strong>why</strong> we need the intermediate <tt class="docutils literal">PyTypeManagerVTrunk<CppT></tt>
|
|
template class.</p>
|
|
</div>
|
|
<div class="admonition 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="nc">CppT</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="n">object</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">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">// Disabled for POD & STL types, pointer flavor.</span>
|
|
<span class="k">template</span><span class="o"><</span> <span class="k">typename</span> <span class="nc">CppT</span>
|
|
<span class="p">,</span> <span class="k">typename</span> <span class="nc">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">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">// 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="nc">CppT</span>
|
|
<span class="p">,</span> <span class="k">typename</span> <span class="nc">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">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">// 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="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="nf">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="nf">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="nf">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="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="nf">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="nf">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="nf">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">
|
|
<h3><a class="toc-backref" href="#id10">2.5 Object Methods Wrappers</a></h3>
|
|
<p>One of the more tedious task in exporting a C++ interface towards Python is
|
|
to have wrap the C++ functions/methods into C-linkage functions that can be
|
|
put into the <tt class="docutils literal">PyMethodDef</tt> table.</p>
|
|
<p>Basically, we want to fit:</p>
|
|
<ul>
|
|
<li><p class="first">A C++ function or method with a variable number of arguments, each argument
|
|
having it's own type.</p>
|
|
<div class="highlight"><pre><span></span><span class="k">class</span> <span class="nc">Parameter</span> <span class="p">{</span>
|
|
<span class="c1">// ...</span>
|
|
<span class="k">public</span><span class="o">:</span>
|
|
<span class="kt">void</span> <span class="n">addValue</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="kt">int</span> <span class="n">v</span> <span class="p">);</span>
|
|
<span class="c1">// ...</span>
|
|
<span class="p">};</span>
|
|
</pre></div>
|
|
</li>
|
|
<li><p class="first">Into a <tt class="docutils literal">PyCFunction</tt> prototype.</p>
|
|
<div class="highlight"><pre><span></span><span class="k">extern</span> <span class="s">"C"</span> <span class="p">{</span>
|
|
<span class="k">typedef</span> <span class="n">PyObject</span><span class="o">*</span> <span class="p">(</span> <span class="o">*</span><span class="n">PyCFunction</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>
|
|
</pre></div>
|
|
<p>Here, the C++ object is provided through the first argument and the
|
|
functions arguments through a <em>tuple</em> in second argument. In Python
|
|
wrappers, the tuple doesn't have any complex structure, it boils down
|
|
to a sequence of <tt class="docutils literal">PyObject*</tt> (that must match the number of arguments
|
|
of it's C++ function conterpart).</p>
|
|
</li>
|
|
</ul>
|
|
<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.
|
|
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 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">
|
|
<li>Function returning nothing (<tt class="docutils literal">void</tt>).</li>
|
|
<li>Function returning a value.</li>
|
|
<li>Function returning a reference to a value.</li>
|
|
<li>Function returning a pointer.</li>
|
|
</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">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="admonition 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 value" (seems to match std::is_object<>)</span>
|
|
<span class="k">template</span><span class="o"><</span> <span class="k">typename</span> <span class="nc">TC</span><span class="p">,</span> <span class="k">typename</span> <span class="nc">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="nc">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="nc">TC</span><span class="p">,</span> <span class="k">typename</span> <span class="nc">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="nc">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="nc">TC</span><span class="p">,</span> <span class="k">typename</span> <span class="nc">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="nc">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">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">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="nc">TC</span><span class="p">,</span> <span class="k">typename</span> <span class="nc">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="nc">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">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">// 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="nc">TC</span><span class="p">,</span> <span class="k">typename</span> <span class="nc">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">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">// 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="nc">TC</span><span class="p">,</span> <span class="k">typename</span> <span class="nc">TR</span><span class="p">,</span> <span class="k">typename</span> <span class="nc">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">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="nf">_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">// 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="nc">TC</span><span class="p">,</span> <span class="k">typename</span> <span class="nc">TR</span><span class="p">,</span> <span class="k">typename</span> <span class="nc">TA0</span><span class="p">,</span> <span class="k">typename</span> <span class="nc">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">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="nf">_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">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="n">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="nf">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>
|
|
<span class="p">{</span> <span class="p">{</span> <span class="s">"isFile"</span> <span class="p">,</span> <span class="p">(</span><span class="n">PyCFunction</span><span class="p">)</span><span class="n">PyParameter_isFile</span> <span class="p">,</span> <span class="n">METH_NOARGS</span>
|
|
<span class="p">,</span> <span class="s">"Tells if this parameter (string) holds a file name."</span> <span class="p">}</span>
|
|
<span class="p">,</span> <span class="p">{</span> <span class="s">"addValue"</span><span class="p">,</span> <span class="p">(</span><span class="n">PyCFunction</span><span class="p">)</span><span class="n">PyParameter_addValue</span><span class="p">,</span> <span class="n">METH_VARARGS</span>
|
|
<span class="p">,</span> <span class="s">"Add a new value to parameter of enumerated type."</span> <span class="p">}</span>
|
|
<span class="c1">// ...</span>
|
|
<span class="p">,</span> <span class="p">{</span><span class="nb">NULL</span><span class="p">,</span> <span class="nb">NULL</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="nb">NULL</span><span class="p">}</span> <span class="cm">/* sentinel */</span>
|
|
<span class="p">};</span>
|
|
</pre></div>
|
|
</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 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
|
|
parameters order.</p>
|
|
<div class="admonition note">
|
|
<p class="first admonition-title">Note</p>
|
|
<p class="last">If something goes wrong in a <tt class="docutils literal">callMethod()</tt>, it returns <tt class="docutils literal">NULL</tt> and
|
|
sets an error exception. If, say, the <tt class="docutils literal">setString3()</tt> variant fails,
|
|
but <tt class="docutils literal">setString2()</tt> succeed, it will clear the error and sets <tt class="docutils literal">rvalue</tt>
|
|
to something non-<tt class="docutils literal">NULL</tt>.</p>
|
|
</div>
|
|
<p>You may also notice that the signature of an un-overloaded function is that
|
|
of a normal function, not a class method, with the object (aka C++ <tt class="docutils literal">this</tt>
|
|
passed as the first argument). So <tt class="docutils literal">callMethod()</tt> and <tt class="docutils literal">PyMethodWrapper</tt>
|
|
support both case (through different constructors).</p>
|
|
<div class="highlight"><pre><span></span><span class="k">static</span> <span class="kt">bool</span> <span class="n">setString1</span> <span class="p">(</span> <span class="n">Parameter</span><span class="o">*</span> <span class="n">self</span><span class="p">,</span> <span class="n">std</span><span class="o">::</span><span class="n">string</span> <span class="n">value</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="p">}</span>
|
|
|
|
<span class="k">static</span> <span class="kt">bool</span> <span class="n">setString2</span> <span class="p">(</span> <span class="n">Parameter</span><span class="o">*</span> <span class="n">self</span><span class="p">,</span> <span class="n">std</span><span class="o">::</span><span class="n">string</span> <span class="n">value</span><span class="p">,</span> <span class="kt">unsigned</span> <span class="kt">int</span> <span class="n">flags</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">Configuration</span><span class="o">::</span><span class="n">getDefaultPriority</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="kt">bool</span> <span class="n">setString3</span> <span class="p">(</span> <span class="n">Parameter</span><span class="o">*</span> <span class="n">self</span>
|
|
<span class="p">,</span> <span class="n">std</span><span class="o">::</span><span class="n">string</span> <span class="n">value</span>
|
|
<span class="p">,</span> <span class="kt">unsigned</span> <span class="kt">int</span> <span class="n">flags</span>
|
|
<span class="p">,</span> <span class="n">Parameter</span><span class="o">::</span><span class="n">Priority</span> <span class="n">pri</span> <span class="p">)</span>
|
|
<span class="p">{</span> <span class="k">return</span> <span class="n">self</span><span class="o">-></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="n">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>
|
|
<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">setString1</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">return</span> <span class="n">rvalue</span><span class="p">;</span>
|
|
<span class="p">}</span>
|
|
|
|
<span class="n">PyMethodDef</span> <span class="n">PyParameter_Methods</span><span class="p">[]</span> <span class="o">=</span>
|
|
<span class="p">{</span> <span class="p">{</span> <span class="s">"isFile"</span> <span class="p">,</span> <span class="p">(</span><span class="n">PyCFunction</span><span class="p">)</span><span class="n">PyParameter_isFile</span> <span class="p">,</span> <span class="n">METH_NOARGS</span>
|
|
<span class="p">,</span> <span class="s">"Tells if this parameter (string) holds a file name."</span> <span class="p">}</span>
|
|
<span class="p">,</span> <span class="p">{</span> <span class="s">"setString"</span><span class="p">,</span> <span class="p">(</span><span class="n">PyCFunction</span><span class="p">)</span><span class="n">PyParameter_setString</span><span class="p">,</span> <span class="n">METH_VARARGS</span>
|
|
<span class="p">,</span> <span class="s">"Set the parameter value as a string."</span> <span class="p">}</span>
|
|
<span class="c1">// ...</span>
|
|
<span class="p">,</span> <span class="p">{</span><span class="nb">NULL</span><span class="p">,</span> <span class="nb">NULL</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="nb">NULL</span><span class="p">}</span> <span class="cm">/* sentinel */</span>
|
|
<span class="p">};</span>
|
|
</pre></div>
|
|
</div>
|
|
<div class="section" id="wrapper-for-ordinary-functions">
|
|
<h3><a class="toc-backref" href="#id12">2.7 Wrapper for ordinary functions</a></h3>
|
|
<p>The same mechanic as for the object methods has been built for ordinary
|
|
functions. The top level wrapper beeing <tt class="docutils literal"><span class="pre">callFunction<>()</span></tt> ...</p>
|
|
<div class="highlight"><pre><span></span><span class="k">static</span> <span class="n">PyObject</span><span class="o">*</span> <span class="n">PyCfg_hasParameter</span> <span class="p">(</span> <span class="n">PyObject</span><span class="o">*</span> <span class="k">module</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="nf">callFunction</span><span class="p">(</span><span class="s">"hasParameter"</span><span class="p">,</span><span class="o">&</span><span class="n">hasParameter</span><span class="p">,</span><span class="n">args</span><span class="p">);</span> <span class="p">}</span>
|
|
|
|
<span class="k">static</span> <span class="n">PyMethodDef</span> <span class="n">PyCfg_Methods</span><span class="p">[]</span> <span class="o">=</span>
|
|
<span class="p">{</span> <span class="p">{</span> <span class="s">"hasParameter"</span><span class="p">,</span> <span class="p">(</span><span class="n">PyCFunction</span><span class="p">)</span><span class="n">PyCfg_hasParameter</span><span class="p">,</span> <span class="n">METH_VARARGS</span>
|
|
<span class="p">,</span> <span class="s">"Tells if a parameter exists already in the DB."</span> <span class="p">}</span>
|
|
<span class="c1">// ...</span>
|
|
<span class="p">,</span> <span class="p">{</span><span class="nb">NULL</span><span class="p">,</span> <span class="nb">NULL</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="nb">NULL</span><span class="p">}</span> <span class="cm">/* sentinel */</span>
|
|
<span class="p">};</span>
|
|
</pre></div>
|
|
</div>
|
|
<div class="section" id="object-post-create-hook">
|
|
<h3><a class="toc-backref" href="#id13">2.8 Object post-create hook</a></h3>
|
|
<p>By defining specialization of the <tt class="docutils literal"><span class="pre">pyTypePostModuleinit<>()</span></tt> function template,
|
|
you can add any post-treatment to a Python type object. Like adding sub-classes
|
|
or constants values.</p>
|
|
<p>In the following code, we add <tt class="docutils literal">Priority</tt> as a sub-object of <tt class="docutils literal">Parameter</tt> then
|
|
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">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>
|
|
<span class="p">,</span> <span class="nb">NULL</span>
|
|
<span class="p">,</span> <span class="n">PyTypeManager</span><span class="o">::</span><span class="n">NoCppDelete</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="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>
|
|
<span class="n">addConstant</span><span class="p">(</span> <span class="n">typeObject</span><span class="p">,</span> <span class="s">"ApplicationBuiltin"</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">ApplicationBuiltin</span> <span class="p">);</span>
|
|
<span class="n">addConstant</span><span class="p">(</span> <span class="n">typeObject</span><span class="p">,</span> <span class="s">"ConfigurationFile"</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">ConfigurationFile</span> <span class="p">);</span>
|
|
<span class="n">addConstant</span><span class="p">(</span> <span class="n">typeObject</span><span class="p">,</span> <span class="s">"UserFile"</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">UserFile</span> <span class="p">);</span>
|
|
<span class="n">addConstant</span><span class="p">(</span> <span class="n">typeObject</span><span class="p">,</span> <span class="s">"CommandLine"</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">CommandLine</span> <span class="p">);</span>
|
|
<span class="n">addConstant</span><span class="p">(</span> <span class="n">typeObject</span><span class="p">,</span> <span class="s">"Interactive"</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">Interactive</span> <span class="p">);</span>
|
|
<span class="p">}</span>
|
|
</pre></div>
|
|
</div>
|
|
</div>
|
|
|
|
</div>
|
|
<!-- /Content -->
|
|
|
|
<!-- Footer -->
|
|
<div class="footer gradient-2">
|
|
<div class="container footer-container ">
|
|
<div class="row">
|
|
<div class="col-xs-4 col-sm-3 col-md-3 col-lg-3">
|
|
<div class="footer-title">Social</div>
|
|
<ul class="list-unstyled">
|
|
</ul>
|
|
</div>
|
|
<div class="col-xs-4 col-sm-3 col-md-3 col-lg-2">
|
|
</div>
|
|
<div class="col-xs-4 col-sm-3 col-md-3 col-lg-3">
|
|
<div class="footer-title">Links</div>
|
|
<ul class="list-unstyled">
|
|
<li><a href="https://coriolis.lip6.fr/" target="_blank">Alliance/Coriolis</a></li>
|
|
<li><a href="https://www-soc.lip6.fr/" target="_blank">CIAN Team Website</a></li>
|
|
<li><a href="https://f-si.org" target="_blank">Free Silicon Foundation</a></li>
|
|
</ul>
|
|
</div>
|
|
<div class="col-xs-12 col-sm-3 col-md-3 col-lg-4">
|
|
<p class="pull-right text-right">
|
|
<small><em>Proudly powered by <a href="http://docs.getpelican.com/" target="_blank">pelican</a></em></small><br/>
|
|
<small><em><span class="sc">NEST</span> theme by <a href="https://github.com/molivier" target="_blank">molivier</a></em></small><br/>
|
|
<small>Copyright © 2020-2020 Sorbonne Universite</small>
|
|
</p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<!-- /Footer -->
|
|
</body>
|
|
</html> |