<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet type="text/xml" href="/cjs/screen.xsl" media="screen"?>
<lecture>

<meta>
  <maintitle>Python</maintitle>
  <author>Jiří Znamenáček</author>
  <title>„Foreign Functions Interface“</title>
  <date>2013-05-01</date>
  <link><!--a href="http://vyuka.ookami.cz" rel="external">http://vyuka.ookami.cz</a--></link>
</meta>
<!--
  „“–…
  ←→ ↑↓ ↔↕
  ↵ aneb &#x21B5; aneb \r aneb CR aneb CarriageReturn
-->


<slide title="Úvod">

    <p>
        Python umožňuje poměrně snadno volat funkce ve sdílených knihovnách. Principiálně můžete buď načíst najednou prakticky všechny systémové knihovny nebo libovolnou konkrétní vybranou, samozřejmě pak i nesystémovou. Toho se často užívá pro přímý přístup k vybraným funkcím systému z Python'u nebo například pro přepis časově kritických operací v nějakém rychlejším jazyce.
    </p>
    <p>
        Python pro tyto účely poskytuje modul <a href="ctypes_basics.xml"><code>ctypes</code></a>, který umožňuje volat externí sdílené knihovny vyhovující standardu jazyka C. Pro jiné jazyky je třeba doplnit odpovídající céčkovský „wrapper“.
    </p>

</slide>
<slide title="Knihovny (nejen) v jazyce C/C++">

    <p>
        S externími knihovnami napsanými v jazyce C, respektive C++, je práce nejsnažší, protože Python pro ně poskytuje přímou podporu pomocí modulu <a href="ctypes_basics.xml"><code>ctypes</code></a>. Ten poskytuje datové typy kompatibilní s Céčkem a umožňuje pomocí klasické tečkové notace volat dostupné funkce na externí sdílené knihovně.
    </p>
    <p>
        V praxi tedy stačí napsat požadované části kódu v Céčku, příslušným způsobem je přeložit do podoby sdílené knihovny (<em>shared library</em>), vhodným způsobem tuto knihovnu v rámci Python'u načíst a s takto získaným objektem pracovat, jako kdyby to byl obyčejný pythoní objekt.
    </p>
    <note>
        Tedy pouze <em>téměř</em>, protože přímo operovat můžeme pouze s typy <code>None</code>, <code>int</code>, <code>bytes</code>, <code>bytearray</code> a <code>string</code> (a u něj to není ve trojkovém Python'u vůbec přímočaré); cokoliv jiného je třeba nejdříve „přetypovat“ vhodným typem z kolekce modulu <em>ctypes</em>.
    </note>
    <!-- None, integers, bytes objects and (unicode) strings are the only native Python objects that can directly be used as parameters in these function calls. -->

</slide>
<slide title="Příklad">

    <p>
        Zkusme pro ukázku přesunout do Céčka vykonání dlouhé smyčky <em>while</em>. Zdrojový kód..
    </p>
    <example lang="python" src="_files/c_smycka/mylib.c" />
    <p>
        ..přeložíme do podoby sdílené knihovny např. pomocí příkazu:
    </p>
    <example lang="cmd">
        gcc -O -shared mylib.c -o mylib.dll
    </example>
    <p>
        Se získaným souborem <em>mylib.dll</em> pak z Python'u můžeme pracovat např. takto:
    </p>
    <example layout="vertical">
        <program src="_files/c_smycka/test.py" lang="python"/>
        <out src="_files/c_smycka/test.out" lang="text"/>
    </example>

</slide>
<slide title="Poznámky">

    <ul>
        <li>
            Pod <strong>Windows</strong> je možné odkázat na sdílenou knihovnu <em>relativně vůči umístění skriptu</em>, pod <strong>Linuxem</strong> je však třeba <em>absolutní cesta</em>. Windows naštěstí absolutní cestu zvládnou též, takže principielně postačí nahradit jméno modulu třeba tímto kouskem kódu:
            <example lang="python">
                os.path.join( os.getcwd(), 'mylib.slib' )
            </example>
            <note>A samozřejmě vybrat knihovnu podle aktuální platformy.</note>
        </li>
        <li>
            Podle jazyka, překladače a platformy může – ale také nemusí – býti nutné (nebo jindy přinejmenším vhodné) označit, které funkce budou exportovány vně sdílené knihovny a které jsou určeny pouze pro její vnitřní potřebu.
            <note>
                Jelikož závisí nejen na jazyce a překladači, ale též i platformě, je v tuto chvíli zpravidla čas uplatnit vhodné makro (pokud je jazyk podporuje), pomocí něhož se daný export pro více funkcí zapíše podstatně snáze.
            </note>
        </li>
        <li>
            Je vřele doporučováno nepokoušet se přenášet a zachytávat výjimky mezi jazyky na obou stranách výše uvedeným způsobem vyrobeného spojení.
        </li>
        <li>
            Názvy exportovaných funkcí mohou být zamanglovány takovým způsobem, že nejde o validní pythoní identifikátory a nemůžete tudíž použít klasickou tečkovou notaci. Vždycky je tady ale možnost použít funkci <code>getattr(KNIHOVNA, "FUNKCE")</code>.
        </li>
    </ul>

</slide>
<slide title="Srovnání rychlosti">

    <p>
        Vyrobme pomocí..
    </p>
    <example lang="cmd" src="_files/c_smycka/compile.cmd" />
    <p>
        ..dvě různě optimalizované verze této smyčky a porovnejme je jak mezi sebou, tak se stejnou smyčkou v čistém Python'u:
    </p>
    <example layout="vertical">
        <program src="_files/c_smycka/timing.py" lang="python"/>
        <out src="_files/c_smycka/timing.out" lang="text"/>
    </example>
    <p>
        Vidíme, že – nepřekvapivě – obě smyčky v Céčku jsou enormně rychlejší než smyčka v Python'u. Pythoní kód je v tomto případě pomalejší než základním způsobem optimalizované Céčko v řádu stonásobků, ale „drsnější“ optimalizace Céčka tento už tak obrovský rozdíl zvýší na doslova propastný v řádu ještě dalších tisícinásobků.
    </p>

</slide>
<slide title="Knihovny v jiných jazycích">

    <p class="enumerate">
        Některé jazyky umí vyrobit sdílené knihovny s céčkovským rozhraním (<em>interface</em>), ale jejich obsluha z Python'u často nemusí být stejně přímočará jako u Céčkovských knihoven.
    </p>
    <p>
        Mezi takovéto jazyky patří například <em>Ada</em>, kdy je třeba importované moduly pro použití v pythoním skriptu inicializovat pomocí <code>ada_moduleinit()</code> a dalších prostředků.
    </p>
    
    <p class="enumerate">
        Jiné jazyky sice poskytují sdílené knihovny, ale jejich rozhraní buď nekopíruje kompletně rozhraní knihoven jazyka C nebo vyžaduje pro práci s nimi některé nestandardní prostředky.
    </p>
    <p>
        Mezi takové jazyky patří například <em>Haskell</em> nebo <em>D</em>, které vyžadují slinkování vlastního zdrojového kódu knihovny s „wrapperem“, který doplní požadované rozhraní.
    </p>

</slide>


</lecture>
