coriolis/documentation/output/pages/python-cpp-new.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&lt;&gt;()</span></tt> template.
Template specializations are defined for the <tt class="docutils literal">POD</tt> and basic <tt class="docutils literal">STL</tt>
types like <tt class="docutils literal"><span class="pre">std::string</span></tt>.</p>
<p>To add more easily new specializations, they resides in the top level
scope (<strong>not</strong> inside <tt class="docutils literal">Isobar</tt>).</p>
</li>
<li><p class="first">Decapsulate a C++ object <em>from</em> a Python one, done by <tt class="docutils literal">pyToC()</tt>.
It's implementation is slightly different from the one of <tt class="docutils literal"><span class="pre">cToPy&lt;&gt;()</span></tt>
in the sense that it is a mix of normal C++ functions and template
functions. I was having trouble inside the <tt class="docutils literal">callMethod()</tt> to access
to templates specialization defined <em>after</em> that point, so function be
it.</p>
<p>There are two mutually exclusives versions of the <tt class="docutils literal"><span class="pre">pyToC&lt;&gt;()</span></tt> for
objects managed through the type manager. One is for value types and
the other for pointer types.</p>
</li>
<li><p class="first">Wrap C/C++ functions &amp; methods inside C-linkage <tt class="docutils literal">PyCFunction</tt>, that is
<tt class="docutils literal">PyOject* <span class="pre">(*)(PyObject*,</span> PyObject*)</tt>. This is done respectively through
<tt class="docutils literal"><span class="pre">callFunction&lt;&gt;()</span></tt> and <tt class="docutils literal"><span class="pre">callMethod&lt;&gt;()</span></tt>. <tt class="docutils literal"><span class="pre">callMethod&lt;&gt;()</span></tt> having
two variants, one for directly calling the function member, if there is
no overload and one for calling one given flavor of the overloaded
function member.</p>
<p>In addition we provides special variants for Python unary, binary and
operator functions.</p>
</li>
</ol>
<p>In addition to those user exposed features, we have:</p>
<ul class="simple">
<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 &amp; 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">&quot;C&quot;</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">&quot;C&quot;</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&lt;CppT&gt;</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&lt;CppT&gt;</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&lt;CppT&gt;</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&lt;CppT,BaseT&gt;</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&lt;CppT&gt;</span>, template for C++ <tt class="docutils literal"><span class="pre">std::vector&lt;CppT*&gt;</span></tt>.</li>
<li><span class="cb">PyTypeManagerVectorIterator&lt;CppT&gt;</span>
Template for C++ <tt class="docutils literal"><span class="pre">std::vector&lt;CppT*&gt;::iterator</span></tt>, automatically created
from the vector registration.</li>
<li><span class="cb">PyTypeManagerMap&lt;CppK,CppT&gt;</span>, template for C++ <tt class="docutils literal"><span class="pre">std::map&lt;CppK*,CppT*&gt;</span></tt>.</li>
<li><span class="cb">PyTypeManagerMapIterator&lt;CppK,CppT&gt;</span>
Template for C++ <tt class="docutils literal"><span class="pre">std::vector&lt;CppK*,CppT*&gt;::iterator</span></tt>, automatically created
from the map registration.</li>
<li><span class="cb">PyTypeManagerCollection&lt;,CppT&gt;</span>, template for C++ <tt class="docutils literal"><span class="pre">Hurricane::Collection&lt;CppT*&gt;</span></tt>.</li>
<li><span class="cb">PyTypeManagerCollectionIterator&lt;,CppT&gt;</span>
Template for C++ <tt class="docutils literal"><span class="pre">Hurricane::Locator&lt;CppT*&gt;</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">&quot;C&quot;</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">-&gt;</span><span class="n">_getTpHash</span><span class="p">(</span> <span class="n">self</span> <span class="p">);</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="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">&lt;</span><span class="k">typename</span> <span class="nc">CppT</span><span class="o">&gt;</span>
<span class="k">static</span> <span class="n">PyTypeManager</span><span class="o">*</span> <span class="n">_get</span><span class="p">();</span>
<span class="k">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">-&gt;</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">-&gt;</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">&amp;::</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">-&gt;</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">&amp;::</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">-&gt;</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">&amp;::</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">-&gt;</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">&amp;::</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">-&gt;</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">-&gt;</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&lt;&gt;()</span> templates functions and real functions are supplieds.</p>
<p>Templates functions are used for all types/classes managed through
the <tt class="docutils literal">PyTypeManger</tt>. They come in two flavors:</p>
<ol class="arabic simple" start="2">
<li><strong>Value as pointer version</strong> (C++ argment type is <tt class="docutils literal">T*</tt>):
The encapsulated C++ object is still a pointer,
but to a <em>stand-alone</em> one which has been created for the sole
purpose of this <tt class="docutils literal">PyObject</tt>. Typical example is the <tt class="docutils literal">Box</tt>.
Then, we <em>copy by value</em> the contents of the pointed object into
the contents of the pointer argument that we where given.</li>
<li><strong>Pointer version</strong> (C++ argument type is <tt class="docutils literal">T**</tt>):
The encapsulated C++ object is truly a pointer
to an element of the data-structure, then we just extract the
C++ pointer value.</li>
</ol>
<p>Normal function overload are used for <tt class="docutils literal">POD</tt> types (<tt class="docutils literal">bool</tt>, <tt class="docutils literal">int</tt>,
<tt class="docutils literal">long</tt>, <tt class="docutils literal">double</tt>, ...) and basic <tt class="docutils literal">STL</tt> types (<tt class="docutils literal"><span class="pre">std::string</span></tt>, ...).</p>
<p>Specialization for all POD type that can be directly translated into
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">&lt;</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">&lt;</span> <span class="o">!</span><span class="n">std</span><span class="o">::</span><span class="n">is_pointer</span><span class="o">&lt;</span><span class="n">T</span><span class="o">&gt;::</span><span class="n">value</span><span class="p">,</span> <span class="kt">bool</span> <span class="o">&gt;::</span><span class="n">type</span> <span class="o">=</span> <span class="nb">true</span> <span class="o">&gt;</span>
<span class="kr">inline</span> <span class="kt">bool</span> <span class="n">pyToC</span> <span class="p">(</span> <span class="n">PyObject</span><span class="o">*</span> <span class="n">pyArg</span><span class="p">,</span> <span class="n">T</span><span class="o">*</span> <span class="n">arg</span> <span class="p">)</span>
<span class="p">{</span>
<span class="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">&lt;</span><span class="n">T</span><span class="o">&gt;::</span><span class="n">type</span> <span class="n">NonConstT</span><span class="p">;</span>
<span class="n">Isobar3</span><span class="o">::</span><span class="n">PyTypeManager</span><span class="o">*</span> <span class="n">manager</span> <span class="o">=</span> <span class="n">Isobar3</span><span class="o">::</span><span class="n">PyTypeManager</span><span class="o">::</span><span class="n">_get</span><span class="o">&lt;</span><span class="n">T</span><span class="o">&gt;</span><span class="p">();</span>
<span class="k">if</span> <span class="p">(</span><span class="n">not</span> <span class="n">manager</span><span class="p">)</span> <span class="p">{</span>
<span class="n">std</span><span class="o">::</span><span class="n">cerr</span> <span class="o">&lt;&lt;</span> <span class="s">&quot;Isobar3::pyToC&lt;&gt;(const T*): Unsupported type.&quot;</span> <span class="o">&lt;&lt;</span> <span class="n">std</span><span class="o">::</span><span class="n">endl</span><span class="p">;</span>
<span class="k">return</span> <span class="nb">false</span><span class="p">;</span>
<span class="p">}</span>
<span class="k">if</span> <span class="p">(</span><span class="n">Py_TYPE</span><span class="p">(</span><span class="n">pyArg</span><span class="p">)</span> <span class="o">!=</span> <span class="n">manager</span><span class="o">-&gt;</span><span class="n">_getTypeObject</span><span class="p">())</span> <span class="k">return</span> <span class="nb">false</span><span class="p">;</span>
<span class="o">*</span><span class="p">(</span><span class="k">const_cast</span><span class="o">&lt;</span> <span class="n">NonConstT</span><span class="o">*</span> <span class="o">&gt;</span><span class="p">(</span><span class="n">arg</span><span class="p">))</span> <span class="o">=</span> <span class="o">*</span><span class="p">((</span> <span class="n">T</span><span class="o">*</span> <span class="p">)(</span> <span class="n">Isobar3</span><span class="o">::</span><span class="n">object1</span><span class="p">(</span> <span class="n">pyArg</span> <span class="p">)));</span>
<span class="k">return</span> <span class="nb">true</span><span class="p">;</span>
<span class="p">}</span>
<span class="c1">// Template/Pointer to a pointer flavor.</span>
<span class="k">template</span><span class="o">&lt;</span><span class="k">typename</span> <span class="nc">T</span><span class="o">&gt;</span>
<span class="kr">inline</span> <span class="kt">bool</span> <span class="n">pyToC</span> <span class="p">(</span> <span class="n">PyObject</span><span class="o">*</span> <span class="n">pyArg</span><span class="p">,</span> <span class="n">T</span><span class="o">**</span> <span class="n">arg</span> <span class="p">)</span>
<span class="p">{</span>
<span class="n">Isobar3</span><span class="o">::</span><span class="n">PyTypeManager</span><span class="o">*</span> <span class="n">manager</span> <span class="o">=</span> <span class="n">Isobar3</span><span class="o">::</span><span class="n">PyTypeManager</span><span class="o">::</span><span class="n">_get</span><span class="o">&lt;</span><span class="n">T</span><span class="o">&gt;</span><span class="p">();</span>
<span class="k">if</span> <span class="p">(</span><span class="n">not</span> <span class="n">manager</span><span class="p">)</span> <span class="p">{</span>
<span class="n">std</span><span class="o">::</span><span class="n">cerr</span> <span class="o">&lt;&lt;</span> <span class="s">&quot;Isobar3::pyToC&lt;T&gt;(T*&amp;): Unsupported type </span><span class="se">\&quot;</span><span class="s">&quot;</span> <span class="o">&lt;&lt;</span> <span class="k">typeid</span><span class="p">(</span><span class="n">T</span><span class="p">).</span><span class="n">name</span><span class="p">()</span> <span class="o">&lt;&lt;</span> <span class="s">&quot;</span><span class="se">\&quot;</span><span class="s">&quot;</span> <span class="o">&lt;&lt;</span> <span class="n">std</span><span class="o">::</span><span class="n">endl</span><span class="p">;</span>
<span class="k">return</span> <span class="nb">false</span><span class="p">;</span>
<span class="p">}</span>
<span class="o">*</span><span class="n">arg</span> <span class="o">=</span> <span class="p">(</span><span class="n">T</span><span class="o">*</span><span class="p">)(</span> <span class="n">Isobar3</span><span class="o">::</span><span class="n">object1</span><span class="p">(</span> <span class="n">pyArg</span> <span class="p">));</span>
<span class="k">return</span> <span class="nb">true</span><span class="p">;</span>
<span class="p">}</span>
<span class="c1">// True function overload for std::string.</span>
<span class="kr">inline</span> <span class="kt">bool</span> <span class="n">pyToC</span> <span class="p">(</span> <span class="n">PyObject</span><span class="o">*</span> <span class="n">pyArg</span><span class="p">,</span> <span class="n">std</span><span class="o">::</span><span class="n">string</span><span class="o">*</span> <span class="n">arg</span> <span class="p">)</span>
<span class="p">{</span>
<span class="k">if</span> <span class="p">(</span><span class="n">not</span> <span class="n">PyUnicode_Check</span><span class="p">(</span><span class="n">pyArg</span><span class="p">))</span> <span class="k">return</span> <span class="nb">false</span><span class="p">;</span>
<span class="n">PyObject</span><span class="o">*</span> <span class="n">pyBytes</span> <span class="o">=</span> <span class="n">PyUnicode_AsASCIIString</span><span class="p">(</span> <span class="n">pyArg</span> <span class="p">);</span>
<span class="o">*</span><span class="n">arg</span> <span class="o">=</span> <span class="n">PyBytes_AsString</span><span class="p">(</span> <span class="n">pyBytes</span> <span class="p">);</span>
<span class="n">Py_DECREF</span><span class="p">(</span> <span class="n">pyBytes</span> <span class="p">);</span>
<span class="k">return</span> <span class="nb">true</span><span class="p">;</span>
<span class="p">}</span>
<span class="c1">// True function overload for bool.</span>
<span class="kr">inline</span> <span class="kt">bool</span> <span class="n">pyToC</span> <span class="p">(</span> <span class="n">PyObject</span><span class="o">*</span> <span class="n">pyArg</span><span class="p">,</span> <span class="kt">bool</span><span class="o">*</span> <span class="n">arg</span> <span class="p">)</span>
<span class="p">{</span>
<span class="k">if</span> <span class="p">(</span><span class="n">not</span> <span class="n">PyBool_Check</span><span class="p">(</span><span class="n">pyArg</span><span class="p">))</span> <span class="k">return</span> <span class="nb">false</span><span class="p">;</span>
<span class="o">*</span><span class="n">arg</span> <span class="o">=</span> <span class="p">(</span><span class="n">pyArg</span> <span class="o">==</span> <span class="n">Py_True</span><span class="p">);</span>
<span class="k">return</span> <span class="nb">true</span><span class="p">;</span>
<span class="p">}</span>
<span class="c1">// True function overload for int.</span>
<span class="kr">inline</span> <span class="kt">bool</span> <span class="n">pyToC</span> <span class="p">(</span> <span class="n">PyObject</span><span class="o">*</span> <span class="n">pyArg</span><span class="p">,</span> <span class="kt">int</span><span class="o">*</span> <span class="n">arg</span> <span class="p">)</span>
<span class="p">{</span>
<span class="k">if</span> <span class="p">(</span><span class="n">PyLong_Check</span><span class="p">(</span><span class="n">pyArg</span><span class="p">))</span> <span class="p">{</span> <span class="o">*</span><span class="n">arg</span> <span class="o">=</span> <span class="n">PyLong_AsLong</span><span class="p">(</span> <span class="n">pyArg</span> <span class="p">);</span> <span class="p">}</span>
<span class="k">else</span> <span class="k">return</span> <span class="nb">false</span><span class="p">;</span>
<span class="k">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&lt;&gt;</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&lt;CppT&gt;::_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&lt;CppT&gt;::_link()</span></tt> method is the reason
<strong>why</strong> we need the intermediate <tt class="docutils literal">PyTypeManagerVTrunk&lt;CppT&gt;</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&lt;typename CppT&gt; PyObject* cToPy ( CppT&nbsp; object )</tt></li>
<li><tt class="docutils literal">template&lt;typename CppT&gt; PyObject* cToPy ( CppT* object )</tt></li>
</ul>
<p class="last">Which would be illegal (function templates are not allowed to have <em>partial</em>
specialization), but they are <em>not</em>. The two pairs
<tt class="docutils literal">(template parameter,function parameter)</tt>, that is <tt class="docutils literal">(CppT,CppT)</tt> and
<tt class="docutils literal">(CppT,CppT*)</tt> cannot be made to be a specialization of each other.</p>
</div>
<div class="highlight"><pre><span></span><span class="c1">// Generic template for values.</span>
<span class="k">template</span><span class="o">&lt;</span> <span class="k">typename</span> <span class="nc">CppT</span> <span class="o">&gt;</span>
<span class="kr">inline</span> <span class="n">PyObject</span><span class="o">*</span> <span class="n">cToPy</span> <span class="p">(</span> <span class="n">CppT</span> <span class="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">&lt;</span><span class="n">CppT</span><span class="o">&gt;</span><span class="p">())</span> <span class="p">{</span>
<span class="n">std</span><span class="o">::</span><span class="n">string</span> <span class="n">message</span> <span class="o">=</span> <span class="s">&quot;Overload for Isobar3::cToPy&lt; &quot;</span>
<span class="o">+</span> <span class="n">Hurricane</span><span class="o">::</span><span class="n">demangle</span><span class="p">(</span><span class="k">typeid</span><span class="p">(</span><span class="n">CppT</span><span class="p">).</span><span class="n">name</span><span class="p">())</span> <span class="o">+</span> <span class="s">&quot; &gt;() Type not registered in the manager.&quot;</span><span class="p">;</span>
<span class="n">PyErr_SetString</span><span class="p">(</span> <span class="n">Isobar3</span><span class="o">::</span><span class="n">HurricaneError</span><span class="p">,</span> <span class="n">message</span><span class="p">.</span><span class="n">c_str</span><span class="p">()</span> <span class="p">);</span>
<span class="k">return</span> <span class="nb">NULL</span><span class="p">;</span>
<span class="p">}</span>
<span class="k">return</span> <span class="n">Isobar3</span><span class="o">::</span><span class="n">PyTypeManager</span><span class="o">::</span><span class="n">link</span><span class="o">&lt;</span><span class="n">CppT</span><span class="o">&gt;</span><span class="p">(</span> <span class="k">new</span> <span class="n">CppT</span> <span class="p">(</span><span class="n">object</span><span class="p">)</span> <span class="p">);</span>
<span class="p">}</span>
<span class="c1">// Disabled for POD &amp; STL types, pointer flavor.</span>
<span class="k">template</span><span class="o">&lt;</span> <span class="k">typename</span> <span class="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">&lt;</span> <span class="o">!</span><span class="n">std</span><span class="o">::</span><span class="n">is_same</span><span class="o">&lt;</span><span class="n">CppT</span><span class="p">,</span><span class="kt">bool</span><span class="o">&gt;::</span><span class="n">value</span>
<span class="o">&amp;&amp;</span> <span class="o">!</span><span class="n">std</span><span class="o">::</span><span class="n">is_same</span><span class="o">&lt;</span><span class="n">CppT</span><span class="p">,</span><span class="kt">int</span> <span class="o">&gt;::</span><span class="n">value</span>
<span class="o">&amp;&amp;</span> <span class="o">!</span><span class="n">std</span><span class="o">::</span><span class="n">is_same</span><span class="o">&lt;</span><span class="n">CppT</span><span class="p">,</span><span class="n">std</span><span class="o">::</span><span class="n">string</span><span class="o">&gt;::</span><span class="n">value</span>
<span class="o">&amp;&amp;</span> <span class="o">!</span><span class="n">std</span><span class="o">::</span><span class="n">is_same</span><span class="o">&lt;</span><span class="n">CppT</span><span class="p">,</span><span class="k">const</span> <span class="n">std</span><span class="o">::</span><span class="n">string</span><span class="o">&gt;::</span><span class="n">value</span><span class="p">,</span><span class="kt">bool</span><span class="o">&gt;::</span><span class="n">type</span> <span class="o">=</span> <span class="nb">true</span> <span class="o">&gt;</span>
<span class="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">&lt;</span><span class="n">CppT</span><span class="o">&gt;</span><span class="p">(</span> <span class="n">object</span> <span class="p">);</span> <span class="p">}</span>
<span class="c1">// Disabled for POD &amp; STL types, const pointer flavor.</span>
<span class="k">template</span><span class="o">&lt;</span> <span class="k">typename</span> <span class="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">&lt;</span> <span class="o">!</span><span class="n">std</span><span class="o">::</span><span class="n">is_same</span><span class="o">&lt;</span><span class="n">CppT</span><span class="p">,</span><span class="kt">bool</span><span class="o">&gt;::</span><span class="n">value</span>
<span class="o">&amp;&amp;</span> <span class="o">!</span><span class="n">std</span><span class="o">::</span><span class="n">is_same</span><span class="o">&lt;</span><span class="n">CppT</span><span class="p">,</span><span class="kt">int</span> <span class="o">&gt;::</span><span class="n">value</span>
<span class="o">&amp;&amp;</span> <span class="o">!</span><span class="n">std</span><span class="o">::</span><span class="n">is_same</span><span class="o">&lt;</span><span class="n">CppT</span><span class="p">,</span><span class="n">std</span><span class="o">::</span><span class="n">string</span><span class="o">&gt;::</span><span class="n">value</span>
<span class="o">&amp;&amp;</span> <span class="o">!</span><span class="n">std</span><span class="o">::</span><span class="n">is_same</span><span class="o">&lt;</span><span class="n">CppT</span><span class="p">,</span><span class="k">const</span> <span class="n">std</span><span class="o">::</span><span class="n">string</span><span class="o">&gt;::</span><span class="n">value</span><span class="p">,</span><span class="kt">bool</span><span class="o">&gt;::</span><span class="n">type</span> <span class="o">=</span> <span class="nb">true</span> <span class="o">&gt;</span>
<span class="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">&lt;</span><span class="n">CppT</span><span class="o">&gt;</span><span class="p">(</span> <span class="k">const_cast</span><span class="o">&lt;</span><span class="n">CppT</span><span class="o">*&gt;</span><span class="p">(</span> <span class="n">object</span> <span class="p">));</span> <span class="p">}</span>
<span class="c1">// Specialization for booleans.</span>
<span class="k">template</span><span class="o">&lt;&gt;</span>
<span class="kr">inline</span> <span class="n">PyObject</span><span class="o">*</span> <span class="n">cToPy</span><span class="o">&lt;</span><span class="kt">bool</span><span class="o">&gt;</span> <span class="p">(</span> <span class="kt">bool</span> <span class="n">b</span> <span class="p">)</span>
<span class="p">{</span> <span class="k">if</span> <span class="p">(</span><span class="n">b</span><span class="p">)</span> <span class="n">Py_RETURN_TRUE</span><span class="p">;</span> <span class="n">Py_RETURN_FALSE</span><span class="p">;</span> <span class="p">}</span>
<span class="c1">// Specialization for STL std::string.</span>
<span class="k">template</span><span class="o">&lt;&gt;</span> <span class="kr">inline</span> <span class="n">PyObject</span><span class="o">*</span> <span class="n">cToPy</span><span class="o">&lt;</span> <span class="n">std</span><span class="o">::</span><span class="n">string</span><span class="o">&gt;</span> <span class="p">(</span> <span class="n">std</span><span class="o">::</span><span class="n">string</span> <span class="n">s</span> <span class="p">)</span> <span class="p">{</span> <span class="k">return</span> <span class="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">&lt;&gt;</span> <span class="kr">inline</span> <span class="n">PyObject</span><span class="o">*</span> <span class="n">cToPy</span><span class="o">&lt;</span><span class="k">const</span> <span class="n">std</span><span class="o">::</span><span class="n">string</span> <span class="o">&gt;</span> <span class="p">(</span> <span class="k">const</span> <span class="n">std</span><span class="o">::</span><span class="n">string</span> <span class="n">s</span> <span class="p">)</span> <span class="p">{</span> <span class="k">return</span> <span class="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">&lt;&gt;</span> <span class="kr">inline</span> <span class="n">PyObject</span><span class="o">*</span> <span class="n">cToPy</span><span class="o">&lt;</span><span class="k">const</span> <span class="n">std</span><span class="o">::</span><span class="n">string</span><span class="o">*&gt;</span> <span class="p">(</span> <span class="k">const</span> <span class="n">std</span><span class="o">::</span><span class="n">string</span><span class="o">*</span> <span class="n">s</span> <span class="p">)</span> <span class="p">{</span> <span class="k">return</span> <span class="nf">PyUnicode_FromString</span><span class="p">(</span> <span class="n">s</span><span class="o">-&gt;</span><span class="n">c_str</span><span class="p">()</span> <span class="p">);</span> <span class="p">}</span>
<span class="c1">// Specialization for POD int.</span>
<span class="k">template</span><span class="o">&lt;&gt;</span> <span class="kr">inline</span> <span class="n">PyObject</span><span class="o">*</span> <span class="n">cToPy</span><span class="o">&lt;</span> <span class="kt">int</span> <span class="o">&gt;</span> <span class="p">(</span> <span class="kt">int</span> <span class="n">i</span> <span class="p">)</span> <span class="p">{</span> <span class="k">return</span> <span class="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">&lt;&gt;</span> <span class="kr">inline</span> <span class="n">PyObject</span><span class="o">*</span> <span class="n">cToPy</span><span class="o">&lt;</span><span class="k">const</span> <span class="kt">int</span> <span class="o">&gt;</span> <span class="p">(</span> <span class="k">const</span> <span class="kt">int</span> <span class="n">i</span> <span class="p">)</span> <span class="p">{</span> <span class="k">return</span> <span class="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">&lt;&gt;</span> <span class="kr">inline</span> <span class="n">PyObject</span><span class="o">*</span> <span class="n">cToPy</span><span class="o">&lt;</span><span class="k">const</span> <span class="kt">int</span><span class="o">*&gt;</span> <span class="p">(</span> <span class="k">const</span> <span class="kt">int</span><span class="o">*</span> <span class="n">i</span> <span class="p">)</span> <span class="p">{</span> <span class="k">return</span> <span class="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">&quot;C&quot;</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&lt;&gt;</span></tt>,
but I did encouter problems when the functions signature did contains
references. So to manage that I did implement:</p>
<ul class="simple">
<li>A <tt class="docutils literal">BaseArg</tt> class and it's template derived <tt class="docutils literal">Arg&lt;T&gt;</tt> to hold
one value of a type (more or less like <tt class="docutils literal"><span class="pre">std::any&lt;&gt;</span></tt>).
The type of the value attribute of <tt class="docutils literal">Arg&lt;T&gt;</tt> is <tt class="docutils literal">T</tt> <em>stripped</em>
from reference and constness. This internal type is accessible
through <tt class="docutils literal"><span class="pre">Arg&lt;T&gt;::ValueT</span></tt>.</li>
<li>A template list of arguments <tt class="docutils literal"><span class="pre">Args&lt;typename...</span> Ts&gt;</tt> analogous to
<tt class="docutils literal"><span class="pre">std::tuple&lt;&gt;</span></tt> which holds a table of <tt class="docutils literal">BaseArg</tt> to convert all the
arguments.</li>
<li>A recursive template converter function <tt class="docutils literal">parse_pyobjects&lt;&gt;</tt>, which is
called through the <tt class="docutils literal"><span class="pre">Args&lt;&gt;::parse()</span></tt> function.</li>
</ul>
<p>Another challenge is the return type. I distinguish three flavor of
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&lt;&gt;()</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&lt;&gt;()</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&lt;&gt;</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&lt;&gt;</tt> we have <em>two</em> sets for the
method parameters types : <tt class="docutils literal"><span class="pre">TArgsF...</span></tt> and <tt class="docutils literal"><span class="pre">TArgsW...</span></tt>. This is to
allow a wider range of matching in the template as the type of the
arguments of the method (<tt class="docutils literal"><span class="pre">TArgsF...</span></tt>) may not <em>exactly</em> matches the
one passed by the wrapper (<tt class="docutils literal"><span class="pre">TArgsW...</span></tt>), typically the method has
a <tt class="docutils literal">const</tt> parameter which is non-<tt class="docutils literal">const</tt> in the wrapper.</p>
</div>
<p>Here is an excerpt of the code:</p>
<div class="highlight"><pre><span></span><span class="c1">// Flavor for &quot;return by value&quot; (seems to match std::is_object&lt;&gt;)</span>
<span class="k">template</span><span class="o">&lt;</span> <span class="k">typename</span> <span class="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">&lt;</span> <span class="o">!</span><span class="n">std</span><span class="o">::</span><span class="n">is_reference</span><span class="o">&lt;</span><span class="n">TR</span><span class="o">&gt;::</span><span class="n">value</span>
<span class="o">&amp;&amp;</span> <span class="o">!</span><span class="n">std</span><span class="o">::</span><span class="n">is_pointer</span> <span class="o">&lt;</span><span class="n">TR</span><span class="o">&gt;::</span><span class="n">value</span>
<span class="o">&amp;&amp;</span> <span class="o">!</span><span class="n">std</span><span class="o">::</span><span class="n">is_void</span> <span class="o">&lt;</span><span class="n">TR</span><span class="o">&gt;::</span><span class="n">value</span><span class="p">,</span><span class="kt">bool</span><span class="o">&gt;::</span><span class="n">type</span> <span class="o">=</span> <span class="nb">true</span> <span class="o">&gt;</span>
<span class="kr">inline</span> <span class="n">PyObject</span><span class="o">*</span> <span class="n">_callMethodReturn</span> <span class="p">(</span> <span class="n">TR</span><span class="p">(</span><span class="n">TC</span><span class="o">::*</span> <span class="n">method</span><span class="p">)(</span><span class="n">TArgsF</span><span class="p">...),</span> <span class="n">TC</span><span class="o">*</span> <span class="n">cppObject</span><span class="p">,</span> <span class="n">TArgsW</span><span class="p">...</span> <span class="n">args</span> <span class="p">)</span>
<span class="p">{</span>
<span class="n">TR</span> <span class="n">value</span> <span class="o">=</span> <span class="p">(</span><span class="n">cppObject</span><span class="o">-&gt;*</span><span class="n">method</span><span class="p">)(</span> <span class="n">args</span><span class="p">...</span> <span class="p">);</span>
<span class="k">return</span> <span class="nf">cToPy</span><span class="p">(</span> <span class="n">value</span> <span class="p">);</span>
<span class="p">}</span>
<span class="c1">// Flavor for &quot;return by reference&quot;</span>
<span class="k">template</span><span class="o">&lt;</span> <span class="k">typename</span> <span class="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">&lt;</span> <span class="n">std</span><span class="o">::</span><span class="n">is_reference</span><span class="o">&lt;</span><span class="n">TR</span><span class="o">&gt;::</span><span class="n">value</span>
<span class="o">&amp;&amp;</span> <span class="o">!</span><span class="n">std</span><span class="o">::</span><span class="n">is_pointer</span> <span class="o">&lt;</span><span class="n">TR</span><span class="o">&gt;::</span><span class="n">value</span>
<span class="o">&amp;&amp;</span> <span class="o">!</span><span class="n">std</span><span class="o">::</span><span class="n">is_void</span> <span class="o">&lt;</span><span class="n">TR</span><span class="o">&gt;::</span><span class="n">value</span><span class="p">,</span><span class="kt">bool</span><span class="o">&gt;::</span><span class="n">type</span> <span class="o">=</span> <span class="nb">true</span> <span class="o">&gt;</span>
<span class="kr">inline</span> <span class="n">PyObject</span><span class="o">*</span> <span class="n">_callMethodReturn</span> <span class="p">(</span> <span class="n">TR</span><span class="p">(</span><span class="n">TC</span><span class="o">::*</span> <span class="n">method</span><span class="p">)(</span><span class="n">TArgsF</span><span class="p">...),</span> <span class="n">TC</span><span class="o">*</span> <span class="n">cppObject</span><span class="p">,</span> <span class="n">TArgsW</span><span class="p">...</span> <span class="n">args</span> <span class="p">)</span>
<span class="p">{</span>
<span class="n">TR</span> <span class="n">rvalue</span> <span class="o">=</span> <span class="p">(</span><span class="n">cppObject</span><span class="o">-&gt;*</span><span class="n">method</span><span class="p">)(</span> <span class="n">args</span><span class="p">...</span> <span class="p">);</span>
<span class="k">return</span> <span class="nf">cToPy</span><span class="p">(</span> <span class="n">rvalue</span> <span class="p">);</span>
<span class="p">}</span>
<span class="c1">// Flavor for &quot;return by pointer&quot;.</span>
<span class="k">template</span><span class="o">&lt;</span> <span class="k">typename</span> <span class="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">&lt;</span><span class="n">std</span><span class="o">::</span><span class="n">is_pointer</span><span class="o">&lt;</span><span class="n">TR</span><span class="o">&gt;::</span><span class="n">value</span><span class="p">,</span><span class="kt">bool</span><span class="o">&gt;::</span><span class="n">type</span> <span class="o">=</span> <span class="nb">true</span> <span class="o">&gt;</span>
<span class="kr">inline</span> <span class="n">PyObject</span><span class="o">*</span> <span class="n">_callMethodReturn</span> <span class="p">(</span> <span class="n">TR</span><span class="p">(</span><span class="n">TC</span><span class="o">::*</span> <span class="n">method</span><span class="p">)(</span><span class="n">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">-&gt;*</span><span class="n">method</span><span class="p">)(</span> <span class="n">args</span><span class="p">...</span> <span class="p">);</span>
<span class="k">return</span> <span class="nf">cToPy</span><span class="p">(</span> <span class="n">pvalue</span> <span class="p">);</span>
<span class="p">}</span>
<span class="c1">// Flavor for &quot;return void&quot;.</span>
<span class="k">template</span><span class="o">&lt;</span> <span class="k">typename</span> <span class="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">&lt;</span><span class="n">std</span><span class="o">::</span><span class="n">is_void</span><span class="o">&lt;</span><span class="n">TR</span><span class="o">&gt;::</span><span class="n">value</span><span class="p">,</span><span class="kt">bool</span><span class="o">&gt;::</span><span class="n">type</span> <span class="o">=</span> <span class="nb">true</span> <span class="o">&gt;</span>
<span class="kr">inline</span> <span class="n">PyObject</span><span class="o">*</span> <span class="n">_callMethodReturn</span> <span class="p">(</span> <span class="n">TR</span><span class="p">(</span><span class="n">TC</span><span class="o">::*</span> <span class="n">method</span><span class="p">)(</span><span class="n">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">-&gt;*</span><span class="n">method</span><span class="p">)(</span> <span class="n">args</span><span class="p">...</span> <span class="p">);</span>
<span class="n">Py_RETURN_NONE</span><span class="p">;</span>
<span class="p">}</span>
<span class="c1">// Make the translation call for a method without arguments.</span>
<span class="k">template</span><span class="o">&lt;</span> <span class="k">typename</span> <span class="nc">TC</span><span class="p">,</span> <span class="k">typename</span> <span class="nc">TR</span> <span class="o">&gt;</span>
<span class="kr">inline</span> <span class="n">PyObject</span><span class="o">*</span> <span class="n">_callMethod</span> <span class="p">(</span> <span class="n">TR</span><span class="p">(</span><span class="n">TC</span><span class="o">::*</span> <span class="n">method</span><span class="p">)(),</span> <span class="n">TC</span><span class="o">*</span> <span class="n">cppObject</span><span class="p">,</span> <span class="n">Args</span><span class="o">&lt;&gt;&amp;</span> <span class="p">)</span>
<span class="p">{</span> <span class="k">return</span> <span class="n">_callMethodReturn</span><span class="o">&lt;</span><span class="n">TC</span><span class="p">,</span><span class="n">TR</span><span class="o">&gt;</span><span class="p">(</span> <span class="n">method</span><span class="p">,</span> <span class="n">cppObject</span> <span class="p">);</span> <span class="p">}</span>
<span class="c1">// Make the translation call for a method one argument.</span>
<span class="k">template</span><span class="o">&lt;</span> <span class="k">typename</span> <span class="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">&gt;</span>
<span class="kr">inline</span> <span class="n">PyObject</span><span class="o">*</span> <span class="n">_callMethod</span> <span class="p">(</span> <span class="n">TR</span><span class="p">(</span><span class="n">TC</span><span class="o">::*</span> <span class="n">method</span><span class="p">)(</span><span class="n">TA0</span><span class="p">),</span> <span class="n">TC</span><span class="o">*</span> <span class="n">cppObject</span><span class="p">,</span> <span class="n">Args</span><span class="o">&lt;</span><span class="n">TA0</span><span class="o">&gt;&amp;</span> <span class="n">args</span> <span class="p">)</span>
<span class="p">{</span> <span class="k">return</span> <span class="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">&lt;</span><span class="n">TA0</span><span class="o">&gt;</span><span class="p">(</span> <span class="n">args</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span> <span class="p">)</span> <span class="p">);</span> <span class="p">}</span>
<span class="c1">// Make the translation call for a method two argument.</span>
<span class="k">template</span><span class="o">&lt;</span> <span class="k">typename</span> <span class="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">&gt;</span>
<span class="n">PyObject</span><span class="o">*</span> <span class="n">_callMethod</span> <span class="p">(</span> <span class="n">TR</span><span class="p">(</span><span class="n">TC</span><span class="o">::*</span> <span class="n">method</span><span class="p">)(</span><span class="n">TA0</span><span class="p">,</span><span class="n">TA1</span><span class="p">),</span> <span class="n">TC</span><span class="o">*</span> <span class="n">cppObject</span><span class="p">,</span> <span class="n">Args</span><span class="o">&lt;</span><span class="n">TA0</span><span class="p">,</span><span class="n">TA1</span><span class="o">&gt;&amp;</span> <span class="n">args</span> <span class="p">)</span>
<span class="p">{</span> <span class="k">return</span> <span class="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">&lt;</span><span class="n">TA0</span><span class="o">&gt;</span><span class="p">(</span> <span class="n">args</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span> <span class="p">),</span> <span class="n">as</span><span class="o">&lt;</span><span class="n">TA1</span><span class="o">&gt;</span><span class="p">(</span> <span class="n">args</span><span class="p">[</span><span class="mi">1</span><span class="p">]</span> <span class="p">)</span> <span class="p">);</span> <span class="p">}</span>
</pre></div>
<p>The complete work of translating the Python tuple into a <tt class="docutils literal">Args&lt;&gt;</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&lt;&gt;</tt>, template derived class for C/C++ normal functions.</li>
<li><tt class="docutils literal">PyMethodWrapper&lt;&gt;</tt>, template derived class for C++ class methods.
Two flavors are supported, the real method and a function build upon a
method (first argument beaing the object itself). The later is used when
we need to desambiguate overloaded functions, we must create one <em>function</em>
per overload.</li>
</ul>
<p>As a class template cannot guess the template parameters, we wrap them into a
function template which can perform the guess. The <tt class="docutils literal">callMethod&lt;&gt;</tt> template function.</p>
<p>In the end, what the user can write is simply:</p>
<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">&quot;Parameter.addValue&quot;</span><span class="p">,</span><span class="o">&amp;</span><span class="n">Parameter</span><span class="o">::</span><span class="n">addValue</span><span class="p">,</span><span class="n">self</span><span class="p">,</span><span class="n">args</span><span class="p">);</span> <span class="p">}</span>
<span class="n">PyMethodDef</span> <span class="n">PyParameter_Methods</span><span class="p">[]</span> <span class="o">=</span>
<span class="p">{</span> <span class="p">{</span> <span class="s">&quot;isFile&quot;</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">&quot;Tells if this parameter (string) holds a file name.&quot;</span> <span class="p">}</span>
<span class="p">,</span> <span class="p">{</span> <span class="s">&quot;addValue&quot;</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">&quot;Add a new value to parameter of enumerated type.&quot;</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">-&gt;</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">-&gt;</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">-&gt;</span><span class="n">setString</span><span class="p">(</span><span class="n">value</span><span class="p">,</span><span class="n">pri</span><span class="p">,</span><span class="n">flags</span><span class="p">);</span> <span class="p">}</span>
<span class="k">static</span> <span class="n">PyObject</span><span class="o">*</span> <span class="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">&quot;Parameter.setString&quot;</span><span class="p">,</span><span class="o">&amp;</span><span class="n">setString3</span><span class="p">,</span><span class="n">self</span><span class="p">,</span><span class="n">args</span><span class="p">);</span>
<span class="k">if</span> <span class="p">(</span><span class="n">not</span> <span class="n">rvalue</span><span class="p">)</span> <span class="n">rvalue</span> <span class="o">=</span> <span class="n">callMethod</span><span class="p">(</span><span class="s">&quot;Parameter.setString&quot;</span><span class="p">,</span><span class="o">&amp;</span><span class="n">setString2</span><span class="p">,</span><span class="n">self</span><span class="p">,</span><span class="n">args</span><span class="p">);</span>
<span class="k">if</span> <span class="p">(</span><span class="n">not</span> <span class="n">rvalue</span><span class="p">)</span> <span class="n">rvalue</span> <span class="o">=</span> <span class="n">callMethod</span><span class="p">(</span><span class="s">&quot;Parameter.setString&quot;</span><span class="p">,</span><span class="o">&amp;</span><span class="n">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">&quot;isFile&quot;</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">&quot;Tells if this parameter (string) holds a file name.&quot;</span> <span class="p">}</span>
<span class="p">,</span> <span class="p">{</span> <span class="s">&quot;setString&quot;</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">&quot;Set the parameter value as a string.&quot;</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&lt;&gt;()</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">&quot;hasParameter&quot;</span><span class="p">,</span><span class="o">&amp;</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">&quot;hasParameter&quot;</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">&quot;Tells if a parameter exists already in the DB.&quot;</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&lt;&gt;()</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">&lt;&gt;</span>
<span class="kr">inline</span> <span class="kt">void</span> <span class="n">pyTypePostInit</span><span class="o">&lt;</span><span class="n">Cfg</span><span class="o">::</span><span class="n">Parameter</span><span class="o">&gt;</span> <span class="p">(</span> <span class="n">PyTypeObject</span><span class="o">*</span> <span class="n">typeObject</span> <span class="p">)</span>
<span class="p">{</span>
<span class="n">PyTypeManagerNonDBo</span><span class="o">&lt;</span><span class="n">Cfg</span><span class="o">::</span><span class="n">Parameter</span><span class="o">::</span><span class="n">Priority</span><span class="o">&gt;::</span><span class="n">create</span><span class="p">(</span> <span class="p">(</span><span class="n">PyObject</span><span class="o">*</span><span class="p">)</span><span class="n">typeObject</span>
<span class="p">,</span> <span class="n">Cfg</span><span class="o">::</span><span class="n">PyParameterPriority_Methods</span>
<span class="p">,</span> <span class="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">&lt;&gt;</span>
<span class="kr">inline</span> <span class="kt">void</span> <span class="n">pyTypePostInit</span><span class="o">&lt;</span><span class="n">Cfg</span><span class="o">::</span><span class="n">Parameter</span><span class="o">::</span><span class="n">Priority</span><span class="o">&gt;</span> <span class="p">(</span> <span class="n">PyTypeObject</span><span class="o">*</span> <span class="n">typeObject</span> <span class="p">)</span>
<span class="p">{</span>
<span class="c1">// Parameter::Priority enum.</span>
<span class="n">addConstant</span><span class="p">(</span> <span class="n">typeObject</span><span class="p">,</span> <span class="s">&quot;UseDefault&quot;</span> <span class="p">,</span> <span class="n">Cfg</span><span class="o">::</span><span class="n">Parameter</span><span class="o">::</span><span class="n">UseDefault</span> <span class="p">);</span>
<span class="n">addConstant</span><span class="p">(</span> <span class="n">typeObject</span><span class="p">,</span> <span class="s">&quot;ApplicationBuiltin&quot;</span><span class="p">,</span> <span class="n">Cfg</span><span class="o">::</span><span class="n">Parameter</span><span class="o">::</span><span class="n">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">&quot;ConfigurationFile&quot;</span> <span class="p">,</span> <span class="n">Cfg</span><span class="o">::</span><span class="n">Parameter</span><span class="o">::</span><span class="n">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">&quot;UserFile&quot;</span> <span class="p">,</span> <span class="n">Cfg</span><span class="o">::</span><span class="n">Parameter</span><span class="o">::</span><span class="n">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">&quot;CommandLine&quot;</span> <span class="p">,</span> <span class="n">Cfg</span><span class="o">::</span><span class="n">Parameter</span><span class="o">::</span><span class="n">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">&quot;Interactive&quot;</span> <span class="p">,</span> <span class="n">Cfg</span><span class="o">::</span><span class="n">Parameter</span><span class="o">::</span><span class="n">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>