mirror of https://github.com/McSinyx/palace
383 lines
22 KiB
HTML
383 lines
22 KiB
HTML
|
||
|
||
<!DOCTYPE html>
|
||
<html class="writer-html5" lang="en" >
|
||
<head>
|
||
<meta charset="utf-8">
|
||
|
||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||
|
||
<title>Design Principles — palace 0.2.1 documentation</title>
|
||
|
||
|
||
|
||
<link rel="stylesheet" href="_static/css/theme.css" type="text/css" />
|
||
<link rel="stylesheet" href="_static/pygments.css" type="text/css" />
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
<!--[if lt IE 9]>
|
||
<script src="_static/js/html5shiv.min.js"></script>
|
||
<![endif]-->
|
||
|
||
|
||
<script type="text/javascript" id="documentation_options" data-url_root="./" src="_static/documentation_options.js"></script>
|
||
<script src="_static/jquery.js"></script>
|
||
<script src="_static/underscore.js"></script>
|
||
<script src="_static/doctools.js"></script>
|
||
<script src="_static/language_data.js"></script>
|
||
|
||
<script type="text/javascript" src="_static/js/theme.js"></script>
|
||
|
||
|
||
<link rel="index" title="Index" href="genindex.html" />
|
||
<link rel="search" title="Search" href="search.html" />
|
||
<link rel="next" title="Getting Involved" href="contributing.html" />
|
||
<link rel="prev" title="File I/O Interface" href="reference/file-io.html" />
|
||
</head>
|
||
|
||
<body class="wy-body-for-nav">
|
||
|
||
|
||
<div class="wy-grid-for-nav">
|
||
|
||
<nav data-toggle="wy-nav-shift" class="wy-nav-side">
|
||
<div class="wy-side-scroll">
|
||
<div class="wy-side-nav-search" >
|
||
|
||
|
||
|
||
<a href="index.html" class="icon icon-home" alt="Documentation Home"> palace
|
||
|
||
|
||
|
||
</a>
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
<div role="search">
|
||
<form id="rtd-search-form" class="wy-form" action="search.html" method="get">
|
||
<input type="text" name="q" placeholder="Search docs" />
|
||
<input type="hidden" name="check_keywords" value="yes" />
|
||
<input type="hidden" name="area" value="default" />
|
||
</form>
|
||
</div>
|
||
|
||
|
||
</div>
|
||
|
||
|
||
<div class="wy-menu wy-menu-vertical" data-spy="affix" role="navigation" aria-label="main navigation">
|
||
|
||
|
||
|
||
|
||
|
||
|
||
<p class="caption"><span class="caption-text">Table of Contents</span></p>
|
||
<ul class="current">
|
||
<li class="toctree-l1"><a class="reference internal" href="installation.html">Installation</a></li>
|
||
<li class="toctree-l1"><a class="reference internal" href="tutorial/index.html">Tutorial</a></li>
|
||
<li class="toctree-l1"><a class="reference internal" href="reference/index.html">Reference</a></li>
|
||
<li class="toctree-l1 current"><a class="current reference internal" href="#">Design Principles</a><ul>
|
||
<li class="toctree-l2"><a class="reference internal" href="#the-impl-idiom">The Impl Idiom</a></li>
|
||
<li class="toctree-l2"><a class="reference internal" href="#the-modern-python">The Modern Python</a><ul>
|
||
<li class="toctree-l3"><a class="reference internal" href="#property-attributes">Property Attributes</a></li>
|
||
<li class="toctree-l3"><a class="reference internal" href="#context-managers">Context Managers</a></li>
|
||
</ul>
|
||
</li>
|
||
<li class="toctree-l2"><a class="reference internal" href="#the-double-reference">The Double Reference</a></li>
|
||
</ul>
|
||
</li>
|
||
<li class="toctree-l1"><a class="reference internal" href="contributing.html">Getting Involved</a></li>
|
||
<li class="toctree-l1"><a class="reference internal" href="copying.html">Copying</a></li>
|
||
</ul>
|
||
<p class="caption"><span class="caption-text">Quick Navigation</span></p>
|
||
<ul>
|
||
<li class="toctree-l1"><a class="reference external" href="https://pypi.org/project/palace/">Python Package Index</a></li>
|
||
<li class="toctree-l1"><a class="reference external" href="https://travis-ci.com/github/McSinyx/palace">Travis CI Build</a></li>
|
||
<li class="toctree-l1"><a class="reference external" href="https://ci.appveyor.com/project/McSinyx/palace">AppVeyor Build</a></li>
|
||
<li class="toctree-l1"><a class="reference external" href="https://github.com/McSinyx/palace">GitHub Repository</a></li>
|
||
<li class="toctree-l1"><a class="reference external" href="https://matrix.to/#/#palace-dev:matrix.org">Matrix Chat Room</a></li>
|
||
</ul>
|
||
|
||
|
||
|
||
</div>
|
||
|
||
</div>
|
||
</nav>
|
||
|
||
<section data-toggle="wy-nav-shift" class="wy-nav-content-wrap">
|
||
|
||
|
||
<nav class="wy-nav-top" aria-label="top navigation">
|
||
|
||
<i data-toggle="wy-nav-top" class="fa fa-bars"></i>
|
||
<a href="index.html">palace</a>
|
||
|
||
</nav>
|
||
|
||
|
||
<div class="wy-nav-content">
|
||
|
||
<div class="rst-content">
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
<div role="navigation" aria-label="breadcrumbs navigation">
|
||
|
||
<ul class="wy-breadcrumbs">
|
||
|
||
<li><a href="index.html" class="icon icon-home"></a> »</li>
|
||
|
||
<li>Design Principles</li>
|
||
|
||
|
||
<li class="wy-breadcrumbs-aside">
|
||
|
||
|
||
<a href="_sources/design.rst.txt" rel="nofollow"> View page source</a>
|
||
|
||
|
||
</li>
|
||
|
||
</ul>
|
||
|
||
|
||
<hr/>
|
||
</div>
|
||
<div role="main" class="document" itemscope="itemscope" itemtype="http://schema.org/Article">
|
||
<div itemprop="articleBody">
|
||
|
||
<div class="section" id="design-principles">
|
||
<h1>Design Principles<a class="headerlink" href="#design-principles" title="Permalink to this headline">¶</a></h1>
|
||
<p>In this section, we will discuss a few design principles in order to write
|
||
a safe, efficient, easy-to-use and extendable 3D audio library for Python,
|
||
by wrapping existing functionalities from the C++ API <a class="reference external" href="https://github.com/kcat/alure">alure</a>.</p>
|
||
<p>This part of the documentation assumes its reader are at least familiar with
|
||
Cython, Python and C++11.</p>
|
||
<div class="section" id="the-impl-idiom">
|
||
<span id="impl-idiom"></span><h2>The Impl Idiom<a class="headerlink" href="#the-impl-idiom" title="Permalink to this headline">¶</a></h2>
|
||
<p><em>Not to be confused with</em> <a class="reference external" href="https://wiki.c2.com/?PimplIdiom">the pimpl idiom</a>.</p>
|
||
<p>For memory-safety, whenever possible, we rely on Cython for allocation and
|
||
deallocation of C++ objects. To do this, the nullary constructor needs to be
|
||
(re-)declared in Cython, e.g.</p>
|
||
<div class="highlight-cython notranslate"><div class="highlight"><pre><span></span><span class="k">cdef</span> <span class="kr">extern</span> <span class="k">from</span> <span class="s">'foobar.h'</span> <span class="n">namespace</span> <span class="s">'foobar'</span><span class="p">:</span>
|
||
<span class="k">cdef</span> <span class="kt">cppclass</span> <span class="nf">Foo</span><span class="p">:</span>
|
||
<span class="n">Foo</span><span class="p">()</span>
|
||
<span class="nb">float</span> <span class="n">meth</span><span class="p">(</span><span class="n">size_t</span> <span class="n">crack</span><span class="p">)</span> <span class="k">except</span> <span class="o">+</span>
|
||
<span class="o">...</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>The Cython extension type can then be declared as follows</p>
|
||
<div class="highlight-cython notranslate"><div class="highlight"><pre><span></span><span class="k">cdef</span> <span class="k">class</span> <span class="nf">Bar</span><span class="p">:</span>
|
||
<span class="k">cdef</span> <span class="kt">Foo</span> <span class="nf">impl</span>
|
||
|
||
<span class="k">def</span> <span class="nf">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="o">*</span><span class="n">args</span><span class="p">,</span> <span class="o">**</span><span class="n">kwargs</span><span class="p">):</span>
|
||
<span class="bp">self</span><span class="o">.</span><span class="n">impl</span> <span class="o">=</span> <span class="o">...</span>
|
||
|
||
<span class="nd">@staticmethod</span>
|
||
<span class="k">def</span> <span class="nf">from_baz</span><span class="p">(</span><span class="n">baz</span><span class="p">:</span> <span class="n">Baz</span><span class="p">)</span> <span class="o">-></span> <span class="n">Bar</span><span class="p">:</span>
|
||
<span class="n">bar</span> <span class="o">=</span> <span class="n">Bar</span><span class="o">.</span><span class="n">__new__</span><span class="p">(</span><span class="n">Bar</span><span class="p">)</span>
|
||
<span class="n">bar</span><span class="o">.</span><span class="n">impl</span> <span class="o">=</span> <span class="o">...</span>
|
||
<span class="k">return</span> <span class="n">bar</span>
|
||
|
||
<span class="k">def</span> <span class="nf">meth</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">crack</span><span class="p">:</span> <span class="nb">int</span><span class="p">)</span> <span class="o">-></span> <span class="nb">float</span><span class="p">:</span>
|
||
<span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">impl</span><span class="o">.</span><span class="n">meth</span><span class="p">(</span><span class="n">crack</span><span class="p">)</span>
|
||
</pre></div>
|
||
</div>
|
||
</div>
|
||
<div class="section" id="the-modern-python">
|
||
<h2>The Modern Python<a class="headerlink" href="#the-modern-python" title="Permalink to this headline">¶</a></h2>
|
||
<p>One of the goal of palace is to create a Pythonic, i.e. intuitive and concise,
|
||
interface. To achieve this, we try to make use of some modern Python features,
|
||
which not only allow users to adopt palace with ease, but also make their
|
||
programs more readable and less error-prone.</p>
|
||
<div class="section" id="property-attributes">
|
||
<span id="getter-setter"></span><h3>Property Attributes<a class="headerlink" href="#property-attributes" title="Permalink to this headline">¶</a></h3>
|
||
<p>A large proportion of alure API are getters/setter methods. In Python,
|
||
it is a good practice to use <a class="reference external" href="https://docs.python.org/3/library/functions.html#property">property</a> to abstract these calls, and thus make
|
||
the interface more natural with attribute-like referencing and assignments.</p>
|
||
<p>Due to implementation details, Cython has to hijack the <code class="docutils literal notranslate"><span class="pre">@property</span></code> decorator
|
||
to make it work for read-write properties. Unfortunately, the Cython-generated
|
||
descriptors do not play very well with other builtin decorators, thus in some
|
||
cases, it is recommended to alias the call to <code class="docutils literal notranslate"><span class="pre">property</span></code> as follows</p>
|
||
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span><span class="n">getter</span> <span class="o">=</span> <span class="nb">property</span>
|
||
<span class="n">setter</span> <span class="o">=</span> <span class="k">lambda</span> <span class="n">fset</span><span class="p">:</span> <span class="nb">property</span><span class="p">(</span><span class="n">fset</span><span class="o">=</span><span class="n">fset</span><span class="p">,</span> <span class="n">doc</span><span class="o">=</span><span class="n">fset</span><span class="o">.</span><span class="vm">__doc__</span><span class="p">)</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>Then <code class="docutils literal notranslate"><span class="pre">@getter</span></code> and <code class="docutils literal notranslate"><span class="pre">@setter</span></code> can be used to decorate read-only and
|
||
write-only properties, respectively, without any trouble even if other
|
||
decorators are used for the same extension type method.</p>
|
||
</div>
|
||
<div class="section" id="context-managers">
|
||
<h3>Context Managers<a class="headerlink" href="#context-managers" title="Permalink to this headline">¶</a></h3>
|
||
<p>The alure API defines many objects that need manual tear-down in
|
||
a particular order. Instead of trying to be clever and perform automatic
|
||
clean-ups at garbage collection, we should put the user in control.
|
||
To quote <em>The Zen of Python</em>,</p>
|
||
<blockquote>
|
||
<div><div class="line-block">
|
||
<div class="line">If the implementation is hard to explain, it’s a bad idea.</div>
|
||
<div class="line">If the implementation is easy to explain, it may be a good idea.</div>
|
||
</div>
|
||
</div></blockquote>
|
||
<p>With that being said, it does not mean we do not provide any level of
|
||
abstraction. A simplified case in point would be</p>
|
||
<div class="highlight-cython notranslate"><div class="highlight"><pre><span></span><span class="k">cdef</span> <span class="k">class</span> <span class="nf">Device</span><span class="p">:</span>
|
||
<span class="k">cdef</span> <span class="kt">alure</span>.<span class="kt">Device</span> <span class="nf">impl</span>
|
||
|
||
<span class="k">def</span> <span class="nf">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">name</span><span class="p">:</span> <span class="nb">str</span> <span class="o">=</span> <span class="s">''</span><span class="p">)</span> <span class="o">-></span> <span class="bp">None</span><span class="p">:</span>
|
||
<span class="bp">self</span><span class="o">.</span><span class="n">impl</span> <span class="o">=</span> <span class="n">devmgr</span><span class="o">.</span><span class="n">open_playback</span><span class="p">(</span><span class="n">name</span><span class="p">)</span>
|
||
|
||
<span class="k">def</span> <span class="nf">__enter__</span><span class="p">(</span><span class="bp">self</span><span class="p">)</span> <span class="o">-></span> <span class="n">Device</span><span class="p">:</span>
|
||
<span class="k">return</span> <span class="bp">self</span>
|
||
|
||
<span class="k">def</span> <span class="nf">__exit__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="o">*</span><span class="n">exc</span><span class="p">)</span> <span class="o">-></span> <span class="n">Optional</span><span class="p">[</span><span class="nb">bool</span><span class="p">]:</span>
|
||
<span class="bp">self</span><span class="o">.</span><span class="n">close</span><span class="p">()</span>
|
||
|
||
<span class="k">def</span> <span class="nf">close</span><span class="p">(</span><span class="bp">self</span><span class="p">)</span> <span class="o">-></span> <span class="bp">None</span><span class="p">:</span>
|
||
<span class="bp">self</span><span class="o">.</span><span class="n">impl</span><span class="o">.</span><span class="n">close</span><span class="p">()</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>Now if the <code class="docutils literal notranslate"><span class="pre">with</span></code> statement is used, it will make sure the device
|
||
will be closed, regardless of whatever may happen within the inner block</p>
|
||
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span><span class="k">with</span> <span class="n">Device</span><span class="p">()</span> <span class="k">as</span> <span class="n">dev</span><span class="p">:</span>
|
||
<span class="o">...</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>as it is equivalent to</p>
|
||
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span><span class="n">dev</span> <span class="o">=</span> <span class="n">Device</span><span class="p">()</span>
|
||
<span class="k">try</span><span class="p">:</span>
|
||
<span class="o">...</span>
|
||
<span class="k">finally</span><span class="p">:</span>
|
||
<span class="n">dev</span><span class="o">.</span><span class="n">close</span><span class="p">()</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>Other than closure/destruction of objects, typical uses of <a class="reference external" href="https://docs.python.org/3/reference/datamodel.html#context-managers">context managers</a>
|
||
also include saving and restoring various kinds of global state (as seen in
|
||
<a class="reference internal" href="reference/context.html#palace.Context" title="palace.Context"><code class="xref py py-class docutils literal notranslate"><span class="pre">Context</span></code></a>), locking and unlocking resources, etc.</p>
|
||
</div>
|
||
</div>
|
||
<div class="section" id="the-double-reference">
|
||
<h2>The Double Reference<a class="headerlink" href="#the-double-reference" title="Permalink to this headline">¶</a></h2>
|
||
<p>While wrapping C++ interfaces, <a class="reference internal" href="#impl-idiom"><span class="std std-ref">the impl idiom</span></a> might not
|
||
be adequate, since the derived Python methods need to be callable from C++.
|
||
Luckily, Cython can handle Python objects within C++ classes just fine,
|
||
although we’ll need to handle the reference count ourselves, e.g.</p>
|
||
<div class="highlight-cython notranslate"><div class="highlight"><pre><span></span><span class="k">cdef</span> <span class="kt">cppclass</span> <span class="nf">CppDecoder</span><span class="p">(</span><span class="n">alure</span><span class="o">.</span><span class="n">BaseDecoder</span><span class="p">):</span>
|
||
<span class="n">Decoder</span> <span class="n">pyo</span>
|
||
|
||
<span class="n">__init__</span><span class="p">(</span><span class="n">Decoder</span> <span class="n">decoder</span><span class="p">):</span>
|
||
<span class="n">this</span><span class="o">.</span><span class="n">pyo</span> <span class="o">=</span> <span class="n">decoder</span>
|
||
<span class="n">Py_INCREF</span><span class="p">(</span><span class="n">pyo</span><span class="p">)</span>
|
||
|
||
<span class="n">__dealloc__</span><span class="p">():</span>
|
||
<span class="n">Py_DECREF</span><span class="p">(</span><span class="n">pyo</span><span class="p">)</span>
|
||
|
||
<span class="nb">bool</span> <span class="n">seek</span><span class="p">(</span><span class="n">uint64_t</span> <span class="n">pos</span><span class="p">):</span>
|
||
<span class="k">return</span> <span class="n">pyo</span><span class="o">.</span><span class="n">seek</span><span class="p">(</span><span class="n">pos</span><span class="p">)</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>With this being done, we can now write the wrapper as simply as</p>
|
||
<div class="highlight-cython notranslate"><div class="highlight"><pre><span></span><span class="k">cdef</span> <span class="k">class</span> <span class="nf">BaseDecoder</span><span class="p">:</span>
|
||
<span class="k">cdef</span> <span class="kt">shared_ptr</span>[<span class="kt">alure</span>.<span class="kt">Decoder</span>] <span class="nf">pimpl</span>
|
||
|
||
<span class="k">def</span> <span class="nf">__cinit__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="o">*</span><span class="n">args</span><span class="p">,</span> <span class="o">**</span><span class="n">kwargs</span><span class="p">)</span> <span class="o">-></span> <span class="bp">None</span><span class="p">:</span>
|
||
<span class="bp">self</span><span class="o">.</span><span class="n">pimpl</span> <span class="o">=</span> <span class="n">shared_ptr</span><span class="p">[</span><span class="n">alure</span><span class="o">.</span><span class="n">Decoder</span><span class="p">](</span><span class="n">new</span> <span class="n">CppDecoder</span><span class="p">(</span><span class="bp">self</span><span class="p">))</span>
|
||
|
||
<span class="k">def</span> <span class="nf">seek</span><span class="p">(</span><span class="n">pos</span><span class="p">:</span> <span class="nb">int</span><span class="p">)</span> <span class="o">-></span> <span class="nb">bool</span><span class="p">:</span>
|
||
<span class="o">...</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>Because <code class="docutils literal notranslate"><span class="pre">__cinit__</span></code> is called by <code class="docutils literal notranslate"><span class="pre">__new__</span></code>, any Python class derived
|
||
from <code class="docutils literal notranslate"><span class="pre">BaseDecoder</span></code> will be exposed to C++ as an attribute of <code class="docutils literal notranslate"><span class="pre">CppDecoder</span></code>.
|
||
Effectively, this means the users can have the alure API calling their
|
||
inherited Python object as naturally as if palace is implemented in pure Python.</p>
|
||
<p>In practice, <a class="reference internal" href="reference/decoder.html#palace.BaseDecoder" title="palace.BaseDecoder"><code class="xref py py-class docutils literal notranslate"><span class="pre">BaseDecoder</span></code></a> will also need to take into account
|
||
other guarding mechanisms like <code class="xref py py-class docutils literal notranslate"><span class="pre">abc.ABC</span></code>. Due to Cython limitations,
|
||
implementation as a pure Python class and <a class="reference internal" href="#getter-setter"><span class="std std-ref">aliasing</span></a> of
|
||
<code class="docutils literal notranslate"><span class="pre">@getter</span></code>/<code class="docutils literal notranslate"><span class="pre">@setter</span></code> should be considered.</p>
|
||
</div>
|
||
</div>
|
||
|
||
|
||
</div>
|
||
|
||
</div>
|
||
<footer>
|
||
|
||
<div class="rst-footer-buttons" role="navigation" aria-label="footer navigation">
|
||
|
||
<a href="contributing.html" class="btn btn-neutral float-right" title="Getting Involved" accesskey="n" rel="next">Next <span class="fa fa-arrow-circle-right"></span></a>
|
||
|
||
|
||
<a href="reference/file-io.html" class="btn btn-neutral float-left" title="File I/O Interface" accesskey="p" rel="prev"><span class="fa fa-arrow-circle-left"></span> Previous</a>
|
||
|
||
</div>
|
||
|
||
|
||
<hr/>
|
||
|
||
<div role="contentinfo">
|
||
<p>
|
||
|
||
© Copyright 2019, 2020 Nguyễn Gia Phong et al
|
||
|
||
</p>
|
||
</div>
|
||
|
||
|
||
|
||
Built with <a href="http://sphinx-doc.org/">Sphinx</a> using a
|
||
|
||
<a href="https://github.com/rtfd/sphinx_rtd_theme">theme</a>
|
||
|
||
provided by <a href="https://readthedocs.org">Read the Docs</a>.
|
||
|
||
</footer>
|
||
|
||
</div>
|
||
</div>
|
||
|
||
</section>
|
||
|
||
</div>
|
||
|
||
|
||
<script type="text/javascript">
|
||
jQuery(function () {
|
||
SphinxRtdTheme.Navigation.enable(true);
|
||
});
|
||
</script>
|
||
|
||
|
||
|
||
|
||
|
||
|
||
</body>
|
||
</html> |