Obsah
1. Propojení Pythonu s nativními knihovnami s využitím balíčku cffi
3. Funkce psaná v C, která sečte své dva celočíselné parametry
4. Překlad funkce a uložení výsledku překladu do dynamicky linkované knihovny
5. Načtení dynamicky linkované knihovny v Pythonním skriptu a zavolání nativní funkce přes ctypes
6. Srážka staticky typovaného světa se světem typovaným dynamicky
7. Načtení dynamicky linkované knihovny a zavolání funkce z této knihovny s využitím cffi
8. Kontrola datových typů předávaných hodnot knihovnou cffi
9. Funkce naprogramovaná v céčku, která vytiskne předaný řetězec
10. Céčkovské řetězce jsou odlišné od Pythonovských řetězců!
11. Předání řetězců do céčkovské funkce realizované přes cffi
12. Programovací jazyk Python a céčkovské ukazatele
13. Funkce psaná v C, která prohodí hodnoty parametrů předaných referencí
14. Inicializace ukazatelů na straně Pythonu, předání ukazatelů do volané céčkové funkce
15. Kontrola, zda se s ukazatelem nezachází jako s polem
16. Práce s poli, konverze mezi polem a datovými typy Pythonu
17. Funkce psaná v C, která vyplní pole zvolenou hodnotou
18. Volání funkce pro výplň pole z Pythonu, zobrazení výsledného pole
19. Repositář s demonstračními příklady
1. Propojení Pythonu s nativními knihovnami s využitím balíčku cffi
Programovací jazyk Python je dnes pravděpodobně nejrozšířenějším [1][2] programovacím jazykem, k čemuž přispěl i fakt, že pro něj existuje obrovské množství balíčků řešících problémy z různých oborů (od obecného zpracování dat přes tvorbu webů a grafického uživatelského rozhraní až po ML a AI). A mnoho těchto balíčků není ve skutečnosti nic jiného, než „pouhé“ sofistikované lepidlo mezi Pythonem a nativními (dynamicky linkovanými) knihovnami. Jak je však kooperace mezi Pythonem na straně jedné a nativním kódem na straně druhé realizována po technologické stránce?
Programový kód napsaný typicky v C, C++ (popř. v Rustu, Go či Fortranu atd.) je nejprve přeložen do nativní dynamické knihovny (tedy konkrétně do souboru s koncovkou „.so“ na Linuxu a „.dll“ ve Windows). Aplikace psaná v Pythonu tuto dynamickou knihovnu načte a přes balíček ctypes umožní volání funkcí naprogramovaných v C/C++ atd. Zpočátku se může zdát, že se jedná o bezproblémové řešení, ovšem na cestě k výslednému produktu je nutné zdolat poměrně mnoho překážek. Některé jsou relativně snadné (například ctypes lze nahradit za cffi, pokud to vývojáři více vyhovuje – což si ukážeme dnes i příště), další již komplikovanější. Tyto problémy spočívají v tom, že se střetávají dva rozdílné typové systémy. Navíc obě technologie předpokládají, že jedna z komunikujících stran je psaná v céčku – a to znamená, že se mezi dva programovací jazyky s automatickou správou paměti vložilo rozhraní předpokládající manuální správu paměti se všemi z toho plynoucími důsledky (to je případ komunikace mezi Pythonem a Go).
Dnes se zaměříme na balíček cffi, který v Pythonu umožňuje volat nativní funkce a předávat jim „nativní“ datové typy a datové struktury. cffi není, na rozdíl od ctypes, součástí standardní knihovny Pythonu, ovšem v několika ohledech se jedná o lepší řešení, které dobře „škáluje“ společně s rostoucím množstvím nativního kódu, který je nutné z Pythonu volat.
2. Instalace knihovny cffi
Samotná instalace knihovny cffi je velmi snadná, protože poslední stabilní verze této knihovny je dostupná na PyPI a tato knihovna má jen jedinou závislosti – knihovnu pycparser. Instalaci pro aktuálně přihlášeného uživatele provedeme příkazem:
$ pip3 install --user cffi Collecting cffi Using cached cffi-1.15.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (442 kB) Collecting pycparser Using cached pycparser-2.21-py2.py3-none-any.whl (118 kB) Installing collected packages: pycparser, cffi Successfully installed cffi-1.15.1 pycparser-2.21
Poněkud naivní kontrola instalace může vypadat následovně:
$ python3 Python 3.8.10 (default, Mar 13 2023, 10:26:41) [GCC 9.4.0] on linux Type "help", "copyright", "credits" or "license" for more information. >>> import cffi >>> help(cffi]
3. Funkce psaná v C, která sečte své dva celočíselné parametry
V úvodních kapitolách praktické části dnešního článku si ukážeme, jak lze z Pythonu zavolat velmi jednoduchou funkci, která je naprogramována v jazyku C. Tato funkce akceptuje dva parametry typu int, sečte je a následně vrátí výsledek součtu. Deklarace takové funkce je skutečně triviální:
extern int add(int x, int y) { return x+y; }
4. Překlad funkce a uložení výsledku překladu do dynamicky linkované knihovny
Soubor adder.c, v němž je výše uvedená funkce uložena, nyní přeložíme do objektového souboru, který bude pojmenován adder.o. Povšimněte si použití volby PIC, kterou se zapíná takzvaný Position Independent Code (tedy instrukcí nepoužívajících absolutní skoky) a která se používá při vytváření sdílených knihoven na některých architekturách (na x86_64 však v našem jednoduchém příkladu dostaneme stejný výsledek i bez použití této volby):
$ gcc -Wall -ansi -c -fPIC adder.c -o adder.o
Následně z tohoto objektového souboru vytvoříme sdílenou knihovnu (shared library) pojmenovanou libadder.so (přípona .so značí „shared object“):
$ gcc -shared -Wl,-soname,libadder.so -o libadder.so adder.o
Přesvědčíme se, že soubor libadder.so skutečně vznikl:
$ file libadder.so libadder.so: ELF 64-bit LSB shared object, x86-64, version 1 (SYSV), dynamically linked, BuildID[sha1]=ca87f6b0705f4ddd589de8cc00bd31cdb8c6b6d6, not stripped
Popř. se můžeme podívat i na symboly, které jsou v tomto souboru definovány. Použijeme k tomu nástroj nm:
$ nm libadder.so 00000000000010f9 T add 0000000000004020 b completed.8061 w __cxa_finalize 0000000000001040 t deregister_tm_clones 00000000000010b0 t __do_global_dtors_aux 0000000000003e78 d __do_global_dtors_aux_fini_array_entry 0000000000004018 d __dso_handle 0000000000003e80 d _DYNAMIC 0000000000001114 t _fini 00000000000010f0 t frame_dummy 0000000000003e70 d __frame_dummy_init_array_entry 00000000000020a0 r __FRAME_END__ 0000000000004000 d _GLOBAL_OFFSET_TABLE_ w __gmon_start__ 0000000000002000 r __GNU_EH_FRAME_HDR 0000000000001000 t _init w _ITM_deregisterTMCloneTable w _ITM_registerTMCloneTable 0000000000001070 t register_tm_clones 0000000000004020 d __TMC_END__
Můžeme se dokonce podívat, jakým způsobem se funkce add přeložila do strojového kódu. K tomuto účelu použijeme nástroj objdump a pro získání disassemblovaného textu zvolené funkce použijeme trik s awk, který z výpisu „vykousne“ pouze požadovanou funkci:
$ objdump -d -M intel libadder.so | awk -F"\n" -v RS="\n\n" '$1 ~ /add/' 00000000000010f9 : 10f9: f3 0f 1e fa endbr64 10fd: 55 push rbp 10fe: 48 89 e5 mov rbp,rsp 1101: 89 7d fc mov DWORD PTR [rbp-0x4],edi 1104: 89 75 f8 mov DWORD PTR [rbp-0x8],esi 1107: 8b 55 fc mov edx,DWORD PTR [rbp-0x4] 110a: 8b 45 f8 mov eax,DWORD PTR [rbp-0x8] 110d: 01 d0 add eax,edx 110f: 5d pop rbp 1110: c3 ret
5. Načtení dynamicky linkované knihovny v Pythonním skriptu a zavolání nativní funkce přes ctypes
Nejdříve si ukážeme, jak je možné dynamicky linkovanou knihovnu načíst do Pythonu přes standardní knihovnu ctypes. Po načtení (které ovšem může vyvolat výjimku) je možné pracovat s výsledkem jako s objektem, který mj. obsahuje i metodu nazvanou add, kterou lze zavolat jako běžnou Pythonovskou funkci:
import ctypes def load_library(library_name): return ctypes.CDLL(library_name) adder = load_library("libadder.so") print(adder.add(1,2))
$ export LD_LIBRARY_PATH=. $ python3 call_via_ctypes.py
Nebo takto:
$ LD_LIBRARY_PATH=. python3 call_from_python.py
Výsledkem by v každém případě měla být hodnota „3“ vypsaná na terminál.
6. Srážka staticky typovaného světa se světem typovaným dynamicky
Výše uvedená céčkovská funkce akceptuje pouze parametry typu int, což je z pohledu céčka celočíselná hodnota se znaménkem s „vhodnou“ šířkou odvozenou mj. i od použité architektury. Z pohledu Pythonu se ovšem chápání funkcí liší, protože funkcím je možné v runtime předat parametry libovolného typu. Například se můžeme pokusit o předání řetězce namísto druhého celočíselného parametru a sledovat, jak se bude program chovat:
import ctypes def load_library(library_name): return ctypes.CDLL(library_name) adder = load_library("libadder.so") print(adder.add(1, "foo"))
Program po spuštění kupodivu nezhavaruje (ctypes se snaží o provedení konverze), i když vypíše nesmyslné výsledky:
$ python3 call_via_ctypes2.py -1157149183
Naproti tomu snaha o předání celého čísla obsahujícího příliš velkou hodnotu, která se nevejde do nativního typu int, již vede k pádu aplikace:
import ctypes def load_library(library_name): return ctypes.CDLL(library_name) adder = load_library("libadder.so") print(adder.add(1, 2**100))
Pokus o spuštění:
$ python3 call_via_ctypes3.py Traceback (most recent call last): File "call_via_ctypes3.py", line 9, in print(adder.add(1, 2**100)) ctypes.ArgumentError: argument 2: <class 'OverflowError'>: int too long to convert
7. Načtení dynamicky linkované knihovny a zavolání funkce z této knihovny s využitím cffi
Nyní se skript načítající dynamicky linkovanou knihovnu a volající v ní uloženou nativní funkci pokusme přepsat tak, aby využíval balíček cffi a nikoli ctypes. Je to vlastně velmi jednoduché a přímočaré, ovšem s jednou výjimkou – balíčku cffi musíme specifikovat i hlavičku volané funkce (popř. definici použitých datových typů), a to v původní céčkovské syntaxi. Díky tomu dodáme balíčku cffi i ty informace, které nelze odvodit z obsahu dynamicky linkované knihovny:
from cffi import FFI ffi = FFI() ffi.cdef(""" int add(int x, int y); """) def load_library(library_name): return ffi.dlopen(library_name) adder = load_library("libadder.so") print(adder.add(1,2))
Při spuštění je opět nutné zajistit, aby načítaná dynamicky linkovaná knihovna byla uložena v adresáři, na který ukazuje proměnná prostředí LD_LIBRARY_PATH.
8. Kontrola datových typů předávaných hodnot knihovnou cffi
Víme již, že knihovna ctypes nekontroluje, že například do funkce akceptující celé číslo předáváme řetězec atd. Jak ale bude kontrola datových typů předávaných hodnot vypadat, pokud použijeme knihovnu cffi?
from cffi import FFI ffi = FFI() ffi.cdef(""" int add(int x, int y); """) def load_library(library_name): return ffi.dlopen(library_name) adder = load_library("libadder.so") print(adder.add(1,"foo"))
Nyní bude chyba detekována a volání se nezdaří (což je jen dobře):
Traceback (most recent call last): File "call_via_cffi2.py", line 14, in <module> print(adder.add(1,"foo")) TypeError: an integer is required
Podobně dojde k chybě ve chvíli, kdy se pokusíme předat příliš velkou celočíselnou hodnotu:
from cffi import FFI ffi = FFI() ffi.cdef(""" int add(int x, int y); """) def load_library(library_name): return ffi.dlopen(library_name) adder = load_library("libadder.so") print(adder.add(1,2**100))
Detekce chyby v runtime:
Traceback (most recent call last): File "call_via_cffi3.py", line 14, in <module> print(adder.add(1,2**100)) OverflowError: int too big to convert
9. Funkce naprogramovaná v céčku, která vytiskne předaný řetězec
V další části dnešního článku si opět ukážeme způsob volání velmi jednoduché céčkovské funkce. Tentokrát bude funkce akceptovat řetězec (což je ovšem v pojetí céčka ukazatel na první znak řetězce), který následně vytiskne:
#include <stdio.h> extern void greet(char *x) { printf("Hello %s!\n", x); }
Z dalšího textu bude patrné, že nyní již nebude vše tak jednoduché, jako doposud – a to kvůli zcela odlišnému chápání řetězců v céčku a v Pythonu.
10. Céčkovské řetězce jsou odlišné od Pythonovských řetězců!
Způsob předávání dalších číselných typů (kromě komplexních čísel) je stejně jednoduchý (nebo problematický – záleží na úhlu pohledu), jako práce s parametry a návratovými hodnotami typu int, resp. long. Pojďme si však ukázat, jak lze zajistit komunikaci mezi funkcí napsanou v C a skriptem v Pythonu za situace, kdy je nutné céčkovské funkci předat řetězec. Z pohledu céčka mají řetězce zcela jiné vlastnosti, než v Pythonu:
- Jedná se o sekvenci bajtů ukončených hodnotou 0
- Z pohledu programátora se jedná o ukazatel na první znak řetězce
- Tato sekvence bajtů je měnitelná
- O alokaci a dealokaci se musí postarat programátor
Začneme funkcí popsanou v předchozí kapitole.
Nejprve nám již známým způsobem načteme a inicializujeme dynamicky linkovanou knihovnu, získáme referenci na céčkovskou funkci a zavoláme tuto funkci s předáním řetězce:
import ctypes def load_library(library_name): return ctypes.CDLL(library_name) greeter = load_library("libgreeter.so") greeter.greet("world")
Výsledkem bude:
Hello w!
Tento první pokus nebude v Pythonu 3 úspěšný, neboť se provádí převod na wchar_t*, tedy předává se ukazatel na řetězec zkonvertovaný na pole „širokých“ znaků. Hned první široký znak, tedy „W“, bude ve druhém bajtu obsahovat nulu, která (z pohledu céčka) řetězec ukončí, takže se vypíše právě ono dvojité wé:
Zkusme tedy převod řetězce na typ bytes, který je taktéž knihovnou ctypes podporován. Určíme, že se má řetězec přetransformovat do kódování UTF-8, kde již bude nulový bajt skutečně umístěn pouze na konci řetězce:
import ctypes def load_library(library_name): return ctypes.CDLL(library_name) greeter = load_library("libgreeter.so") greeter.greet(b"world")
Výsledek:
Hello world!
Alternativní způsob, který korektně převede Pythonovský řetězec do podoby kompatibilní s většinou céčkovských funkcí:
import ctypes def load_library(library_name): return ctypes.CDLL(library_name) greeter = load_library("libgreeter.so") greeter.greet("world".encode("utf-8"))
11. Předání řetězců do céčkovské funkce realizované přes cffi
Nyní se pokusme stejnou chybu, tedy přímé předání Pythonovského řetězce do céčkovské funkce, která akceptuje char *, udělat i při použití knihovny cffi:
from cffi import FFI ffi = FFI() ffi.cdef(""" void greet(char *x); """) def load_library(library_name): return ffi.dlopen(library_name) greeter = load_library("libgreeter.so") greeter.greet("world")
Knihovna cffi je v tomto ohledu mnohem přísnější než ctypes a chybu odhalí (což je jen dobře, protože tato chyba by jinak mohla zůstat nepovšimnuta):
Traceback (most recent call last): File "call_via_cffi1.py", line 14, in greeter.greet("world") TypeError: initializer for ctype 'char *' must be a bytes or list or tuple, not str
Oba způsoby opravy jsou knihovnou cffi akceptovány:
from cffi import FFI ffi = FFI() ffi.cdef(""" void greet(char *x); """) def load_library(library_name): return ffi.dlopen(library_name) greeter = load_library("libgreeter.so") greeter.greet(b"world")
Výsledek:
Hello world!
I tento způsob je akceptován:
from cffi import FFI ffi = FFI() ffi.cdef(""" void greet(char *x); """) def load_library(library_name): return ffi.dlopen(library_name) greeter = load_library("libgreeter.so") greeter.greet("world".encode("utf-8"))
Výsledek:
Hello world!
12. Programovací jazyk Python a céčkovské ukazatele
Mnoho funkcí naprogramovaných v céčku a uložených v přeložené formě do dynamicky linkovaných knihoven očekává, že některé předávané parametry budou ukazateli, popř. takové funkce vrací ukazatele na hodnotu určitého typu. To představuje určitý problém, protože v Pythonu se koncept ukazatelů nepoužívá (a reference jsou v Pythonu něco zcela odlišného). Proto je nutné, aby byla zajištěna explicitní „výroba“ ukazatelů přímo v Pythonu, což budou, jak uvidíme dále, objekty, které nám na straně Pythonu umožní přístup k uložené hodnotě a na straně céčka se bude jednat o skutečné řetězce.
13. Funkce psaná v C, která prohodí hodnoty parametrů předaných referencí
Použití ukazatelů si prozatím ukážeme na další velmi jednoduché funkci (v další části článku budou uvedeny složitější příklady). Jedná se o funkci, které se předají odkazy na dvě celočíselné hodnoty. Funkce tyto hodnoty prohodí a díky tomu, že parametry nejsou předávány hodnotou, ale již zmíněným odkazem, bude tato modifikace viditelná i mimo funkci swap:
extern void swap(int *x, int *y) { *x = *x ^ *y; *y = *x ^ *y; *x = *x ^ *y; }
14. Inicializace ukazatelů na straně Pythonu, předání ukazatelů do volané céčkové funkce
Funkci swap musíme při jejím volání předat dvojici ukazatelů. Tyto ukazatele na straně Pythonu „vyrobíme“ konstruktorem ffi.new, kterému v řetězci předáme jak očekávaný datový typ z pohledu céčka, tak i (nepovinnou) výchozí hodnotu. Pro přístup k takto reprezentovaným hodnotám se používá operátor indexování, přičemž při použití ukazatelů je jediným platným indexem nula:
from cffi import FFI ffi = FFI() ffi.cdef(""" void swap(int *x, int *y); """) def load_library(library_name): return ffi.dlopen(library_name) swapper = load_library("libswapper.so") x = ffi.new("int *", 10) y = ffi.new("int *", 20) swapper.swap(x, y) print(x[0]) print(y[0])
Výsledek:
20 10
15. Kontrola, zda se s ukazatelem nezachází jako s polem
Mohlo by se zdát, že se objekty získané přes ffi.new(„int *“, 10) chovají jako pole, když se k jejím hodnotám přistupuje přes indexovací operátor. Ve skutečnosti je však jediným platným indexem nula, což je v době běhu programu kontrolováno:
from cffi import FFI ffi = FFI() ffi.cdef(""" void swap(int *x, int *y); """) def load_library(library_name): return ffi.dlopen(library_name) swapper = load_library("libswapper.so") x = ffi.new("int *", 10) y = ffi.new("int *", 20) swapper.swap(x, y) print(x[1]) print(y[1])
V tomto příkladu jsme se pokusili o přístup k prvkům s indexem 1, což však není povoleno:
Traceback (most recent call last): File "call_via_cffi2.py", line 19, in <module> print(x[1]) IndexError: cdata 'int *' can only be indexed by 0
16. Práce s poli, konverze mezi polem a datovými typy Pythonu
Od ukazatelů (k nimž se ovšem ještě vrátíme příště) se dostáváme k problematice použití céčkových polí. Připomeňme si, že pole jsou společně s řetězci (což jsou vlastně taktéž pole) v céčku jedinými datovými typy, které se předávají odkazem a nikoli hodnotou (pole lze předat hodnotou v případě, že je zabaleno do struktury). Navíc se pole pro naprostou většinu operací (s výjimkou operátorů & a sizeof) chovají stejně, jako by se jednalo o ukazatel na typ, jenž odpovídá typu prvků pole:
int a[10];
V naprosté většině výrazů se s tímto polem pracuje, jakoby se jednalo o proměnnou typu int *. Podobně platí pro referencování prvků polí, že lze identifikátorem pole pracovat tak, jakoby se jednalo o ukazatel:
E1[E2] == (*((E1)+(E2)))
Interně jsou prvky pole uloženy za sebou bez použití výplně – a tudíž se jedná o zcela odlišnou formu reprezentace, než je tomu u Pythonních seznamů a n-tic.
17. Funkce psaná v C, která vyplní pole zvolenou hodnotou
Poslední céčkovskou funkcí, kterou dnes použijeme, je funkce určená pro vyplnění prvků pole zadanou hodnotou, které se předávají tři parametry:
- Ukazatel na první prvek pole
- Velikost pole, resp. počet prvků, které se mají změnit
- Nová hodnota prvků, která se má nastavit
Deklarace této funkce vypadá následovně:
extern void fill(int *array, int size, int value) { int i; for (i=0; i<size; i++) { array[i] = value; } }
18. Volání funkce pro výplň pole z Pythonu, zobrazení výsledného pole
Výše uvedenou céčkovskou funkci nyní zavoláme s předáním pole, které je vytvořeno konstruktorem:
array = ffi.new("int[10]")
Nejprve naplníme prvních pět prvků pole hodnotou 42 a následně druhých pět prvků hodnotou –1:
filler.fill(array, len(array)//2, 42) filler.fill(array+5, len(array)//2, -1)
Celý skript bude vypadat následovně:
from cffi import FFI ffi = FFI() ffi.cdef(""" void fill(int *x, int, int); """) def load_library(library_name): return ffi.dlopen(library_name) filler = load_library("libfiller.so") array = ffi.new("int[10]") print(list(array)) filler.fill(array, len(array)//2, 42) print(list(array)) filler.fill(array+5, len(array)//2, -1) print(list(array))
Vypsané hodnoty:
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0] [42, 42, 42, 42, 42, 0, 0, 0, 0, 0] [42, 42, 42, 42, 42, -1, -1, -1, -1, -1]
19. Repositář s demonstračními příklady
Všechny Pythonovské skripty, které jsme si v dnešním článku ukázali, naleznete na adrese https://github.com/tisnik/most-popular-python-libs. Následují odkazy na jednotlivé příklady (pro jejich spuštění je nutné mít nainstalovánu knihovnu cffi:
# | Příklad | Stručný popis | Adresa |
---|---|---|---|
1 | adder/adder.c | funkce psaná v C, která sečte své dva celočíselné parametry | https://github.com/tisnik/most-popular-python-libs/blob/master/cffi/adder/adder.c |
2 | adder/call_via_cffi1.py | zavolání céčkovské funkce přes cffi s korektními parametry | https://github.com/tisnik/most-popular-python-libs/blob/master/cffi/adder/call_via_cffi1.py |
3 | adder/call_via_cffi2.py | zavolání céčkovské funkce přes cffi s nekorektními parametry | https://github.com/tisnik/most-popular-python-libs/blob/master/cffi/adder/call_via_cffi2.py |
4 | adder/call_via_cffi3.py | zavolání céčkovské funkce přes cffi s nekorektními parametry | https://github.com/tisnik/most-popular-python-libs/blob/master/cffi/adder/call_via_cffi3.py |
5 | adder/call_via_cffi.sh | nastavení cest a spuštění všech tří předchozích Pythonovských skriptů | https://github.com/tisnik/most-popular-python-libs/blob/master/cffi/adder/call_via_cffi.sh |
6 | adder/call_via_ctypes1.py | zavolání céčkovské funkce přes ctypes s korektními parametry | https://github.com/tisnik/most-popular-python-libs/blob/master/cffi/adder/call_via_ctypes1.py |
7 | adder/call_via_ctypes2.py | zavolání céčkovské funkce přes ctypes s nekorektními parametry | https://github.com/tisnik/most-popular-python-libs/blob/master/cffi/adder/call_via_ctypes2.py |
8 | adder/call_via_ctypes3.py | zavolání céčkovské funkce přes ctypes s nekorektními parametry | https://github.com/tisnik/most-popular-python-libs/blob/master/cffi/adder/call_via_ctypes3.py |
9 | adder/call_via_ctypes.sh | nastavení cest a spuštění všech tří předchozích Pythonovských skriptů | https://github.com/tisnik/most-popular-python-libs/blob/master/cffi/adder/call_via_ctypes.sh |
10 | adder/make_library.sh | skript pro překlad céčkovské funkce a vytvoření dynamicky linkované knihovny | https://github.com/tisnik/most-popular-python-libs/blob/master/cffi/adder/make_library.sh |
11 | adder/clean.sh | skript pro smazání objektového souboru i dynamicky linkované knihovny | https://github.com/tisnik/most-popular-python-libs/blob/master/cffi/adder/clean.sh |
12 | greeter/greeter.c | funkce psaná v C, která na standardní výstup vytiskne řetězec | https://github.com/tisnik/most-popular-python-libs/blob/master/cffi/greeter/greeter.c |
13 | greeter/call_via_cffi1.py | zavolání céčkovské funkce přes cffi s nekorektním parametrem | https://github.com/tisnik/most-popular-python-libs/blob/master/cffi/greeter/call_via_cffi1.py |
14 | greeter/call_via_cffi2.py | zavolání céčkovské funkce přes cffi s korektním parametrem | https://github.com/tisnik/most-popular-python-libs/blob/master/cffi/greeter/call_via_cffi2.py |
15 | greeter/call_via_cffi3.py | zavolání céčkovské funkce přes cffi s korektním parametrem | https://github.com/tisnik/most-popular-python-libs/blob/master/cffi/greeter/call_via_cffi3.py |
16 | greeter/call_via_cffi.sh | nastavení cest a spuštění všech tří předchozích Pythonovských skriptů | https://github.com/tisnik/most-popular-python-libs/blob/master/cffi/greeter/call_via_cffi.sh |
17 | greeter/call_via_ctypes1.py | zavolání céčkovské funkce přes ctypes s nekorektním parametrem | https://github.com/tisnik/most-popular-python-libs/blob/master/cffi/greeter/call_via_ctypes1.py |
18 | greeter/call_via_ctypes2.py | zavolání céčkovské funkce přes ctypes s korektním parametrem | https://github.com/tisnik/most-popular-python-libs/blob/master/cffi/greeter/call_via_ctypes2.py |
19 | greeter/call_via_ctypes3.py | zavolání céčkovské funkce přes ctypes s korektním parametrem | https://github.com/tisnik/most-popular-python-libs/blob/master/cffi/greeter/call_via_ctypes3.py |
20 | greeter/call_via_ctypes.sh | nastavení cest a spuštění všech tří předchozích Pythonovských skriptů | https://github.com/tisnik/most-popular-python-libs/blob/master/cffi/greeter/call_via_ctypes.sh |
21 | greeter/make_library.sh | skript pro překlad céčkovské funkce a vytvoření dynamicky linkované knihovny | https://github.com/tisnik/most-popular-python-libs/blob/master/cffi/greeter/make_library.sh |
22 | greeter/clean.sh | skript pro smazání objektového souboru i dynamicky linkované knihovny | https://github.com/tisnik/most-popular-python-libs/blob/master/cffi/greeter/clean.sh |
23 | swapper/swapper.c | céčkovská funkce prohazující obsah svých dvou parametrů předávaných referencí | https://github.com/tisnik/most-popular-python-libs/blob/master/cffi/swapper/swapper.c |
24 | swapper/call_via_cffi1.py | zavolání céčkovské knihovny z jazyka Python (korektní varianta) | https://github.com/tisnik/most-popular-python-libs/blob/master/cffi/swapper/call_via_cffi1.py |
25 | swapper/call_via_cffi2.py | zavolání céčkovské knihovny z jazyka Python (nekorektní varianta) | https://github.com/tisnik/most-popular-python-libs/blob/master/cffi/swapper/call_via_cffi2.py |
26 | swapper/call_via_cffi.sh | nastavení cest a spuštění Pythonovského skriptu | https://github.com/tisnik/most-popular-python-libs/blob/master/cffi/swapper/call_via_cffi.sh |
27 | swapper/make_library.sh | skript pro překlad céčkovské funkce a vytvoření dynamicky linkované knihovny | https://github.com/tisnik/most-popular-python-libs/blob/master/cffi/swapper/make_library.sh |
28 | swapper/clean.sh | skript pro smazání objektového souboru i dynamicky linkované knihovny | https://github.com/tisnik/most-popular-python-libs/blob/master/cffi/swapper/clean.sh |
29 | filler/filler.c | céčkovská funkce pro vyplnění části pole zadanou hodnotou | https://github.com/tisnik/most-popular-python-libs/blob/master/cffi/filler/filler.c |
30 | filler/call_via_cffi.py | zavolání céčkovské knihovny z jazyka Python | https://github.com/tisnik/most-popular-python-libs/blob/master/cffi/filler/call_via_cffi.py |
31 | filler/call_via_cffi.sh | nastavení cest a spuštění Pythonovského skriptu | https://github.com/tisnik/most-popular-python-libs/blob/master/cffi/filler/call_via_cffi.sh |
32 | filler/make_library.sh | skript pro překlad céčkovské funkce a vytvoření dynamicky linkované knihovny | https://github.com/tisnik/most-popular-python-libs/blob/master/cffi/filler/make_library.sh |
32 | filler/clean.sh | skript pro smazání objektového souboru i dynamicky linkované knihovny | https://github.com/tisnik/most-popular-python-libs/blob/master/cffi/filler/clean.sh |
20. Odkazy na Internetu
- TIOBE Index for May 2023
https://www.tiobe.com/tiobe-index/ - PYPL PopularitY of Programming Language
https://pypl.github.io/PYPL.html - CFFI documentation
https://cffi.readthedocs.io/en/latest/ - cffi 1.15.1 na PyPi
https://pypi.org/project/cffi/ - Python Bindings: Calling C or C++ From Python
https://realpython.com/python-bindings-overview/ - Interfacing with C/C++ Libraries
https://docs.python-guide.org/scenarios/clibs/ - Cython, pybind11, cffi – which tool should you choose?
http://blog.behnel.de/posts/cython-pybind11-cffi-which-tool-to-choose.html - Python FFI with ctypes and cffi
https://eli.thegreenplace.net/2013/03/09/python-ffi-with-ctypes-and-cffi - Propojení Go s Pythonem s využitím cgo a ctypes
https://www.root.cz/clanky/propojeni-go-s-pythonem-s-vyuzitim-cgo-a-ctypes/ - Propojení Go s Pythonem s využitím cgo a ctypes (2. část)
https://www.root.cz/clanky/propojeni-go-s-pythonem-s-vyuzitim-cgo-a-ctypes-2-cast/ - Programovací jazyk Rust: použití FFI pro volání funkcí z nativních knihoven
https://www.root.cz/clanky/programovaci-jazyk-rust-pouziti-ffi-pro-volani-funkci-z-nativnich-knihoven/ - Programovací jazyk Rust: použití FFI při předávání struktur
https://www.root.cz/clanky/programovaci-jazyk-rust-pouziti-ffi-pri-predavani-struktur/ - Programovací jazyk Rust: použití FFI pro volání funkcí z nativních knihoven (2. část)
https://www.root.cz/clanky/programovaci-jazyk-rust-pouziti-ffi-pro-volani-funkci-z-nativnich-knihoven-2-cast/ - Dynamic-link library
https://en.wikipedia.org/wiki/Dynamic-link_library