coriolis/documentation/output/pages/python-cpp-new.html

691 lines
70 KiB
HTML
Raw Normal View History

Migration towards Python3, first stage: still based on C-Macros. * New: Python/C++ API level: * Write a new C++/template wrapper to get rid of boost::python * The int & long Python type are now merged. So a C/C++ level, it became "PyLong_X" (remove "PyInt_X") and at Python code level, it became "int" (remove "long"). * Change: VLSISAPD finally defunct. * Configuration is now integrated as a Hurricane component, makes use of the new C++/template wrapper. * vlsisapd is now defunct. Keep it in the source for now as some remaining non essential code may have to be ported in the future. * Note: Python code (copy of the migration howto): * New print function syntax print(). * Changed "dict.has_key(k)" for "k" in dict. * Changed "except Exception, e" for "except Exception as e". * The division "/" is now the floating point division, even if both operand are integers. So 3/2 now gives 1.5 and no longer 1. The integer division is now "//" : 1 = 3//2. So have to carefully review the code to update. Most of the time we want to use "//". We must never change to float for long that, in fact, represents DbU (exposed as Python int type). * execfile() must be replaced by exec(open("file").read()). * iter().__next__() becomes iter(x).__next__(). * __getslice__() has been removed, integrated to __getitem__(). * The formating used for str(type(o)) has changed, so In Stratus, have to update them ("<class 'MyClass'>" instead of "MyClass"). * the "types" module no longer supply values for default types like str (types.StringType) or list (types.StringType). Must use "isinstance()" where they were occuring. * Remove the 'L' to indicate "long integer" (like "12L"), now all Python integer are long. * Change in bootstrap: * Ported Coriolis builder (ccb) to Python3. * Ported Coriolis socInstaller.py to Python3. * Note: In PyQt4+Python3, QVariant no longer exists. Use None or directly convert using the python syntax: bool(x), int(x), ... By default, it is a string (str). * Note: PyQt4 bindings & Python3 under SL7. * In order to compile user's must upgrade to my own rebuild of PyQt 4 & 5 bindings 4.19.21-1.el7.soc. * Bug: In cumulus/plugins.block.htree.HTree.splitNet(), set the root buffer of the H-Tree to the original signal (mainly: top clock). Strangely, it was only done when working in full chip mode.
2021-09-19 12:41:24 -05:00
<!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 Going ...">
<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/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/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 first">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 2.7.</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 Hurricane has a modular design, the core database
then various tools. We do not, and cannot, have one gigantic wrapper
that would encompass all the libraries in one go. We do one Python
module for one C++ library.</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 iterface 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 key features of our wrapper are:</p>
<ul class="simple">
<li>The <tt class="docutils literal">PyTypeManager</tt> and it's derived classes to store and share informations
about all our newly defined <tt class="docutils literal">PyTypeObjects</tt>.</li>
<li>The <tt class="docutils literal"><span class="pre">callMethod&lt;&gt;()</span></tt> and <tt class="docutils literal"><span class="pre">callFunction&lt;&gt;()</span></tt> function templates to
automatically generate a wrapper around C++ object methods or functions.</li>
<li>The <tt class="docutils literal">PyBoject* objectLink&lt;CppT&gt;( CppT* )</tt> functions templates that provides C++ to Python
object translation.</li>
<li>The <tt class="docutils literal">bool pyAs&lt;CppT&gt;( PyObject*, CppT*&amp; )</tt> functions templates that provides Python to C++
object translation.</li>
</ul>
<p>We creates only two kind of <tt class="docutils literal">PyObject</tt> (but many <tt class="docutils literal">PyTypeObject</tt>):</p>
<ul>
<li><p class="first"><tt class="docutils literal">PyVoidpointer</tt> which encapsulate one void pointer to the C++ associated
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">PyVoidPointer</span> <span class="p">{</span>
<span class="n">PyObject_HEAD</span>
<span class="n">void</span><span class="o">*</span> <span class="n">_object</span><span class="p">;</span>
<span class="p">};</span>
<span class="p">}</span>
</pre></div>
</li>
<li><p class="first"><tt class="docutils literal">PyIteratorPointer</tt> which encapsulate one void pointer to the C++ associated
iterator and one another to the <tt class="docutils literal">PyObject</tt> of the container.</p>
<div class="highlight"><pre><span></span><span class="n">extern</span> <span class="s2">&quot;C&quot;</span> <span class="p">{</span>
<span class="n">typedef</span> <span class="n">struct</span> <span class="n">PyVoidPointer</span> <span class="p">{</span>
<span class="n">PyObject_HEAD</span>
<span class="n">void</span><span class="o">*</span> <span class="n">_object</span><span class="p">;</span> <span class="o">//</span> <span class="n">C</span><span class="o">++</span> <span class="n">iterator</span><span class="o">.</span>
<span class="n">PyVoidPointer</span><span class="o">*</span> <span class="n">_container</span><span class="p">;</span> <span class="o">//</span> <span class="n">Python</span> <span class="n">wrapped</span> <span class="n">container</span><span class="o">.</span>
<span class="p">};</span>
<span class="p">}</span>
</pre></div>
</li>
</ul>
<div class="section" id="pytypemanager">
<h3><a class="toc-backref" href="#id6">2.1 PyTypeManager</a></h3>
<p><tt class="docutils literal">PyTypeManager</tt> has two usage:</p>
<ul>
<li><p class="first">Act as a registry of all the created <tt class="docutils literal">PyTypeObject</tt>, and serve as a
dispatcher for the <tt class="docutils literal">PyTypeObject</tt> <em>tp</em> like methods.</p>
</li>
<li><p class="first">Provide a non-template abstract base class for all the derived <tt class="docutils literal">PyTypeObject</tt>.
As said, it is not a template class but it supplies function member
template. Derived classes are provides for different kind of C++
class.</p>
<ul>
<li><p class="first"><span class="cb">PyTypeManagerVTrunk&lt;CppT&gt;</span></p>
<p>Is an intermediate between the non-template base class and all the
templatized others.</p>
</li>
<li><p class="first"><span class="cb">PyTypeManagerNonDBo&lt;CppT&gt;</span></p>
<p>Template for standalone C++ classes that are not derived from <tt class="docutils literal">DBo</tt>.
For example <tt class="docutils literal">Box</tt> or <tt class="docutils literal">Parameter</tt>.</p>
</li>
<li><p class="first"><span class="cb">PyTypeManagerDBo&lt;CppT&gt;</span></p>
<p>Template for C++ classes that <em>are</em> not derived from <tt class="docutils literal">DBo</tt>.
For example <tt class="docutils literal">Cell</tt> or <tt class="docutils literal">Instance</tt>.</p>
</li>
<li><p class="first"><span class="cb">PyTypeManagerVector&lt;CppT&gt;</span>, template for C++ <tt class="docutils literal"><span class="pre">std::vector&lt;CppT*&gt;</span></tt>.</p>
</li>
<li><p class="first"><span class="cb">PyTypeManagerVectorIterator&lt;CppT&gt;</span></p>
<p>Template for C++ <tt class="docutils literal"><span class="pre">std::vector&lt;CppT*&gt;::iterator</span></tt>, automatically created
from the vector registration.</p>
</li>
<li><p class="first"><span class="cb">PyTypeManagerMap&lt;CppK,CppT&gt;</span>, template for C++ <tt class="docutils literal"><span class="pre">std::map&lt;CppK*,CppT*&gt;</span></tt>.</p>
</li>
<li><p class="first"><span class="cb">PyTypeManagerMapIterator&lt;CppK,CppT&gt;</span></p>
<p>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.</p>
</li>
<li><p class="first"><span class="cb">PyTypeManagerCollection&lt;,CppT&gt;</span>, template for C++ <tt class="docutils literal"><span class="pre">Hurricane::Collection&lt;CppT*&gt;</span></tt>.</p>
</li>
<li><p class="first"><span class="cb">PyTypeManagerCollectionIterator&lt;,CppT&gt;</span></p>
<p>Template for C++ <tt class="docutils literal"><span class="pre">Hurricane::Locator&lt;CppT*&gt;</span></tt>, automatically created from
the collection registration.</p>
</li>
</ul>
</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 dispact 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">self</span><span class="o">-&gt;</span><span class="n">ob_type</span> <span class="p">)</span><span class="o">-&gt;</span><span class="n">_getTpHash</span><span class="p">(</span> <span class="n">asVPtr</span><span class="p">(</span><span class="n">self</span><span class="p">)</span> <span class="p">);</span>
<span class="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">PyVoidPointer</span><span class="o">*</span> <span class="p">)</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
<span class="k">template</span><span class="o">&lt;</span><span class="k">typename</span> <span class="n">CppT</span><span class="o">&gt;</span>
<span class="k">static</span> <span class="n">PyTypeManager</span><span class="o">*</span> <span class="n">_get</span><span class="p">();</span>
<span class="k">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 set of
<span class="cb">pyAs&lt;&gt;</span> templates functions are supplieds.</p>
<p>Specialization for all POD type that can be directly translated into
Python types must be provideds (<tt class="docutils literal">bool</tt>, <tt class="docutils literal">int</tt>, <tt class="docutils literal">long</tt>, <tt class="docutils literal">double</tt>,
<tt class="docutils literal"><span class="pre">std::string</span></tt>, ...).</p>
<p>Partial specialization for type availables through the <tt class="docutils literal">PyTypeManager</tt>
is supplied. It checks the manager for the type's existence.</p>
<div class="highlight"><pre><span></span><span class="c1">// Most generic template, should *not* be used.</span>
<span class="c1">// So issue an error message and report a failed conversion.</span>
<span class="k">template</span><span class="o">&lt;</span><span class="k">typename</span> <span class="n">T</span><span class="o">&gt;</span>
<span class="kr">inline</span> <span class="kt">bool</span> <span class="n">pyAs</span> <span class="p">(</span> <span class="n">PyObject</span><span class="o">*</span> <span class="n">pyArg</span><span class="p">,</span> <span class="n">T</span><span class="o">&amp;</span> <span class="n">arg</span> <span class="p">)</span>
<span class="p">{</span>
<span class="n">std</span><span class="o">::</span><span class="n">cerr</span> <span class="o">&lt;&lt;</span> <span class="s">&quot;Isobar3.pyAs&lt;T&gt;(): Unsupported type </span><span class="se">\&quot;</span><span class="s">&quot;</span>
<span class="o">&lt;&lt;</span> <span class="n">demangle</span><span class="p">(</span><span class="k">typeid</span><span class="p">(</span><span class="n">T</span><span class="p">).</span><span class="n">name</span><span class="p">())</span> <span class="o">&lt;&lt;</span> <span class="s">&quot;</span><span class="se">\&quot;</span><span class="s">&quot;</span> <span class="o">&lt;&lt;</span> <span class="n">endl</span><span class="p">;</span>
<span class="k">return</span> <span class="nb">false</span><span class="p">;</span>
<span class="p">}</span>
<span class="c1">// Template specialization for POD type &quot;int&quot;.</span>
<span class="k">template</span><span class="o">&lt;&gt;</span>
<span class="kr">inline</span> <span class="kt">bool</span> <span class="n">pyAs</span> <span class="p">(</span> <span class="n">PyObject</span><span class="o">*</span> <span class="n">pyArg</span><span class="p">,</span> <span class="kt">int</span><span class="o">&amp;</span> <span class="n">arg</span> <span class="p">)</span>
<span class="p">{</span>
<span class="k">if</span> <span class="p">(</span><span class="n">PyInt_Check</span> <span class="p">(</span><span class="n">pyArg</span><span class="p">))</span> <span class="p">{</span> <span class="n">arg</span> <span class="o">=</span> <span class="n">PyInt_AsLong</span> <span class="p">(</span> <span class="n">pyArg</span> <span class="p">);</span> <span class="p">}</span>
<span class="k">else</span> <span class="k">if</span> <span class="p">(</span><span class="n">PyLong_Check</span><span class="p">(</span><span class="n">pyArg</span><span class="p">))</span> <span class="p">{</span> <span class="n">arg</span> <span class="o">=</span> <span class="n">PyLong_AsLong</span><span class="p">(</span> <span class="n">pyArg</span> <span class="p">);</span> <span class="p">}</span>
<span class="k">else</span> <span class="k">return</span> <span class="nb">false</span><span class="p">;</span>
<span class="k">return</span> <span class="nb">true</span><span class="p">;</span>
<span class="p">}</span>
<span class="c1">// Template partial specialization for any type registered in</span>
<span class="c1">// the PyTypeManager.</span>
<span class="k">template</span><span class="o">&lt;</span><span class="k">typename</span> <span class="n">T</span><span class="o">&gt;</span>
<span class="kr">inline</span> <span class="kt">bool</span> <span class="n">pyAs</span> <span class="p">(</span> <span class="n">PyObject</span><span class="o">*</span> <span class="n">pyArg</span><span class="p">,</span> <span class="n">T</span><span class="o">*&amp;</span> <span class="n">arg</span> <span class="p">)</span>
<span class="p">{</span>
<span class="n">PyTypeManager</span><span class="o">*</span> <span class="n">manager</span> <span class="o">=</span> <span class="n">PyTypeManager</span><span class="o">::</span><span class="n">_get</span><span class="o">&lt;</span><span class="n">T</span><span class="o">&gt;</span><span class="p">();</span>
<span class="k">if</span> <span class="p">(</span><span class="n">not</span> <span class="n">manager</span><span class="p">)</span> <span class="p">{</span>
<span class="n">std</span><span class="o">::</span><span class="n">cerr</span> <span class="o">&lt;&lt;</span> <span class="s">&quot;Isobar3.pyAs&lt;T&gt;(): Unsupported type </span><span class="se">\&quot;</span><span class="s">&quot;</span>
<span class="o">&lt;&lt;</span> <span class="k">typeid</span><span class="p">(</span><span class="n">T</span><span class="p">).</span><span class="n">name</span><span class="p">()</span> <span class="o">&lt;&lt;</span> <span class="s">&quot;</span><span class="se">\&quot;</span><span class="s">&quot;</span> <span class="o">&lt;&lt;</span> <span class="n">endl</span><span class="p">;</span>
<span class="k">return</span> <span class="nb">false</span><span class="p">;</span>
<span class="p">}</span>
<span class="n">arg</span> <span class="o">=</span> <span class="p">(</span><span class="n">T</span><span class="o">*</span><span class="p">)(</span> <span class="n">asVPtr</span><span class="p">(</span> <span class="n">pyArg</span> <span class="p">)</span><span class="o">-&gt;</span><span class="n">_object</span> <span class="p">);</span>
<span class="k">return</span> <span class="nb">true</span><span class="p">;</span>
<span class="p">}</span>
<span class="k">template</span><span class="o">&lt;</span><span class="k">typename</span> <span class="n">T</span><span class="o">&gt;</span>
<span class="kr">inline</span> <span class="kt">bool</span> <span class="n">pyAs</span> <span class="p">(</span> <span class="n">PyVoidPointer</span><span class="o">*</span> <span class="n">pyArg</span><span class="p">,</span> <span class="n">T</span><span class="o">*&amp;</span> <span class="n">arg</span> <span class="p">)</span>
<span class="p">{</span> <span class="k">return</span> <span class="n">pyAs</span><span class="p">(</span> <span class="p">(</span><span class="n">PyObject</span><span class="o">*</span><span class="p">)</span><span class="n">pyArg</span><span class="p">,</span> <span class="n">arg</span> <span class="p">);</span> <span class="p">}</span>
</pre></div>
</div>
<div class="section" id="going-from-c-to-python">
<h3><a class="toc-backref" href="#id9">2.4 Going From C++ to Python</a></h3>
<p>To convert a Python object into a C++ object, a set of
<span class="cb">objectLink&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="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="highlight"><pre><span></span><span class="c1">// The most generic template, should *not* be used. So raise a Python exception.</span>
<span class="k">template</span><span class="o">&lt;</span> <span class="k">typename</span> <span class="n">CppT</span> <span class="o">&gt;</span>
<span class="kr">inline</span> <span class="n">PyObject</span><span class="o">*</span> <span class="n">objectLink</span> <span class="p">(</span> <span class="n">CppT</span> <span class="n">object</span> <span class="p">)</span>
<span class="p">{</span>
<span class="n">std</span><span class="o">::</span><span class="n">string</span> <span class="n">message</span> <span class="o">=</span> <span class="s">&quot;Overload for Isobar3::objectLink&lt; &quot;</span>
<span class="o">+</span> <span class="n">demangle</span><span class="p">(</span><span class="k">typeid</span><span class="p">(</span><span class="n">CppT</span><span class="p">).</span><span class="n">name</span><span class="p">())</span> <span class="o">+</span> <span class="s">&quot; &gt;() is missing.&quot;</span><span class="p">;</span>
<span class="n">PyErr_SetString</span><span class="p">(</span> <span class="n">HurricaneError</span><span class="p">,</span> <span class="n">message</span><span class="p">.</span><span class="n">c_str</span><span class="p">()</span> <span class="p">);</span>
<span class="k">return</span> <span class="nb">NULL</span><span class="p">;</span>
<span class="p">}</span>
<span class="c1">// Disable this template if &quot;CppT&quot; is an already supported type.</span>
<span class="k">template</span><span class="o">&lt;</span> <span class="k">typename</span> <span class="n">CppT</span>
<span class="p">,</span> <span class="k">typename</span> <span class="n">std</span><span class="o">::</span><span class="n">enable_if</span><span class="o">&lt;</span> <span class="o">!</span><span class="n">std</span><span class="o">::</span><span class="n">is_same</span><span class="o">&lt;</span><span class="n">CppT</span><span class="p">,</span><span class="kt">bool</span><span class="o">&gt;::</span><span class="n">value</span>
<span class="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">objectLink</span> <span class="p">(</span> <span class="n">CppT</span><span class="o">*</span> <span class="n">object</span> <span class="p">)</span>
<span class="p">{</span> <span class="k">return</span> <span class="n">PyTypeManager</span><span class="o">::</span><span class="n">link</span><span class="o">&lt;</span><span class="n">CppT</span><span class="o">&gt;</span><span class="p">(</span> <span class="n">object</span> <span class="p">);</span> <span class="p">}</span>
<span class="c1">// Disable this template if &quot;CppT&quot; is an already supported type.</span>
<span class="k">template</span><span class="o">&lt;</span> <span class="k">typename</span> <span class="n">CppT</span>
<span class="p">,</span> <span class="k">typename</span> <span class="n">std</span><span class="o">::</span><span class="n">enable_if</span><span class="o">&lt;</span> <span class="o">!</span><span class="n">std</span><span class="o">::</span><span class="n">is_same</span><span class="o">&lt;</span><span class="n">CppT</span><span class="p">,</span><span class="kt">bool</span><span class="o">&gt;::</span><span class="n">value</span>
<span class="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">objectLink</span> <span class="p">(</span> <span class="k">const</span> <span class="n">CppT</span><span class="o">*</span> <span class="n">object</span> <span class="p">)</span>
<span class="p">{</span> <span class="k">return</span> <span class="n">PyTypeManager</span><span class="o">::</span><span class="n">link</span><span class="o">&lt;</span><span class="n">CppT</span><span class="o">&gt;</span><span class="p">(</span> <span class="k">const_cast</span><span class="o">&lt;</span><span class="n">CppT</span><span class="o">*&gt;</span><span class="p">(</span> <span class="n">object</span> <span class="p">));</span> <span class="p">}</span>
<span class="c1">// Specializations for POD type &quot;int&quot; .</span>
<span class="k">template</span><span class="o">&lt;&gt;</span> <span class="kr">inline</span> <span class="n">PyObject</span><span class="o">*</span> <span class="n">objectLink</span><span class="o">&lt;</span><span class="kt">int</span> <span class="o">&gt;</span> <span class="p">(</span> <span class="kt">int</span> <span class="n">i</span> <span class="p">)</span>
<span class="p">{</span> <span class="k">return</span> <span class="n">PyInt_FromLong</span><span class="p">(</span> <span class="n">i</span> <span class="p">);</span> <span class="p">}</span>
<span class="k">template</span><span class="o">&lt;&gt;</span> <span class="kr">inline</span> <span class="n">PyObject</span><span class="o">*</span> <span class="n">objectLink</span><span class="o">&lt;</span><span class="k">const</span> <span class="kt">int</span> <span class="o">&gt;</span> <span class="p">(</span> <span class="k">const</span> <span class="kt">int</span> <span class="n">i</span> <span class="p">)</span>
<span class="p">{</span> <span class="k">return</span> <span class="n">PyInt_FromLong</span><span class="p">(</span> <span class="n">i</span> <span class="p">);</span> <span class="p">}</span>
<span class="k">template</span><span class="o">&lt;&gt;</span> <span class="kr">inline</span> <span class="n">PyObject</span><span class="o">*</span> <span class="n">objectLink</span><span class="o">&lt;</span><span class="k">const</span> <span class="kt">int</span><span class="o">*&gt;</span> <span class="p">(</span> <span class="k">const</span> <span class="kt">int</span><span class="o">*</span> <span class="n">i</span> <span class="p">)</span>
<span class="p">{</span> <span class="k">return</span> <span class="n">PyInt_FromLong</span><span class="p">(</span> <span class="o">*</span><span class="n">i</span> <span class="p">);</span> <span class="p">}</span>
</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 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 is C++ <tt class="docutils literal"><span class="pre">std::tuple&lt;&gt;</span></tt>.</p>
<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">objectLink&lt;&gt;()</span></tt> to
wrap the value.</p>
<p>Here is an excerpt of the code:</p>
<div class="highlight"><pre><span></span><span class="c1">// Flavor for &quot;return by pointer&quot;.</span>
<span class="k">template</span><span class="o">&lt;</span> <span class="k">typename</span> <span class="n">TC</span><span class="p">,</span> <span class="k">typename</span> <span class="n">TR</span><span class="p">,</span> <span class="k">typename</span><span class="p">...</span> <span class="n">TArgs</span>
<span class="p">,</span> <span class="k">typename</span> <span class="n">std</span><span class="o">::</span><span class="n">enable_if</span><span class="o">&lt;</span><span class="n">std</span><span class="o">::</span><span class="n">is_pointer</span><span class="o">&lt;</span><span class="n">TR</span><span class="o">&gt;::</span><span class="n">value</span><span class="p">,</span><span class="kt">bool</span><span class="o">&gt;::</span><span class="n">type</span> <span class="o">=</span> <span class="nb">true</span> <span class="o">&gt;</span>
<span class="kr">inline</span> <span class="n">PyObject</span><span class="o">*</span> <span class="n">_callMethodReturn</span> <span class="p">(</span> <span class="n">TR</span><span class="p">(</span><span class="n">TC</span><span class="o">::*</span> <span class="n">method</span><span class="p">)(</span><span class="n">TArgs</span><span class="p">...),</span> <span class="n">TC</span><span class="o">*</span> <span class="n">cppObject</span><span class="p">,</span> <span class="n">TArgs</span><span class="p">...</span> <span class="n">args</span> <span class="p">)</span>
<span class="p">{</span>
<span class="n">TR</span> <span class="n">pvalue</span> <span class="o">=</span> <span class="p">(</span><span class="n">cppObject</span><span class="o">-&gt;*</span><span class="n">method</span><span class="p">)(</span> <span class="n">args</span><span class="p">...</span> <span class="p">);</span>
<span class="k">return</span> <span class="nf">objectLink</span><span class="p">(</span> <span class="n">pvalue</span> <span class="p">);</span>
<span class="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="n">TC</span><span class="p">,</span> <span class="k">typename</span> <span class="n">TR</span><span class="p">,</span> <span class="k">typename</span><span class="p">...</span> <span class="n">TArgs</span>
<span class="p">,</span> <span class="k">typename</span> <span class="n">std</span><span class="o">::</span><span class="n">enable_if</span><span class="o">&lt;</span><span class="n">std</span><span class="o">::</span><span class="n">is_void</span><span class="o">&lt;</span><span class="n">TR</span><span class="o">&gt;::</span><span class="n">value</span><span class="p">,</span><span class="kt">bool</span><span class="o">&gt;::</span><span class="n">type</span> <span class="o">=</span> <span class="nb">true</span> <span class="o">&gt;</span>
<span class="kr">inline</span> <span class="n">PyObject</span><span class="o">*</span> <span class="n">_callMethodReturn</span> <span class="p">(</span> <span class="n">TR</span><span class="p">(</span><span class="n">TC</span><span class="o">::*</span> <span class="n">method</span><span class="p">)(</span><span class="n">TArgs</span><span class="p">...),</span> <span class="n">TC</span><span class="o">*</span> <span class="n">cppObject</span><span class="p">,</span> <span class="n">TArgs</span><span class="p">...</span> <span class="n">args</span> <span class="p">)</span>
<span class="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">// Function without argument.</span>
<span class="k">template</span><span class="o">&lt;</span> <span class="k">typename</span> <span class="n">TC</span><span class="p">,</span> <span class="k">typename</span> <span class="n">TR</span> <span class="o">&gt;</span>
<span class="kr">inline</span> <span class="n">PyObject</span><span class="o">*</span> <span class="n">_callMethod</span> <span class="p">(</span> <span class="n">TR</span><span class="p">(</span><span class="n">TC</span><span class="o">::*</span> <span class="n">method</span><span class="p">)(),</span> <span class="n">TC</span><span class="o">*</span> <span class="n">cppObject</span><span class="p">,</span> <span class="n">std</span><span class="o">::</span><span class="n">tuple</span><span class="o">&lt;&gt;</span> <span class="p">)</span>
<span class="p">{</span> <span class="k">return</span> <span class="n">_callMethodReturn</span><span class="o">&lt;</span><span class="n">TC</span><span class="p">,</span><span class="n">TR</span><span class="o">&gt;</span><span class="p">(</span> <span class="n">method</span><span class="p">,</span> <span class="n">cppObject</span> <span class="p">);</span> <span class="p">}</span>
<span class="c1">// Function with one argument.</span>
<span class="k">template</span><span class="o">&lt;</span> <span class="k">typename</span> <span class="n">TC</span><span class="p">,</span> <span class="k">typename</span> <span class="n">TR</span><span class="p">,</span> <span class="k">typename</span> <span class="n">TA0</span> <span class="o">&gt;</span>
<span class="kr">inline</span> <span class="n">PyObject</span><span class="o">*</span> <span class="n">_callMethod</span> <span class="p">(</span> <span class="n">TR</span><span class="p">(</span><span class="n">TC</span><span class="o">::*</span> <span class="n">method</span><span class="p">)(</span><span class="n">TA0</span><span class="p">),</span> <span class="n">TC</span><span class="o">*</span> <span class="n">cppObject</span><span class="p">,</span> <span class="n">std</span><span class="o">::</span><span class="n">tuple</span><span class="o">&lt;</span><span class="n">TA0</span><span class="o">&gt;</span> <span class="n">args</span> <span class="p">)</span>
<span class="p">{</span> <span class="k">return</span> <span class="n">_callMethodReturn</span><span class="p">(</span> <span class="n">method</span><span class="p">,</span> <span class="n">cppObject</span><span class="p">,</span> <span class="n">std</span><span class="o">::</span><span class="n">get</span><span class="o">&lt;</span><span class="mi">0</span><span class="o">&gt;</span><span class="p">(</span><span class="n">args</span><span class="p">)</span> <span class="p">);</span> <span class="p">}</span>
<span class="c1">// Function with two arguments.</span>
<span class="k">template</span><span class="o">&lt;</span> <span class="k">typename</span> <span class="n">TC</span><span class="p">,</span> <span class="k">typename</span> <span class="n">TR</span><span class="p">,</span> <span class="k">typename</span> <span class="n">TA0</span><span class="p">,</span> <span class="k">typename</span> <span class="n">TA1</span> <span class="o">&gt;</span>
<span class="n">PyObject</span><span class="o">*</span> <span class="n">_callMethod</span> <span class="p">(</span> <span class="n">TR</span><span class="p">(</span><span class="n">TC</span><span class="o">::*</span> <span class="n">method</span><span class="p">)(</span><span class="n">TA0</span><span class="p">,</span><span class="n">TA1</span><span class="p">),</span> <span class="n">TC</span><span class="o">*</span> <span class="n">cppObject</span><span class="p">,</span> <span class="n">std</span><span class="o">::</span><span class="n">tuple</span><span class="o">&lt;</span><span class="n">TA0</span><span class="p">,</span><span class="n">TA1</span><span class="o">&gt;</span> <span class="n">args</span> <span class="p">)</span>
<span class="p">{</span> <span class="k">return</span> <span class="n">_callMethodReturn</span><span class="p">(</span> <span class="n">method</span><span class="p">,</span> <span class="n">cppObject</span><span class="p">,</span> <span class="n">std</span><span class="o">::</span><span class="n">get</span><span class="o">&lt;</span><span class="mi">0</span><span class="o">&gt;</span><span class="p">(</span><span class="n">args</span><span class="p">),</span> <span class="n">std</span><span class="o">::</span><span class="n">get</span><span class="o">&lt;</span><span class="mi">1</span><span class="o">&gt;</span><span class="p">(</span><span class="n">args</span><span class="p">)</span> <span class="p">);</span> <span class="p">}</span>
</pre></div>
<p>The complete work of translating the Python tuple into a <tt class="docutils literal"><span class="pre">std::tuple&lt;&gt;</span></tt> and error
handling is done with a dedicated template class <tt class="docutils literal">PyMethodWrapper</tt> and it's <tt class="docutils literal">call()</tt>
method.</p>
<p>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="nf">PyParameter_addValue</span> <span class="p">(</span> <span class="n">PyVoidPointer</span><span class="o">*</span> <span class="n">self</span><span class="p">,</span> <span class="n">PyObject</span><span class="o">*</span> <span class="n">args</span> <span class="p">)</span>
<span class="p">{</span> <span class="k">return</span> <span class="n">callMethod</span><span class="p">(</span><span class="s">&quot;Parameter.addValue&quot;</span><span class="p">,</span><span class="o">&amp;</span><span class="n">Parameter</span><span class="o">::</span><span class="n">addValue</span><span class="p">,</span><span class="n">self</span><span class="p">,</span><span class="n">args</span><span class="p">);</span> <span class="p">}</span>
<span class="n">PyMethodDef</span> <span class="n">PyParameter_Methods</span><span class="p">[]</span> <span class="o">=</span>
<span class="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 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="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="nf">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="nf">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="nf">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="nf">PyParameter_setString</span> <span class="p">(</span> <span class="n">PyVoidPointer</span><span class="o">*</span> <span class="n">self</span><span class="p">,</span> <span class="n">PyObject</span><span class="o">*</span> <span class="n">args</span> <span class="p">)</span>
<span class="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="nf">PyCfg_hasParameter</span> <span class="p">(</span> <span class="n">PyObject</span><span class="o">*</span> <span class="n">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="n">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">pyTypePostModuleInit</span><span class="o">&lt;</span><span class="n">Cfg</span><span class="o">::</span><span class="n">Parameter</span><span class="o">&gt;</span> <span class="p">(</span> <span class="n">PyTypeObject</span><span class="o">*</span> <span class="n">typeObject</span> <span class="p">)</span>
<span class="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">pyTypePostModuleInit</span><span class="o">&lt;</span><span class="n">Cfg</span><span class="o">::</span><span class="n">Parameter</span><span class="o">::</span><span class="n">Priority</span><span class="o">&gt;</span> <span class="p">(</span> <span class="n">PyTypeObject</span><span class="o">*</span> <span class="n">typeObject</span> <span class="p">)</span>
<span class="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>