691 lines
70 KiB
HTML
691 lines
70 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 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<>()</span></tt> and <tt class="docutils literal"><span class="pre">callFunction<>()</span></tt> function templates to
|
||
|
automatically generate a wrapper around C++ object methods or functions.</li>
|
||
|
<li>The <tt class="docutils literal">PyBoject* objectLink<CppT>( CppT* )</tt> functions templates that provides C++ to Python
|
||
|
object translation.</li>
|
||
|
<li>The <tt class="docutils literal">bool pyAs<CppT>( PyObject*, CppT*& )</tt> functions templates that provides Python to C++
|
||
|
object translation.</li>
|
||
|
</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">"C"</span> <span class="p">{</span>
|
||
|
<span class="n">typedef</span> <span class="n">struct</span> <span class="n">PyVoidPointer</span> <span class="p">{</span>
|
||
|
<span class="n">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">"C"</span> <span class="p">{</span>
|
||
|
<span class="n">typedef</span> <span class="n">struct</span> <span class="n">PyVoidPointer</span> <span class="p">{</span>
|
||
|
<span class="n">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<CppT></span></p>
|
||
|
<p>Is an intermediate between the non-template base class and all the
|
||
|
templatized others.</p>
|
||
|
</li>
|
||
|
<li><p class="first"><span class="cb">PyTypeManagerNonDBo<CppT></span></p>
|
||
|
<p>Template for standalone C++ classes that are not derived from <tt class="docutils literal">DBo</tt>.
|
||
|
For example <tt class="docutils literal">Box</tt> or <tt class="docutils literal">Parameter</tt>.</p>
|
||
|
</li>
|
||
|
<li><p class="first"><span class="cb">PyTypeManagerDBo<CppT></span></p>
|
||
|
<p>Template for C++ classes that <em>are</em> not derived from <tt class="docutils literal">DBo</tt>.
|
||
|
For example <tt class="docutils literal">Cell</tt> or <tt class="docutils literal">Instance</tt>.</p>
|
||
|
</li>
|
||
|
<li><p class="first"><span class="cb">PyTypeManagerVector<CppT></span>, template for C++ <tt class="docutils literal"><span class="pre">std::vector<CppT*></span></tt>.</p>
|
||
|
</li>
|
||
|
<li><p class="first"><span class="cb">PyTypeManagerVectorIterator<CppT></span></p>
|
||
|
<p>Template for C++ <tt class="docutils literal"><span class="pre">std::vector<CppT*>::iterator</span></tt>, automatically created
|
||
|
from the vector registration.</p>
|
||
|
</li>
|
||
|
<li><p class="first"><span class="cb">PyTypeManagerMap<CppK,CppT></span>, template for C++ <tt class="docutils literal"><span class="pre">std::map<CppK*,CppT*></span></tt>.</p>
|
||
|
</li>
|
||
|
<li><p class="first"><span class="cb">PyTypeManagerMapIterator<CppK,CppT></span></p>
|
||
|
<p>Template for C++ <tt class="docutils literal"><span class="pre">std::vector<CppK*,CppT*>::iterator</span></tt>, automatically created
|
||
|
from the map registration.</p>
|
||
|
</li>
|
||
|
<li><p class="first"><span class="cb">PyTypeManagerCollection<,CppT></span>, template for C++ <tt class="docutils literal"><span class="pre">Hurricane::Collection<CppT*></span></tt>.</p>
|
||
|
</li>
|
||
|
<li><p class="first"><span class="cb">PyTypeManagerCollectionIterator<,CppT></span></p>
|
||
|
<p>Template for C++ <tt class="docutils literal"><span class="pre">Hurricane::Locator<CppT*></span></tt>, automatically created from
|
||
|
the collection registration.</p>
|
||
|
</li>
|
||
|
</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">"C"</span> <span class="p">{</span>
|
||
|
|
||
|
<span class="c1">// Here we have C-linkage.</span>
|
||
|
<span class="k">extern</span> <span class="kt">long</span> <span class="n">_tpHash</span> <span class="p">(</span> <span class="n">PyObject</span><span class="o">*</span> <span class="n">self</span> <span class="p">)</span>
|
||
|
<span class="p">{</span>
|
||
|
<span class="c1">// Dispatch towards the relevant class, based on ob_type pointer.</span>
|
||
|
<span class="k">return</span> <span class="n">PyTypeManager</span><span class="o">::</span><span class="n">get</span><span class="p">(</span> <span class="n">self</span><span class="o">-></span><span class="n">ob_type</span> <span class="p">)</span><span class="o">-></span><span class="n">_getTpHash</span><span class="p">(</span> <span class="n">asVPtr</span><span class="p">(</span><span class="n">self</span><span class="p">)</span> <span class="p">);</span>
|
||
|
<span class="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"><</span><span class="k">typename</span> <span class="n">CppT</span><span class="o">></span>
|
||
|
<span class="k">static</span> <span class="n">PyTypeManager</span><span class="o">*</span> <span class="n">_get</span><span class="p">();</span>
|
||
|
<span class="k">private</span><span class="o">:</span>
|
||
|
<span class="n">PyTypeObject</span> <span class="n">_typeObject</span><span class="p">;</span>
|
||
|
<span class="p">};</span>
|
||
|
|
||
|
<span class="kt">void</span> <span class="n">PyTypeManager</span><span class="o">::</span><span class="n">_setupPyType</span> <span class="p">()</span>
|
||
|
<span class="p">{</span>
|
||
|
<span class="n">PyTypeObject</span><span class="o">*</span> <span class="n">ob_type</span> <span class="o">=</span> <span class="n">_getTypeObject</span><span class="p">();</span>
|
||
|
<span class="n">ob_type</span><span class="o">-></span><span class="n">tp_name</span> <span class="o">=</span> <span class="n">_getPyTypeName</span><span class="p">().</span><span class="n">c_str</span><span class="p">();</span>
|
||
|
<span class="n">ob_type</span><span class="o">-></span><span class="n">tp_dealloc</span> <span class="o">=</span> <span class="p">(</span><span class="n">destructor</span><span class="p">)</span><span class="o">&::</span><span class="n">Isobar3</span><span class="o">::</span><span class="n">_tpDeAlloc</span><span class="p">;</span>
|
||
|
<span class="n">ob_type</span><span class="o">-></span><span class="n">tp_str</span> <span class="o">=</span> <span class="p">(</span><span class="n">reprfunc</span><span class="p">)</span> <span class="o">&::</span><span class="n">Isobar3</span><span class="o">::</span><span class="n">_tpStr</span><span class="p">;</span>
|
||
|
<span class="c1">// All Python Type will call the same _tpHash().</span>
|
||
|
<span class="n">ob_type</span><span class="o">-></span><span class="n">tp_hash</span> <span class="o">=</span> <span class="p">(</span><span class="n">hashfunc</span><span class="p">)</span> <span class="o">&::</span><span class="n">Isobar3</span><span class="o">::</span><span class="n">_tpHash</span><span class="p">;</span>
|
||
|
<span class="n">ob_type</span><span class="o">-></span><span class="n">tp_compare</span> <span class="o">=</span> <span class="p">(</span><span class="n">cmpfunc</span><span class="p">)</span> <span class="o">&::</span><span class="n">Isobar3</span><span class="o">::</span><span class="n">_getTpCompare</span><span class="p">;</span>
|
||
|
<span class="n">ob_type</span><span class="o">-></span><span class="n">tp_methods</span> <span class="o">=</span> <span class="n">_getMethods</span><span class="p">();</span>
|
||
|
<span class="n">ob_type</span><span class="o">-></span><span class="n">tp_getset</span> <span class="o">=</span> <span class="n">_getGetsets</span><span class="p">();</span>
|
||
|
<span class="p">}</span>
|
||
|
|
||
|
<span class="p">}</span> <span class="c1">// Isobar3 namespace.</span>
|
||
|
</pre></div>
|
||
|
</div>
|
||
|
<div class="section" id="going-from-python-to-c">
|
||
|
<h3><a class="toc-backref" href="#id8">2.3 Going From Python to C++</a></h3>
|
||
|
<p>To convert a C++ object (pointer) into a Python object, a set of
|
||
|
<span class="cb">pyAs<></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"><</span><span class="k">typename</span> <span class="n">T</span><span class="o">></span>
|
||
|
<span class="kr">inline</span> <span class="kt">bool</span> <span class="n">pyAs</span> <span class="p">(</span> <span class="n">PyObject</span><span class="o">*</span> <span class="n">pyArg</span><span class="p">,</span> <span class="n">T</span><span class="o">&</span> <span class="n">arg</span> <span class="p">)</span>
|
||
|
<span class="p">{</span>
|
||
|
<span class="n">std</span><span class="o">::</span><span class="n">cerr</span> <span class="o"><<</span> <span class="s">"Isobar3.pyAs<T>(): Unsupported type </span><span class="se">\"</span><span class="s">"</span>
|
||
|
<span class="o"><<</span> <span class="n">demangle</span><span class="p">(</span><span class="k">typeid</span><span class="p">(</span><span class="n">T</span><span class="p">).</span><span class="n">name</span><span class="p">())</span> <span class="o"><<</span> <span class="s">"</span><span class="se">\"</span><span class="s">"</span> <span class="o"><<</span> <span class="n">endl</span><span class="p">;</span>
|
||
|
<span class="k">return</span> <span class="nb">false</span><span class="p">;</span>
|
||
|
<span class="p">}</span>
|
||
|
|
||
|
<span class="c1">// Template specialization for POD type "int".</span>
|
||
|
<span class="k">template</span><span class="o"><></span>
|
||
|
<span class="kr">inline</span> <span class="kt">bool</span> <span class="n">pyAs</span> <span class="p">(</span> <span class="n">PyObject</span><span class="o">*</span> <span class="n">pyArg</span><span class="p">,</span> <span class="kt">int</span><span class="o">&</span> <span class="n">arg</span> <span class="p">)</span>
|
||
|
<span class="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"><</span><span class="k">typename</span> <span class="n">T</span><span class="o">></span>
|
||
|
<span class="kr">inline</span> <span class="kt">bool</span> <span class="n">pyAs</span> <span class="p">(</span> <span class="n">PyObject</span><span class="o">*</span> <span class="n">pyArg</span><span class="p">,</span> <span class="n">T</span><span class="o">*&</span> <span class="n">arg</span> <span class="p">)</span>
|
||
|
<span class="p">{</span>
|
||
|
<span class="n">PyTypeManager</span><span class="o">*</span> <span class="n">manager</span> <span class="o">=</span> <span class="n">PyTypeManager</span><span class="o">::</span><span class="n">_get</span><span class="o"><</span><span class="n">T</span><span class="o">></span><span class="p">();</span>
|
||
|
<span class="k">if</span> <span class="p">(</span><span class="n">not</span> <span class="n">manager</span><span class="p">)</span> <span class="p">{</span>
|
||
|
<span class="n">std</span><span class="o">::</span><span class="n">cerr</span> <span class="o"><<</span> <span class="s">"Isobar3.pyAs<T>(): Unsupported type </span><span class="se">\"</span><span class="s">"</span>
|
||
|
<span class="o"><<</span> <span class="k">typeid</span><span class="p">(</span><span class="n">T</span><span class="p">).</span><span class="n">name</span><span class="p">()</span> <span class="o"><<</span> <span class="s">"</span><span class="se">\"</span><span class="s">"</span> <span class="o"><<</span> <span class="n">endl</span><span class="p">;</span>
|
||
|
<span class="k">return</span> <span class="nb">false</span><span class="p">;</span>
|
||
|
<span class="p">}</span>
|
||
|
<span class="n">arg</span> <span class="o">=</span> <span class="p">(</span><span class="n">T</span><span class="o">*</span><span class="p">)(</span> <span class="n">asVPtr</span><span class="p">(</span> <span class="n">pyArg</span> <span class="p">)</span><span class="o">-></span><span class="n">_object</span> <span class="p">);</span>
|
||
|
<span class="k">return</span> <span class="nb">true</span><span class="p">;</span>
|
||
|
<span class="p">}</span>
|
||
|
|
||
|
<span class="k">template</span><span class="o"><</span><span class="k">typename</span> <span class="n">T</span><span class="o">></span>
|
||
|
<span class="kr">inline</span> <span class="kt">bool</span> <span class="n">pyAs</span> <span class="p">(</span> <span class="n">PyVoidPointer</span><span class="o">*</span> <span class="n">pyArg</span><span class="p">,</span> <span class="n">T</span><span class="o">*&</span> <span class="n">arg</span> <span class="p">)</span>
|
||
|
<span class="p">{</span> <span class="k">return</span> <span class="n">pyAs</span><span class="p">(</span> <span class="p">(</span><span class="n">PyObject</span><span class="o">*</span><span class="p">)</span><span class="n">pyArg</span><span class="p">,</span> <span class="n">arg</span> <span class="p">);</span> <span class="p">}</span>
|
||
|
</pre></div>
|
||
|
</div>
|
||
|
<div class="section" id="going-from-c-to-python">
|
||
|
<h3><a class="toc-backref" href="#id9">2.4 Going From C++ to Python</a></h3>
|
||
|
<p>To convert a Python object into a C++ object, a set of
|
||
|
<span class="cb">objectLink<></span> templates functions are supplieds.</p>
|
||
|
<p>We completely disable the partially specialized templates for
|
||
|
objects that are non-POD as the compiler seems to be unable to
|
||
|
choose the fully specialized template in this case (or I still
|
||
|
misunderstood the template resolution mechanism).</p>
|
||
|
<p>In the case of object registered in <tt class="docutils literal">PyTypeManager</tt>, we delegate
|
||
|
the <tt class="docutils literal">PyObject</tt> creation to the <tt class="docutils literal"><span class="pre">PyTypeManager::link()</span></tt> template
|
||
|
function, which in turn, can call the right <tt class="docutils literal"><span class="pre">PyTypeManagerVTrunk<CppT>::_link()</span></tt>
|
||
|
method.</p>
|
||
|
<div class="note">
|
||
|
<p class="first admonition-title">Note</p>
|
||
|
<p class="last">The <tt class="docutils literal"><span class="pre">PyTypeManagerVTrunk<CppT>::_link()</span></tt> method is the reason
|
||
|
<strong>why</strong> we need the intermediate <tt class="docutils literal">PyTypeManagerVTrunk<CppT></tt>
|
||
|
template class.</p>
|
||
|
</div>
|
||
|
<div class="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"><</span> <span class="k">typename</span> <span class="n">CppT</span> <span class="o">></span>
|
||
|
<span class="kr">inline</span> <span class="n">PyObject</span><span class="o">*</span> <span class="n">objectLink</span> <span class="p">(</span> <span class="n">CppT</span> <span class="n">object</span> <span class="p">)</span>
|
||
|
<span class="p">{</span>
|
||
|
<span class="n">std</span><span class="o">::</span><span class="n">string</span> <span class="n">message</span> <span class="o">=</span> <span class="s">"Overload for Isobar3::objectLink< "</span>
|
||
|
<span class="o">+</span> <span class="n">demangle</span><span class="p">(</span><span class="k">typeid</span><span class="p">(</span><span class="n">CppT</span><span class="p">).</span><span class="n">name</span><span class="p">())</span> <span class="o">+</span> <span class="s">" >() is missing."</span><span class="p">;</span>
|
||
|
<span class="n">PyErr_SetString</span><span class="p">(</span> <span class="n">HurricaneError</span><span class="p">,</span> <span class="n">message</span><span class="p">.</span><span class="n">c_str</span><span class="p">()</span> <span class="p">);</span>
|
||
|
<span class="k">return</span> <span class="nb">NULL</span><span class="p">;</span>
|
||
|
<span class="p">}</span>
|
||
|
|
||
|
<span class="c1">// Disable this template if "CppT" is an already supported type.</span>
|
||
|
<span class="k">template</span><span class="o"><</span> <span class="k">typename</span> <span class="n">CppT</span>
|
||
|
<span class="p">,</span> <span class="k">typename</span> <span class="n">std</span><span class="o">::</span><span class="n">enable_if</span><span class="o"><</span> <span class="o">!</span><span class="n">std</span><span class="o">::</span><span class="n">is_same</span><span class="o"><</span><span class="n">CppT</span><span class="p">,</span><span class="kt">bool</span><span class="o">>::</span><span class="n">value</span>
|
||
|
<span class="o">&&</span> <span class="o">!</span><span class="n">std</span><span class="o">::</span><span class="n">is_same</span><span class="o"><</span><span class="n">CppT</span><span class="p">,</span><span class="kt">int</span> <span class="o">>::</span><span class="n">value</span>
|
||
|
<span class="o">&&</span> <span class="o">!</span><span class="n">std</span><span class="o">::</span><span class="n">is_same</span><span class="o"><</span><span class="n">CppT</span><span class="p">,</span><span class="n">std</span><span class="o">::</span><span class="n">string</span><span class="o">>::</span><span class="n">value</span>
|
||
|
<span class="o">&&</span> <span class="o">!</span><span class="n">std</span><span class="o">::</span><span class="n">is_same</span><span class="o"><</span><span class="n">CppT</span><span class="p">,</span><span class="k">const</span> <span class="n">std</span><span class="o">::</span><span class="n">string</span><span class="o">>::</span><span class="n">value</span>
|
||
|
<span class="p">,</span><span class="kt">bool</span><span class="o">>::</span><span class="n">type</span> <span class="o">=</span> <span class="nb">true</span> <span class="o">></span>
|
||
|
<span class="kr">inline</span> <span class="n">PyObject</span><span class="o">*</span> <span class="n">objectLink</span> <span class="p">(</span> <span class="n">CppT</span><span class="o">*</span> <span class="n">object</span> <span class="p">)</span>
|
||
|
<span class="p">{</span> <span class="k">return</span> <span class="n">PyTypeManager</span><span class="o">::</span><span class="n">link</span><span class="o"><</span><span class="n">CppT</span><span class="o">></span><span class="p">(</span> <span class="n">object</span> <span class="p">);</span> <span class="p">}</span>
|
||
|
|
||
|
<span class="c1">// Disable this template if "CppT" is an already supported type.</span>
|
||
|
<span class="k">template</span><span class="o"><</span> <span class="k">typename</span> <span class="n">CppT</span>
|
||
|
<span class="p">,</span> <span class="k">typename</span> <span class="n">std</span><span class="o">::</span><span class="n">enable_if</span><span class="o"><</span> <span class="o">!</span><span class="n">std</span><span class="o">::</span><span class="n">is_same</span><span class="o"><</span><span class="n">CppT</span><span class="p">,</span><span class="kt">bool</span><span class="o">>::</span><span class="n">value</span>
|
||
|
<span class="o">&&</span> <span class="o">!</span><span class="n">std</span><span class="o">::</span><span class="n">is_same</span><span class="o"><</span><span class="n">CppT</span><span class="p">,</span><span class="kt">int</span> <span class="o">>::</span><span class="n">value</span>
|
||
|
<span class="o">&&</span> <span class="o">!</span><span class="n">std</span><span class="o">::</span><span class="n">is_same</span><span class="o"><</span><span class="n">CppT</span><span class="p">,</span><span class="n">std</span><span class="o">::</span><span class="n">string</span><span class="o">>::</span><span class="n">value</span>
|
||
|
<span class="o">&&</span> <span class="o">!</span><span class="n">std</span><span class="o">::</span><span class="n">is_same</span><span class="o"><</span><span class="n">CppT</span><span class="p">,</span><span class="k">const</span> <span class="n">std</span><span class="o">::</span><span class="n">string</span><span class="o">>::</span><span class="n">value</span>
|
||
|
<span class="p">,</span><span class="kt">bool</span><span class="o">>::</span><span class="n">type</span> <span class="o">=</span> <span class="nb">true</span> <span class="o">></span>
|
||
|
<span class="kr">inline</span> <span class="n">PyObject</span><span class="o">*</span> <span class="n">objectLink</span> <span class="p">(</span> <span class="k">const</span> <span class="n">CppT</span><span class="o">*</span> <span class="n">object</span> <span class="p">)</span>
|
||
|
<span class="p">{</span> <span class="k">return</span> <span class="n">PyTypeManager</span><span class="o">::</span><span class="n">link</span><span class="o"><</span><span class="n">CppT</span><span class="o">></span><span class="p">(</span> <span class="k">const_cast</span><span class="o"><</span><span class="n">CppT</span><span class="o">*></span><span class="p">(</span> <span class="n">object</span> <span class="p">));</span> <span class="p">}</span>
|
||
|
|
||
|
<span class="c1">// Specializations for POD type "int" .</span>
|
||
|
<span class="k">template</span><span class="o"><></span> <span class="kr">inline</span> <span class="n">PyObject</span><span class="o">*</span> <span class="n">objectLink</span><span class="o"><</span><span class="kt">int</span> <span class="o">></span> <span class="p">(</span> <span class="kt">int</span> <span class="n">i</span> <span class="p">)</span>
|
||
|
<span class="p">{</span> <span class="k">return</span> <span class="n">PyInt_FromLong</span><span class="p">(</span> <span class="n">i</span> <span class="p">);</span> <span class="p">}</span>
|
||
|
|
||
|
<span class="k">template</span><span class="o"><></span> <span class="kr">inline</span> <span class="n">PyObject</span><span class="o">*</span> <span class="n">objectLink</span><span class="o"><</span><span class="k">const</span> <span class="kt">int</span> <span class="o">></span> <span class="p">(</span> <span class="k">const</span> <span class="kt">int</span> <span class="n">i</span> <span class="p">)</span>
|
||
|
<span class="p">{</span> <span class="k">return</span> <span class="n">PyInt_FromLong</span><span class="p">(</span> <span class="n">i</span> <span class="p">);</span> <span class="p">}</span>
|
||
|
|
||
|
<span class="k">template</span><span class="o"><></span> <span class="kr">inline</span> <span class="n">PyObject</span><span class="o">*</span> <span class="n">objectLink</span><span class="o"><</span><span class="k">const</span> <span class="kt">int</span><span class="o">*></span> <span class="p">(</span> <span class="k">const</span> <span class="kt">int</span><span class="o">*</span> <span class="n">i</span> <span class="p">)</span>
|
||
|
<span class="p">{</span> <span class="k">return</span> <span class="n">PyInt_FromLong</span><span class="p">(</span> <span class="o">*</span><span class="n">i</span> <span class="p">);</span> <span class="p">}</span>
|
||
|
</pre></div>
|
||
|
</div>
|
||
|
<div class="section" id="object-methods-wrappers">
|
||
|
<h3><a class="toc-backref" href="#id10">2.5 Object Methods Wrappers</a></h3>
|
||
|
<p>One of the more tedious task in exporting a C++ interface towards Python is
|
||
|
to have wrap the C++ functions/methods into C-linkage functions that can be
|
||
|
put into the <tt class="docutils literal">PyMethodDef</tt> table.</p>
|
||
|
<p>Basically, we want to fit:</p>
|
||
|
<ul>
|
||
|
<li><p class="first">A C++ function or method with a variable number of arguments, each argument
|
||
|
having it's own type.</p>
|
||
|
<div class="highlight"><pre><span></span><span class="k">class</span> <span class="nc">Parameter</span> <span class="p">{</span>
|
||
|
<span class="c1">// ...</span>
|
||
|
<span class="k">public</span><span class="o">:</span>
|
||
|
<span class="kt">void</span> <span class="n">addValue</span> <span class="p">(</span> <span class="n">std</span><span class="o">::</span><span class="n">string</span> <span class="n">s</span><span class="p">,</span> <span class="kt">int</span> <span class="n">v</span> <span class="p">);</span>
|
||
|
<span class="c1">// ...</span>
|
||
|
<span class="p">};</span>
|
||
|
</pre></div>
|
||
|
</li>
|
||
|
<li><p class="first">Into a <tt class="docutils literal">PyCFunction</tt> prototype.</p>
|
||
|
<div class="highlight"><pre><span></span><span class="k">extern</span> <span class="s">"C"</span> <span class="p">{</span>
|
||
|
<span class="k">typedef</span> <span class="n">PyObject</span><span class="o">*</span> <span class="p">(</span> <span class="o">*</span><span class="n">PyCFunction</span> <span class="p">)(</span> <span class="n">PyObject</span><span class="o">*</span> <span class="n">self</span><span class="p">,</span> <span class="n">PyObject</span><span class="o">*</span> <span class="n">args</span> <span class="p">);</span>
|
||
|
<span class="p">}</span>
|
||
|
</pre></div>
|
||
|
<p>Here, the C++ object is provided through the first argument and the
|
||
|
functions arguments through a <em>tuple</em> in second argument. In Python
|
||
|
wrappers, the tuple doesn't have any complex structure, it boils down
|
||
|
to a sequence of <tt class="docutils literal">PyObject*</tt> (that must match the number of arguments
|
||
|
of it's C++ function conterpart).</p>
|
||
|
</li>
|
||
|
</ul>
|
||
|
<p>So, the problem is to change a 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<></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<>()</span></tt>
|
||
|
that takes whatever the C++ return type is, and turn it into a <tt class="docutils literal">PyObject*</tt>.
|
||
|
Except for the functions returning <tt class="docutils literal">void</tt>, we call <tt class="docutils literal"><span class="pre">objectLink<>()</span></tt> to
|
||
|
wrap the value.</p>
|
||
|
<p>Here is an excerpt of the code:</p>
|
||
|
<div class="highlight"><pre><span></span><span class="c1">// Flavor for "return by pointer".</span>
|
||
|
<span class="k">template</span><span class="o"><</span> <span class="k">typename</span> <span class="n">TC</span><span class="p">,</span> <span class="k">typename</span> <span class="n">TR</span><span class="p">,</span> <span class="k">typename</span><span class="p">...</span> <span class="n">TArgs</span>
|
||
|
<span class="p">,</span> <span class="k">typename</span> <span class="n">std</span><span class="o">::</span><span class="n">enable_if</span><span class="o"><</span><span class="n">std</span><span class="o">::</span><span class="n">is_pointer</span><span class="o"><</span><span class="n">TR</span><span class="o">>::</span><span class="n">value</span><span class="p">,</span><span class="kt">bool</span><span class="o">>::</span><span class="n">type</span> <span class="o">=</span> <span class="nb">true</span> <span class="o">></span>
|
||
|
<span class="kr">inline</span> <span class="n">PyObject</span><span class="o">*</span> <span class="n">_callMethodReturn</span> <span class="p">(</span> <span class="n">TR</span><span class="p">(</span><span class="n">TC</span><span class="o">::*</span> <span class="n">method</span><span class="p">)(</span><span class="n">TArgs</span><span class="p">...),</span> <span class="n">TC</span><span class="o">*</span> <span class="n">cppObject</span><span class="p">,</span> <span class="n">TArgs</span><span class="p">...</span> <span class="n">args</span> <span class="p">)</span>
|
||
|
<span class="p">{</span>
|
||
|
<span class="n">TR</span> <span class="n">pvalue</span> <span class="o">=</span> <span class="p">(</span><span class="n">cppObject</span><span class="o">->*</span><span class="n">method</span><span class="p">)(</span> <span class="n">args</span><span class="p">...</span> <span class="p">);</span>
|
||
|
<span class="k">return</span> <span class="nf">objectLink</span><span class="p">(</span> <span class="n">pvalue</span> <span class="p">);</span>
|
||
|
<span class="p">}</span>
|
||
|
|
||
|
<span class="c1">// Flavor for "return void".</span>
|
||
|
<span class="k">template</span><span class="o"><</span> <span class="k">typename</span> <span class="n">TC</span><span class="p">,</span> <span class="k">typename</span> <span class="n">TR</span><span class="p">,</span> <span class="k">typename</span><span class="p">...</span> <span class="n">TArgs</span>
|
||
|
<span class="p">,</span> <span class="k">typename</span> <span class="n">std</span><span class="o">::</span><span class="n">enable_if</span><span class="o"><</span><span class="n">std</span><span class="o">::</span><span class="n">is_void</span><span class="o"><</span><span class="n">TR</span><span class="o">>::</span><span class="n">value</span><span class="p">,</span><span class="kt">bool</span><span class="o">>::</span><span class="n">type</span> <span class="o">=</span> <span class="nb">true</span> <span class="o">></span>
|
||
|
<span class="kr">inline</span> <span class="n">PyObject</span><span class="o">*</span> <span class="n">_callMethodReturn</span> <span class="p">(</span> <span class="n">TR</span><span class="p">(</span><span class="n">TC</span><span class="o">::*</span> <span class="n">method</span><span class="p">)(</span><span class="n">TArgs</span><span class="p">...),</span> <span class="n">TC</span><span class="o">*</span> <span class="n">cppObject</span><span class="p">,</span> <span class="n">TArgs</span><span class="p">...</span> <span class="n">args</span> <span class="p">)</span>
|
||
|
<span class="p">{</span>
|
||
|
<span class="p">(</span><span class="n">cppObject</span><span class="o">->*</span><span class="n">method</span><span class="p">)(</span> <span class="n">args</span><span class="p">...</span> <span class="p">);</span>
|
||
|
<span class="n">Py_RETURN_NONE</span><span class="p">;</span>
|
||
|
<span class="p">}</span>
|
||
|
|
||
|
<span class="c1">// Function without argument.</span>
|
||
|
<span class="k">template</span><span class="o"><</span> <span class="k">typename</span> <span class="n">TC</span><span class="p">,</span> <span class="k">typename</span> <span class="n">TR</span> <span class="o">></span>
|
||
|
<span class="kr">inline</span> <span class="n">PyObject</span><span class="o">*</span> <span class="n">_callMethod</span> <span class="p">(</span> <span class="n">TR</span><span class="p">(</span><span class="n">TC</span><span class="o">::*</span> <span class="n">method</span><span class="p">)(),</span> <span class="n">TC</span><span class="o">*</span> <span class="n">cppObject</span><span class="p">,</span> <span class="n">std</span><span class="o">::</span><span class="n">tuple</span><span class="o"><></span> <span class="p">)</span>
|
||
|
<span class="p">{</span> <span class="k">return</span> <span class="n">_callMethodReturn</span><span class="o"><</span><span class="n">TC</span><span class="p">,</span><span class="n">TR</span><span class="o">></span><span class="p">(</span> <span class="n">method</span><span class="p">,</span> <span class="n">cppObject</span> <span class="p">);</span> <span class="p">}</span>
|
||
|
|
||
|
<span class="c1">// Function with one argument.</span>
|
||
|
<span class="k">template</span><span class="o"><</span> <span class="k">typename</span> <span class="n">TC</span><span class="p">,</span> <span class="k">typename</span> <span class="n">TR</span><span class="p">,</span> <span class="k">typename</span> <span class="n">TA0</span> <span class="o">></span>
|
||
|
<span class="kr">inline</span> <span class="n">PyObject</span><span class="o">*</span> <span class="n">_callMethod</span> <span class="p">(</span> <span class="n">TR</span><span class="p">(</span><span class="n">TC</span><span class="o">::*</span> <span class="n">method</span><span class="p">)(</span><span class="n">TA0</span><span class="p">),</span> <span class="n">TC</span><span class="o">*</span> <span class="n">cppObject</span><span class="p">,</span> <span class="n">std</span><span class="o">::</span><span class="n">tuple</span><span class="o"><</span><span class="n">TA0</span><span class="o">></span> <span class="n">args</span> <span class="p">)</span>
|
||
|
<span class="p">{</span> <span class="k">return</span> <span class="n">_callMethodReturn</span><span class="p">(</span> <span class="n">method</span><span class="p">,</span> <span class="n">cppObject</span><span class="p">,</span> <span class="n">std</span><span class="o">::</span><span class="n">get</span><span class="o"><</span><span class="mi">0</span><span class="o">></span><span class="p">(</span><span class="n">args</span><span class="p">)</span> <span class="p">);</span> <span class="p">}</span>
|
||
|
|
||
|
<span class="c1">// Function with two arguments.</span>
|
||
|
<span class="k">template</span><span class="o"><</span> <span class="k">typename</span> <span class="n">TC</span><span class="p">,</span> <span class="k">typename</span> <span class="n">TR</span><span class="p">,</span> <span class="k">typename</span> <span class="n">TA0</span><span class="p">,</span> <span class="k">typename</span> <span class="n">TA1</span> <span class="o">></span>
|
||
|
<span class="n">PyObject</span><span class="o">*</span> <span class="n">_callMethod</span> <span class="p">(</span> <span class="n">TR</span><span class="p">(</span><span class="n">TC</span><span class="o">::*</span> <span class="n">method</span><span class="p">)(</span><span class="n">TA0</span><span class="p">,</span><span class="n">TA1</span><span class="p">),</span> <span class="n">TC</span><span class="o">*</span> <span class="n">cppObject</span><span class="p">,</span> <span class="n">std</span><span class="o">::</span><span class="n">tuple</span><span class="o"><</span><span class="n">TA0</span><span class="p">,</span><span class="n">TA1</span><span class="o">></span> <span class="n">args</span> <span class="p">)</span>
|
||
|
<span class="p">{</span> <span class="k">return</span> <span class="n">_callMethodReturn</span><span class="p">(</span> <span class="n">method</span><span class="p">,</span> <span class="n">cppObject</span><span class="p">,</span> <span class="n">std</span><span class="o">::</span><span class="n">get</span><span class="o"><</span><span class="mi">0</span><span class="o">></span><span class="p">(</span><span class="n">args</span><span class="p">),</span> <span class="n">std</span><span class="o">::</span><span class="n">get</span><span class="o"><</span><span class="mi">1</span><span class="o">></span><span class="p">(</span><span class="n">args</span><span class="p">)</span> <span class="p">);</span> <span class="p">}</span>
|
||
|
</pre></div>
|
||
|
<p>The complete work of translating the Python tuple into a <tt class="docutils literal"><span class="pre">std::tuple<></span></tt> and error
|
||
|
handling is done with a dedicated template class <tt class="docutils literal">PyMethodWrapper</tt> and it's <tt class="docutils literal">call()</tt>
|
||
|
method.</p>
|
||
|
<p>As a class template cannot guess the template parameters, we wrap them into a
|
||
|
function template which can perform the guess. The <tt class="docutils literal">callMethod<></tt> template function.</p>
|
||
|
<p>In the end, what the user can write is simply:</p>
|
||
|
<div class="highlight"><pre><span></span><span class="k">static</span> <span class="n">PyObject</span><span class="o">*</span> <span class="nf">PyParameter_addValue</span> <span class="p">(</span> <span class="n">PyVoidPointer</span><span class="o">*</span> <span class="n">self</span><span class="p">,</span> <span class="n">PyObject</span><span class="o">*</span> <span class="n">args</span> <span class="p">)</span>
|
||
|
<span class="p">{</span> <span class="k">return</span> <span class="n">callMethod</span><span class="p">(</span><span class="s">"Parameter.addValue"</span><span class="p">,</span><span class="o">&</span><span class="n">Parameter</span><span class="o">::</span><span class="n">addValue</span><span class="p">,</span><span class="n">self</span><span class="p">,</span><span class="n">args</span><span class="p">);</span> <span class="p">}</span>
|
||
|
|
||
|
<span class="n">PyMethodDef</span> <span class="n">PyParameter_Methods</span><span class="p">[]</span> <span class="o">=</span>
|
||
|
<span class="p">{</span> <span class="p">{</span> <span class="s">"isFile"</span> <span class="p">,</span> <span class="p">(</span><span class="n">PyCFunction</span><span class="p">)</span><span class="n">PyParameter_isFile</span> <span class="p">,</span> <span class="n">METH_NOARGS</span>
|
||
|
<span class="p">,</span> <span class="s">"Tells if this parameter (string) holds a file name."</span> <span class="p">}</span>
|
||
|
<span class="p">,</span> <span class="p">{</span> <span class="s">"addValue"</span><span class="p">,</span> <span class="p">(</span><span class="n">PyCFunction</span><span class="p">)</span><span class="n">PyParameter_addValue</span><span class="p">,</span> <span class="n">METH_VARARGS</span>
|
||
|
<span class="p">,</span> <span class="s">"Add a new value to parameter of enumerated type."</span> <span class="p">}</span>
|
||
|
<span class="c1">// ...</span>
|
||
|
<span class="p">,</span> <span class="p">{</span><span class="nb">NULL</span><span class="p">,</span> <span class="nb">NULL</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="nb">NULL</span><span class="p">}</span> <span class="cm">/* sentinel */</span>
|
||
|
<span class="p">};</span>
|
||
|
</pre></div>
|
||
|
</div>
|
||
|
<div class="section" id="case-of-c-overloaded-functions">
|
||
|
<h3><a class="toc-backref" href="#id11">2.6 Case of C++ overloaded functions</a></h3>
|
||
|
<p>This apply to both overloaded functions and functions with 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">-></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">-></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">-></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">"Parameter.setString"</span><span class="p">,</span><span class="o">&</span><span class="n">setString3</span><span class="p">,</span><span class="n">self</span><span class="p">,</span><span class="n">args</span><span class="p">);</span>
|
||
|
<span class="k">if</span> <span class="p">(</span><span class="n">not</span> <span class="n">rvalue</span><span class="p">)</span> <span class="n">rvalue</span> <span class="o">=</span> <span class="n">callMethod</span><span class="p">(</span><span class="s">"Parameter.setString"</span><span class="p">,</span><span class="o">&</span><span class="n">setString2</span><span class="p">,</span><span class="n">self</span><span class="p">,</span><span class="n">args</span><span class="p">);</span>
|
||
|
<span class="k">if</span> <span class="p">(</span><span class="n">not</span> <span class="n">rvalue</span><span class="p">)</span> <span class="n">rvalue</span> <span class="o">=</span> <span class="n">callMethod</span><span class="p">(</span><span class="s">"Parameter.setString"</span><span class="p">,</span><span class="o">&</span><span class="n">setString1</span><span class="p">,</span><span class="n">self</span><span class="p">,</span><span class="n">args</span><span class="p">);</span>
|
||
|
<span class="k">return</span> <span class="n">rvalue</span><span class="p">;</span>
|
||
|
<span class="p">}</span>
|
||
|
|
||
|
<span class="n">PyMethodDef</span> <span class="n">PyParameter_Methods</span><span class="p">[]</span> <span class="o">=</span>
|
||
|
<span class="p">{</span> <span class="p">{</span> <span class="s">"isFile"</span> <span class="p">,</span> <span class="p">(</span><span class="n">PyCFunction</span><span class="p">)</span><span class="n">PyParameter_isFile</span> <span class="p">,</span> <span class="n">METH_NOARGS</span>
|
||
|
<span class="p">,</span> <span class="s">"Tells if this parameter (string) holds a file name."</span> <span class="p">}</span>
|
||
|
<span class="p">,</span> <span class="p">{</span> <span class="s">"setString"</span><span class="p">,</span> <span class="p">(</span><span class="n">PyCFunction</span><span class="p">)</span><span class="n">PyParameter_setString</span><span class="p">,</span> <span class="n">METH_VARARGS</span>
|
||
|
<span class="p">,</span> <span class="s">"Set the parameter value as a string."</span> <span class="p">}</span>
|
||
|
<span class="c1">// ...</span>
|
||
|
<span class="p">,</span> <span class="p">{</span><span class="nb">NULL</span><span class="p">,</span> <span class="nb">NULL</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="nb">NULL</span><span class="p">}</span> <span class="cm">/* sentinel */</span>
|
||
|
<span class="p">};</span>
|
||
|
</pre></div>
|
||
|
</div>
|
||
|
<div class="section" id="wrapper-for-ordinary-functions">
|
||
|
<h3><a class="toc-backref" href="#id12">2.7 Wrapper for ordinary functions</a></h3>
|
||
|
<p>The same mechanic as for the object methods has been built for ordinary
|
||
|
functions. The top level wrapper beeing <tt class="docutils literal"><span class="pre">callFunction<>()</span></tt> ...</p>
|
||
|
<div class="highlight"><pre><span></span><span class="k">static</span> <span class="n">PyObject</span><span class="o">*</span> <span class="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">"hasParameter"</span><span class="p">,</span><span class="o">&</span><span class="n">hasParameter</span><span class="p">,</span><span class="n">args</span><span class="p">);</span> <span class="p">}</span>
|
||
|
|
||
|
<span class="k">static</span> <span class="n">PyMethodDef</span> <span class="n">PyCfg_Methods</span><span class="p">[]</span> <span class="o">=</span>
|
||
|
<span class="p">{</span> <span class="p">{</span> <span class="s">"hasParameter"</span><span class="p">,</span> <span class="p">(</span><span class="n">PyCFunction</span><span class="p">)</span><span class="n">PyCfg_hasParameter</span><span class="p">,</span> <span class="n">METH_VARARGS</span>
|
||
|
<span class="p">,</span> <span class="s">"Tells if a parameter exists already in the DB."</span> <span class="p">}</span>
|
||
|
<span class="c1">// ...</span>
|
||
|
<span class="p">,</span> <span class="p">{</span><span class="nb">NULL</span><span class="p">,</span> <span class="nb">NULL</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="nb">NULL</span><span class="p">}</span> <span class="cm">/* sentinel */</span>
|
||
|
<span class="p">};</span>
|
||
|
</pre></div>
|
||
|
</div>
|
||
|
<div class="section" id="object-post-create-hook">
|
||
|
<h3><a class="toc-backref" href="#id13">2.8 Object post-create hook</a></h3>
|
||
|
<p>By defining specialization of the <tt class="docutils literal"><span class="pre">pyTypePostModuleinit<>()</span></tt> function template,
|
||
|
you can add any post-treatment to a Python type object. Like adding sub-classes
|
||
|
or constants values.</p>
|
||
|
<p>In the following code, we add <tt class="docutils literal">Priority</tt> as a sub-object of <tt class="docutils literal">Parameter</tt> then
|
||
|
set some constant values in <tt class="docutils literal">Priority</tt>. This was we emulate the behavior of
|
||
|
the <tt class="docutils literal">Priority</tt> <tt class="docutils literal">enum</tt>.</p>
|
||
|
<div class="highlight"><pre><span></span><span class="k">template</span><span class="o"><></span>
|
||
|
<span class="kr">inline</span> <span class="kt">void</span> <span class="n">pyTypePostModuleInit</span><span class="o"><</span><span class="n">Cfg</span><span class="o">::</span><span class="n">Parameter</span><span class="o">></span> <span class="p">(</span> <span class="n">PyTypeObject</span><span class="o">*</span> <span class="n">typeObject</span> <span class="p">)</span>
|
||
|
<span class="p">{</span>
|
||
|
<span class="n">PyTypeManagerNonDBo</span><span class="o"><</span><span class="n">Cfg</span><span class="o">::</span><span class="n">Parameter</span><span class="o">::</span><span class="n">Priority</span><span class="o">>::</span><span class="n">create</span><span class="p">(</span> <span class="p">(</span><span class="n">PyObject</span><span class="o">*</span><span class="p">)</span><span class="n">typeObject</span>
|
||
|
<span class="p">,</span> <span class="n">Cfg</span><span class="o">::</span><span class="n">PyParameterPriority_Methods</span>
|
||
|
<span class="p">,</span> <span class="nb">NULL</span>
|
||
|
<span class="p">,</span> <span class="n">PyTypeManager</span><span class="o">::</span><span class="n">NoCppDelete</span> <span class="p">);</span>
|
||
|
<span class="p">}</span>
|
||
|
|
||
|
<span class="k">template</span><span class="o"><></span>
|
||
|
<span class="kr">inline</span> <span class="kt">void</span> <span class="n">pyTypePostModuleInit</span><span class="o"><</span><span class="n">Cfg</span><span class="o">::</span><span class="n">Parameter</span><span class="o">::</span><span class="n">Priority</span><span class="o">></span> <span class="p">(</span> <span class="n">PyTypeObject</span><span class="o">*</span> <span class="n">typeObject</span> <span class="p">)</span>
|
||
|
<span class="p">{</span>
|
||
|
<span class="c1">// Parameter::Priority enum.</span>
|
||
|
<span class="n">addConstant</span><span class="p">(</span> <span class="n">typeObject</span><span class="p">,</span> <span class="s">"UseDefault"</span> <span class="p">,</span> <span class="n">Cfg</span><span class="o">::</span><span class="n">Parameter</span><span class="o">::</span><span class="n">UseDefault</span> <span class="p">);</span>
|
||
|
<span class="n">addConstant</span><span class="p">(</span> <span class="n">typeObject</span><span class="p">,</span> <span class="s">"ApplicationBuiltin"</span><span class="p">,</span> <span class="n">Cfg</span><span class="o">::</span><span class="n">Parameter</span><span class="o">::</span><span class="n">ApplicationBuiltin</span> <span class="p">);</span>
|
||
|
<span class="n">addConstant</span><span class="p">(</span> <span class="n">typeObject</span><span class="p">,</span> <span class="s">"ConfigurationFile"</span> <span class="p">,</span> <span class="n">Cfg</span><span class="o">::</span><span class="n">Parameter</span><span class="o">::</span><span class="n">ConfigurationFile</span> <span class="p">);</span>
|
||
|
<span class="n">addConstant</span><span class="p">(</span> <span class="n">typeObject</span><span class="p">,</span> <span class="s">"UserFile"</span> <span class="p">,</span> <span class="n">Cfg</span><span class="o">::</span><span class="n">Parameter</span><span class="o">::</span><span class="n">UserFile</span> <span class="p">);</span>
|
||
|
<span class="n">addConstant</span><span class="p">(</span> <span class="n">typeObject</span><span class="p">,</span> <span class="s">"CommandLine"</span> <span class="p">,</span> <span class="n">Cfg</span><span class="o">::</span><span class="n">Parameter</span><span class="o">::</span><span class="n">CommandLine</span> <span class="p">);</span>
|
||
|
<span class="n">addConstant</span><span class="p">(</span> <span class="n">typeObject</span><span class="p">,</span> <span class="s">"Interactive"</span> <span class="p">,</span> <span class="n">Cfg</span><span class="o">::</span><span class="n">Parameter</span><span class="o">::</span><span class="n">Interactive</span> <span class="p">);</span>
|
||
|
<span class="p">}</span>
|
||
|
</pre></div>
|
||
|
</div>
|
||
|
</div>
|
||
|
|
||
|
</div>
|
||
|
<!-- /Content -->
|
||
|
|
||
|
<!-- Footer -->
|
||
|
<div class="footer gradient-2">
|
||
|
<div class="container footer-container ">
|
||
|
<div class="row">
|
||
|
<div class="col-xs-4 col-sm-3 col-md-3 col-lg-3">
|
||
|
<div class="footer-title">Social</div>
|
||
|
<ul class="list-unstyled">
|
||
|
</ul>
|
||
|
</div>
|
||
|
<div class="col-xs-4 col-sm-3 col-md-3 col-lg-2">
|
||
|
</div>
|
||
|
<div class="col-xs-4 col-sm-3 col-md-3 col-lg-3">
|
||
|
<div class="footer-title">Links</div>
|
||
|
<ul class="list-unstyled">
|
||
|
<li><a href="https://coriolis.lip6.fr/" target="_blank">Alliance/Coriolis</a></li>
|
||
|
<li><a href="https://www-soc.lip6.fr/" target="_blank">CIAN Team Website</a></li>
|
||
|
<li><a href="https://f-si.org" target="_blank">Free Silicon Foundation</a></li>
|
||
|
</ul>
|
||
|
</div>
|
||
|
<div class="col-xs-12 col-sm-3 col-md-3 col-lg-4">
|
||
|
<p class="pull-right text-right">
|
||
|
<small><em>Proudly powered by <a href="http://docs.getpelican.com/" target="_blank">pelican</a></em></small><br/>
|
||
|
<small><em><span class="sc">NEST</span> theme by <a href="https://github.com/molivier" target="_blank">molivier</a></em></small><br/>
|
||
|
<small>Copyright © 2020-2020 Sorbonne Universite</small>
|
||
|
</p>
|
||
|
</div>
|
||
|
</div>
|
||
|
</div>
|
||
|
</div>
|
||
|
<!-- /Footer -->
|
||
|
</body>
|
||
|
</html>
|