Propojení Pythonu s nativními knihovnami s využitím balíčku ctypes: struktury a ukazatele

26. 6. 2025
Doba čtení: 55 minut

Sdílet

Autor: Root.cz s využitím DALL-E
S modulem ctypes, který umožňuje z Pythonu volat nativní funkce, jež jsou typicky vytvořené v jazyku C, jsme se již setkali. Ovšem ještě si musíme vysvětlit, jak se do nativních funkcí předávají struktury.

Obsah

1. Propojení Pythonu s nativními knihovnami s využitím balíčku ctypes: struktury a ukazatele

2. Nativní funkce, které budou volány z Pythonu

3. Pomocná funkce pro vykreslení pixelu

4. Volání nativních funkcí z Pythonu s předáním všech parametrů

5. Úplný zdrojový kód dnešního prvního demonstračního příkladu: céčková i Pythonní část

6. Předání většího množství parametrů do nativních funkcí volaných z Pythonu

7. Modifikace skriptu psaného v Pythonu, který nativní funkce volá

8. Předání struktury do nativních funkcí

9. Konstrukce struktury na straně skriptu psaného v Pythonu

10. Úplný zdrojový kód dnešního třetího demonstračního příkladu: céčková i Pythonní část

11. Nativní funkce akceptující ukazatel na strukturu

12. Předání ukazatele na strukturu z Pythonu do nativních funkcí

13. Úplný zdrojový kód dnešního čtvrtého demonstračního příkladu: céčková i Pythonní část

14. Struktura s hodnotami použitými při výpočtech fraktálů

15. Úplný zdrojový kód dnešního pátého demonstračního příkladu: céčková i Pythonní část

16. Správa paměti

17. Ukázka nekorektní práce s pamětí

18. Zarovnání a výplně ve strukturách

19. Repositář s demonstračními příklady

20. Odkazy na Internetu

1. Propojení Pythonu s nativními knihovnami s využitím balíčku ctypes: struktury a ukazatele

Propojení programovacího jazyka Python s jazykem C (popř. s Rustem nebo jazykem Zig) přináší zajímavé možnosti a můžeme tak vyřešit poměrně velké množství praktických problémů. V první řadě tato technologie umožňuje relativně snadné volání funkcí ze systémových knihoven, ale i dalších funkcí dostupných formou dynamicky sdílených knihoven. Díky tomu je možné spojit snadnost a rychlost tvorby aplikací v Pythonu (vysokoúrovňový jazyk s relativně velkou mírou abstrakce) s optimalizovaným nativním kódem. Dobrým příkladem takového propojení je projekt Numpy, v němž se výpočetně náročné části realizují nativními funkcemi, ovšem programátoři tyto funkce volají přímo z Pythonu. A příkladem propojení Pythonu s Rustem může být projekt Polars, se kterým jsme se na stránkách Roota taktéž již setkali v článcích Knihovna Polars: výkonnější alternativa ke knihovně Pandas a Knihovna Polars: výkonnější alternativa ke knihovně Pandas (datové rámce).

V úvodním článku o knihovně ctypes, v němž jsme toto téma probírali, jsme si ukázali, jakým způsobem je možné do nativních (céčkovských) funkcí předávat celočíselné hodnoty, hodnoty typu float/double a taktéž pole (resp. přesněji řečeno ukazatele na první prvky polí). Dnes si tyto informace doplníme o způsob předávání celých datových struktur (struct), a to jak hodnotou, tak i referencí (tedy přes ukazatel). To nám umožní jak snížení počtu předávaných parametrů, tak i zajištění větší čitelnosti výsledných programů: týká se to jak Pythonní části, tak i části naprogramované v jazyku C.

2. Nativní funkce, které budou volány z Pythonu

Připomeňme si ve stručnosti, jak vlastně vypadají nativní funkce, které budeme chtít volat ze skriptu naprogramovaného v Pythonu. Jedná se o funkce, které dokážou vypočítat klasickou Mandelbrotovu množinu i Juliovy množiny (pro zadané parametry). Výsledné obrázky jsou reprezentovány jednorozměrnými poli s prvky typu unsigned char (pixely jsou ukládány po řádcích ve formátu RGB se čtyřmi bajty na pixel) a pixely jsou obarveny na základě barvové palety (256 trojic hodnot, taktéž typu unsigned char). Tím jsme si do značné míry zjednodušili práci, protože do funkcí pro výpočet Mandelbrotovy množiny a Juliových množin předáváme celočíselné hodnoty, hodnoty typu double a taktéž ukazatele na typ unsighed char.

Funkce provádějící výpočet Mandelbrotovy množiny, vypadá následovně (kvůli jednoduchosti se neprovádí žádné optimalizace):

void render_mandelbrot(unsigned int width, unsigned int height,
                       const unsigned char *palette, unsigned char *pixels) {
    int x, y;
    double cx, cy;
    double xmin = -2.0, ymin = -1.5, xmax = 1.0, ymax = 1.5;
    unsigned char *p = pixels;
 
    cy = ymin;
    for (y = 0; y < height; y++) {
        cx = xmin;
        for (x = 0; x < width; x++) {
            double zx = 0.0;
            double zy = 0.0;
            unsigned int i = 0;
            while (i < 150) {
                double zx2 = zx * zx;
                double zy2 = zy * zy;
                if (zx2 + zy2 > 4.0) {
                    break;
                }
                zy = 2.0 * zx * zy + cy;
                zx = zx2 - zy2 + cx;
                i++;
            }
            putpixel(&p, palette, i);
            cx += (xmax - xmin) / width;
        }
        cy += (ymax - ymin) / height;
    }
}

Funkce pro výpočet Juliových množin navíc akceptuje další dva parametry cx a cy, které jsou typu double:

void render_julia(unsigned int width, unsigned int height,
                  const unsigned char *palette, unsigned char *pixels,
                  double cx, double cy) {
    int x, y;
    double zx0, zy0;
    double xmin = -1.5, ymin = -1.5, xmax = 1.5, ymax = 1.5;
    unsigned char *p = pixels;
 
    zy0 = ymin;
    for (y = 0; y < height; y++) {
        zx0 = xmin;
        for (x = 0; x < width; x++) {
            double zx = zx0;
            double zy = zy0;
            unsigned int i = 0;
            while (i < 150) {
                double zx2 = zx * zx;
                double zy2 = zy * zy;
                if (zx2 + zy2 > 4.0) {
                    break;
                }
                zy = 2.0 * zx * zy + cy;
                zx = zx2 - zy2 + cx;
                i++;
            }
            putpixel(&p, palette, i);
            zx0 += (xmax - xmin) / width;
        }
        zy0 += (ymax - ymin) / height;
    }
}

3. Pomocná funkce pro vykreslení pixelu

Pro úplnost si doplníme zdrojový kód pomocné funkce, která slouží pro vykreslení jednoho pixelu. Této funkci se předá ukazatel na první bajt pixelu v rastrovém obrázku (přes referenci!), barvová paleta a index do této palety. Funkce navíc změní referenci na aktivní pixel – další volání putpixel tedy vybarví následující pixel v rastrovém obrázku:

void putpixel(unsigned char **pixel, const unsigned char *palette,
              int color_index) {
    int color_offset = color_index * 3;
    unsigned char *pal = (unsigned char *)(palette + color_offset);
 
    *(*pixel)++ = *pal++;
    *(*pixel)++ = *pal++;
    *(*pixel)++ = *pal;
    (*pixel)++;
}

4. Volání nativních funkcí z Pythonu s předáním všech parametrů

Podívejme se nyní na způsob volání nativních funkcí z Pythonu. Volat přitom budeme obě výše zmíněné funkce pro výpočet a vykreslení fraktálů, které mají hlavičky:

void render_mandelbrot(unsigned int width, unsigned int height,
                       const unsigned char *palette, unsigned char *pixels) {
 
void render_julia(unsigned int width, unsigned int height,
                  const unsigned char *palette, unsigned char *pixels,
                  double cx, double cy) {
 

Do těchto funkcí se předává několik typů hodnot:

  1. Celočíselné hodnoty typu unsigned int
  2. Hodnoty typu double
  3. Hodnoty typu const unsigned char * (ve skutečnosti pole 256×3 prvky)
  4. Hodnoty typu unsigned char * (obrázky šířka×výška×4 bajty)

Předání prvních dvou typů hodnot je triviální – Pythonovské proměnné typu int/long převedeme do nativní podoby pomocí ctypes.c_int() a proměnné typu double pak pomocí ctypes.c_double().

Pro převod barvové palety (256×3 prvky) na nativní pole prvků typu unsigned char si vytvoříme pomocnou funkci, která může vypadat následovně:

def palette_to_buffer(p):
    s = create_string_buffer(len(p) * 3)
    i = 0
    for color in p:
        s[i] = color[0]
        s[i + 1] = color[1]
        s[i + 2] = color[2]
        i += 3
    return s

Příklad použití:

pal = palette_to_buffer(palette)
Poznámka: nenechte se zmást názvem create_string_buffer; výsledkem bude paměťová oblast, kterou můžeme v jazyku C považovat za pole prvků typu char nebo unsigned char.

Následně si necháme (přímo v Pythonu) naalokovat buffer o takové velikosti, aby do něj bylo možné uložit všechny pixely obrázku. Každý pixel je uložen ve čtyřech bajtech, takže velikost bude rovna 4 * width * height. Pro alokaci bufferu použijeme funkci create_string_buffer, která však (i přes své jméno) akceptuje velikost v bajtech:

from ctypes import create_string_buffer
 
def image_from_buffer(buffer, width, height, fmt):
    return pygame.image.frombytes(bytes(buffer), (width, height), fmt)

Způsob konstrukce obou výše zmíněných objektů a volání nativních funkcí může vypadat takto:

pal = palette_to_buffer(palette)
 
# try to load dynamically linked library
renderer = CDLL("./renderer.so")
 
# create buffer for raster image
buffer = create_string_buffer(4 * IMAGE_WIDTH * IMAGE_HEIGHT)
 
# render Mandelbrot set into buffer
renderer.render_mandelbrot(c_int(IMAGE_WIDTH), c_int(IMAGE_HEIGHT), pal, buffer)
image1 = image_from_buffer(buffer, IMAGE_WIDTH, IMAGE_HEIGHT, "RGBX")
 
cx = -0.171119200000000013445
cy = 0.657309400000000000000
 
# render Julia set into buffer
renderer.render_julia(
    c_int(IMAGE_WIDTH),
    c_int(IMAGE_HEIGHT),
    pal,
    buffer,
    c_double(cx),
    c_double(cy),
)
image2 = image_from_buffer(buffer, IMAGE_WIDTH, IMAGE_HEIGHT, "RGBX")
...
...
...
Poznámka: výsledek by měl být plně přenositelný mezi platformami.

5. Úplný zdrojový kód dnešního prvního demonstračního příkladu: céčková i Pythonní část

Dnešní první demonstrační příklad má dvě části: nativní (céčkovou) a Pythonní. Zdrojový kód céčkové části vypadá následovně:

void putpixel(unsigned char **pixel, const unsigned char *palette,
              int color_index) {
    int color_offset = color_index * 3;
    unsigned char *pal = (unsigned char *)(palette + color_offset);

    *(*pixel)++ = *pal++;
    *(*pixel)++ = *pal++;
    *(*pixel)++ = *pal;
    (*pixel)++;
}
 
void render_mandelbrot(unsigned int width, unsigned int height,
                       const unsigned char *palette, unsigned char *pixels) {
    int x, y;
    double cx, cy;
    double xmin = -2.0, ymin = -1.5, xmax = 1.0, ymax = 1.5;
    unsigned char *p = pixels;
 
    cy = ymin;
    for (y = 0; y < height; y++) {
        cx = xmin;
        for (x = 0; x < width; x++) {
            double zx = 0.0;
            double zy = 0.0;
            unsigned int i = 0;
            while (i < 150) {
                double zx2 = zx * zx;
                double zy2 = zy * zy;
                if (zx2 + zy2 > 4.0) {
                    break;
                }
                zy = 2.0 * zx * zy + cy;
                zx = zx2 - zy2 + cx;
                i++;
            }
            putpixel(&p, palette, i);
            cx += (xmax - xmin) / width;
        }
        cy += (ymax - ymin) / height;
    }
}
 
void render_julia(unsigned int width, unsigned int height,
                  const unsigned char *palette, unsigned char *pixels,
                  double cx, double cy) {
    int x, y;
    double zx0, zy0;
    double xmin = -1.5, ymin = -1.5, xmax = 1.5, ymax = 1.5;
    unsigned char *p = pixels;

    zy0 = ymin;
    for (y = 0; y < height; y++) {
        zx0 = xmin;
        for (x = 0; x < width; x++) {
            double zx = zx0;
            double zy = zy0;
            unsigned int i = 0;
            while (i < 150) {
                double zx2 = zx * zx;
                double zy2 = zy * zy;
                if (zx2 + zy2 > 4.0) {
                    break;
                }
                zy = 2.0 * zx * zy + cy;
                zx = zx2 - zy2 + cx;
                i++;
            }
            putpixel(&p, palette, i);
            zx0 += (xmax - xmin) / width;
        }
        zy0 += (ymax - ymin) / height;
    }
}

Překlad této části se provede přes Makefile:

.PHONY: all clean
 
CC=gcc
CFLAGS=-Wall -pedantic -ansi -O2
 
all: renderer.so
 
clean:
        rm -f renderer.so
 
renderer.so:    renderer.c
        $(CC) $(CFLAGS) -shared -Wl,-soname,renderer -o $@ -fPIC $<

Část naprogramovaná v Pythonu bude vypadat následovně:

import sys
from ctypes import CDLL, c_double, c_int, create_string_buffer
 
from palette_mandmap import palette
 
import pygame
import pygame.locals
 
TITLE = "Renderer"
SCREEN_WIDTH = 600
SCREEN_HEIGHT = 300
IMAGE_WIDTH = 256
IMAGE_HEIGHT = 256
 
 
def initialize_ui(title, width, height):
    """Initialize Pygame display, drawing surface, and clocks."""
    # set window title
    pygame.display.set_caption(title)
 
    # initialize window
    display = pygame.display.set_mode([width, height])
    display.fill((0, 0, 0))
 
    clock = pygame.time.Clock()
 
    return display, clock
 
 
def palette_to_buffer(p):
    s = create_string_buffer(len(p) * 3)
    i = 0
    for color in p:
        s[i] = color[0]
        s[i + 1] = color[1]
        s[i + 2] = color[2]
        i += 3
    return s
 
 
def event_loop(display, image1, image2, clock):
    while True:
        for event in pygame.event.get():
            if event.type == pygame.locals.QUIT:
                pygame.quit()
                sys.exit()
            if event.type == pygame.locals.KEYDOWN:
                if event.key == pygame.locals.K_ESCAPE:
                    pygame.quit()
                    sys.exit()
 
        # all events has been processed - update scene and redraw the screen
        display.blit(image1, (30, 20))
        display.blit(image2, (60 + image1.get_width(), 20))
 
        # and update the whole display
        pygame.display.update()
        clock.tick(25)
 
 
def image_from_buffer(buffer, width, height, fmt):
    return pygame.image.frombytes(bytes(buffer), (width, height), fmt)
 
 
def main():
    pal = palette_to_buffer(palette)
 
    display, clock = initialize_ui(TITLE, SCREEN_WIDTH, SCREEN_HEIGHT)
 
    # try to load dynamically linked library
    renderer = CDLL("./renderer.so")
 
    # create buffer for raster image
    buffer = create_string_buffer(4 * IMAGE_WIDTH * IMAGE_HEIGHT)
 
    # render Mandelbrot set into buffer
    renderer.render_mandelbrot(c_int(IMAGE_WIDTH), c_int(IMAGE_HEIGHT), pal, buffer)
    image1 = image_from_buffer(buffer, IMAGE_WIDTH, IMAGE_HEIGHT, "RGBX")
 
    cx = -0.171119200000000013445
    cy = 0.657309400000000000000
 
    # render Julia set into buffer
    renderer.render_julia(
        c_int(IMAGE_WIDTH),
        c_int(IMAGE_HEIGHT),
        pal,
        buffer,
        c_double(cx),
        c_double(cy),
    )
    image2 = image_from_buffer(buffer, IMAGE_WIDTH, IMAGE_HEIGHT, "RGBX")
 
    event_loop(display, image1, image2, clock)
 
 
if __name__ == "__main__":
    main()
 
 
# finito

6. Předání většího množství parametrů do nativních funkcí volaných z Pythonu

V dnešním prvním demonstračním příkladu, který jsme si ukázali v předchozích kapitolách, se vyskytovala magická konstanta 150 představující maximální počet iterací při výpočtu barev pixelů v Mandelbrotově nebo Juliových množinách. Tuto konstantu pochopitelně můžeme nahradit za parametr (což by bylo více než vhodné). Obě funkce se nepatrně změní – modifikuje se jejich hlavička (což je pochopitelné) i vnitřní smyčka s výpočtem. Nativní část aplikace se změní následovně (modifikované části jsou podtrženy):

void putpixel(unsigned char **pixel, const unsigned char *palette,
              int color_index) {
    int color_offset = color_index * 3;
    unsigned char *pal = (unsigned char *)(palette + color_offset);
 
    *(*pixel)++ = *pal++;
    *(*pixel)++ = *pal++;
    *(*pixel)++ = *pal;
    (*pixel)++;
}
 
void render_mandelbrot(unsigned int width, unsigned int height,
                       const unsigned char *palette, unsigned char *pixels,
                       unsigned int maxiter) {
    int x, y;
    double cx, cy;
    double xmin = -2.0, ymin = -1.5, xmax = 1.0, ymax = 1.5;
    unsigned char *p = pixels;
 
    cy = ymin;
    for (y = 0; y < height; y++) {
        cx = xmin;
        for (x = 0; x < width; x++) {
            double zx = 0.0;
            double zy = 0.0;
            unsigned int i = 0;
            while (i < maxiter) {
                double zx2 = zx * zx;
                double zy2 = zy * zy;
                if (zx2 + zy2 > 4.0) {
                    break;
                }
                zy = 2.0 * zx * zy + cy;
                zx = zx2 - zy2 + cx;
                i++;
            }
            putpixel(&p, palette, i);
            cx += (xmax - xmin) / width;
        }
        cy += (ymax - ymin) / height;
    }
}
 
void render_julia(unsigned int width, unsigned int height,
                  const unsigned char *palette, unsigned char *pixels,
                  unsigned int maxiter, double cx, double cy) {
    int x, y;
    double zx0, zy0;
    double xmin = -1.5, ymin = -1.5, xmax = 1.5, ymax = 1.5;
    unsigned char *p = pixels;
 
    zy0 = ymin;
    for (y = 0; y < height; y++) {
        zx0 = xmin;
        for (x = 0; x < width; x++) {
            double zx = zx0;
            double zy = zy0;
            unsigned int i = 0;
            while (i < maxiter) {
                double zx2 = zx * zx;
                double zy2 = zy * zy;
                if (zx2 + zy2 > 4.0) {
                    break;
                }
                zy = 2.0 * zx * zy + cy;
                zx = zx2 - zy2 + cx;
                i++;
            }
            putpixel(&p, palette, i);
            zx0 += (xmax - xmin) / width;
        }
        zy0 += (ymax - ymin) / height;
    }
}

7. Modifikace skriptu psaného v Pythonu, který nativní funkce volá

Samozřejmě je nutné modifikovat i Pythonovský skript, který obě výše zmíněné nativní funkce volá. Změny vypadají takto:

MAXITER = 150
...
...
...
# render Mandelbrot set into buffer
renderer.render_mandelbrot(
    c_int(IMAGE_WIDTH), c_int(IMAGE_HEIGHT), pal, buffer, c_int(MAXITER)
)
...
...
...
# render Julia set into buffer
renderer.render_julia(
    c_int(IMAGE_WIDTH),
    c_int(IMAGE_HEIGHT),
    pal,
    buffer,
    c_double(cx),
    c_double(cy),
    c_int(MAXITER),
)
Poznámka: v tomto případě knihovna ctypes neprovádí kontrolu, zda je předán korektní počet a typ parametrů! Ovšem volání nativní funkce může skončit v nekonečné smyčce, program může zhavarovat na poškození paměti atd., pokud nepředáme všechny parametry se správným typem a v korektním pořadí!

Pro úplnost si ukažme, jak vypadá celý skript, který je součástí dnešního druhého demonstračního příkladu. Modifikované části jsou opět podtrženy:

import sys
from ctypes import CDLL, c_double, c_int, create_string_buffer
 
from palette_mandmap import palette
 
import pygame
import pygame.locals
 
TITLE = "Renderer"
SCREEN_WIDTH = 600
SCREEN_HEIGHT = 300
IMAGE_WIDTH = 256
IMAGE_HEIGHT = 256
 
MAXITER = 150
 
 
def initialize_ui(title, width, height):
    """Initialize Pygame display, drawing surface, and clocks."""
    # set window title
    pygame.display.set_caption(title)
 
    # initialize window
    display = pygame.display.set_mode([width, height])
    display.fill((0, 0, 0))
 
    clock = pygame.time.Clock()
 
    return display, clock
 
 
def palette_to_buffer(p):
    s = create_string_buffer(len(p) * 3)
    i = 0
    for color in p:
        s[i] = color[0]
        s[i + 1] = color[1]
        s[i + 2] = color[2]
        i += 3
    return s
 
 
def event_loop(display, image1, image2, clock):
    while True:
        for event in pygame.event.get():
            if event.type == pygame.locals.QUIT:
                pygame.quit()
                sys.exit()
            if event.type == pygame.locals.KEYDOWN:
                if event.key == pygame.locals.K_ESCAPE:
                    pygame.quit()
                    sys.exit()
 
        # all events has been processed - update scene and redraw the screen
        display.blit(image1, (30, 20))
        display.blit(image2, (60 + image1.get_width(), 20))
 
        # and update the whole display
        pygame.display.update()
        clock.tick(25)
 
 
def image_from_buffer(buffer, width, height, fmt):
    return pygame.image.frombytes(bytes(buffer), (width, height), fmt)
 
 
def main():
    pal = palette_to_buffer(palette)
 
    display, clock = initialize_ui(TITLE, SCREEN_WIDTH, SCREEN_HEIGHT)
 
    # try to load dynamically linked library
    renderer = CDLL("./renderer.so")
 
    # create buffer for raster image
    buffer = create_string_buffer(4 * IMAGE_WIDTH * IMAGE_HEIGHT)
 
    # render Mandelbrot set into buffer
    renderer.render_mandelbrot(
        c_int(IMAGE_WIDTH), c_int(IMAGE_HEIGHT), pal, buffer, c_int(MAXITER)
    )
    image1 = image_from_buffer(buffer, IMAGE_WIDTH, IMAGE_HEIGHT, "RGBX")
 
    cx = -0.171119200000000013445
    cy = 0.657309400000000000000
 
    # render Julia set into buffer
    renderer.render_julia(
        c_int(IMAGE_WIDTH),
        c_int(IMAGE_HEIGHT),
        pal,
        buffer,
        c_double(cx),
        c_double(cy),
        c_int(MAXITER),
    )
    image2 = image_from_buffer(buffer, IMAGE_WIDTH, IMAGE_HEIGHT, "RGBX")
 
    event_loop(display, image1, image2, clock)
 
 
if __name__ == "__main__":
    main()
 
 
# finito

8. Předání struktury do nativních funkcí

Jak jsme mohli vidět v předchozích kapitolách, počet předávaných parametrů do nativních funkcí poměrně rychle narůstá, což vede k několika důsledkům. Zejména se jak zápis nativních funkcí, tak i jejich volání z Pythonu stává poměrně špatně čitelné – kód je „roztažen“ na několik řádků zdrojového kódu atd. A navíc ctypes neprovádí striktní kontroly, zda byly skutečně předány všechny parametry, což může vést k pádům programů, vznikům nekonečných smyček apod.

Jedno z možných (i když jen částečných) řešení tohoto problému spočívá v tom, že některé skupiny parametrů budeme předávat formou struktury, tj. céčku s využitím typu struct.

Například první dva parametry předávané do funkcí render_mandelbrot a render_julia představují rozměry obrázku a proto můžeme namísto nich použít tuto jednoduchou strukturu:

typedef struct {
    unsigned int width;
    unsigned int height;
} image_size_t;
Poznámka: prozatím nebudeme řešit zarovnání prvků ve struktuře. V tomto případě to totiž není nutné.

Zdrojový těchto dvou funkcí se změní následovně (změny jsou opět podtrženy, podobně jako u předchozích příkladů):

void render_mandelbrot(image_size_t image_size,
                       const unsigned char *palette, unsigned char *pixels,
                       unsigned int maxiter) {
    ...
    ...
    ...
    for (y = 0; y < image_size.height; y++) {
        ...
        for (x = 0; x < image_size.width; x++) {
            ...
            ...
            ...
            cx += (xmax - xmin) / image_size.width;
        }
        cy += (ymax - ymin) / image_size.height;
        ...
    }
}
 
 
 
void render_julia(image_size_t image_size,
                  const unsigned char *palette, unsigned char *pixels,
                  unsigned int maxiter, double cx, double cy) {
    ...
    ...
    ...
    for (y = 0; y < image_size.height; y++) {
        zx0 = xmin;
        for (x = 0; x < image_size.width; x++) {
            ...
            ...
            ...
            zx0 += (xmax - xmin) / image_size.width;
        }
        zy0 += (ymax - ymin) / image_size.height;
    }
}

9. Konstrukce struktury na straně skriptu psaného v Pythonu

Nyní nám zbývají vyřešit dva další kroky. Na straně programovacího jazyka Python musíme nějakým způsobem vytvořit hodnotu, která odpovídá nativní céčkové struktuře a následně tuto hodnotu předat do volané nativní funkce. V praxi se jedná o relativně snadno řešitelné kroky. Vytvoříme třídu odvozenou od třídy ctypes.Structure, ve které nadeklarujeme třídní atribut nazvaný _fields_. Tento atribut bude obsahovat seznam dvojic „jméno nativního atributu“+„typ nativního atributu“

class ImageSize(Structure):
    _fields_ = [
        ("width", c_uint),
        ("height", c_uint)
    ]
Poznámka: opět platí, že prozatím nemusíme řešit zarovnání prvků ve struktuře. Pouze musíme dodržet pořadí prvků i jejich datový typ tak, aby to odpovídalo deklaraci struktury v nativním kódu!

Dále zkonstruujeme instanci této třídy, což je snadné (oba předávané parametry jsou typu int/long):

image_size = ImageSize(IMAGE_WIDTH, IMAGE_HEIGHT)

A následně již můžeme tuto instanci použít při volání obou nativních funkcí:

# render Mandelbrot set into buffer
renderer.render_mandelbrot(image_size, pal, buffer, c_int(MAXITER))
...
...
...
# render Julia set into buffer
renderer.render_julia(
    image_size,
    pal,
    buffer,
    c_double(cx),
    c_double(cy),
    c_int(MAXITER),
)

10. Úplný zdrojový kód dnešního třetího demonstračního příkladu: céčková i Pythonní část

Opět se podívejme na úplný zdrojový kód dnešního v pořadí již třetího demonstračního příkladu. Nejdříve začneme s jeho céčkovskou částí:

typedef struct {
    unsigned int width;
    unsigned int height;
} image_size_t;
 
void putpixel(unsigned char **pixel, const unsigned char *palette,
              int color_index) {
    int color_offset = color_index * 3;
    unsigned char *pal = (unsigned char *)(palette + color_offset);
 
    *(*pixel)++ = *pal++;
    *(*pixel)++ = *pal++;
    *(*pixel)++ = *pal;
    (*pixel)++;
}
 
void render_mandelbrot(image_size_t image_size,
                       const unsigned char *palette, unsigned char *pixels,
                       unsigned int maxiter) {
    int x, y;
    double cx, cy;
    double xmin = -2.0, ymin = -1.5, xmax = 1.0, ymax = 1.5;
    unsigned char *p = pixels;
 
    cy = ymin;
    for (y = 0; y < image_size.height; y++) {
        cx = xmin;
        for (x = 0; x < image_size.width; x++) {
            double zx = 0.0;
            double zy = 0.0;
            unsigned int i = 0;
            while (i < maxiter) {
                double zx2 = zx * zx;
                double zy2 = zy * zy;
                if (zx2 + zy2 > 4.0) {
                    break;
                }
                zy = 2.0 * zx * zy + cy;
                zx = zx2 - zy2 + cx;
                i++;
            }
            putpixel(&p, palette, i);
            cx += (xmax - xmin) / image_size.width;
        }
        cy += (ymax - ymin) / image_size.height;
    }
}
 
void render_julia(image_size_t image_size,
                  const unsigned char *palette, unsigned char *pixels,
                  unsigned int maxiter, double cx, double cy) {
    int x, y;
    double zx0, zy0;
    double xmin = -1.5, ymin = -1.5, xmax = 1.5, ymax = 1.5;
    unsigned char *p = pixels;

    zy0 = ymin;
    for (y = 0; y < image_size.height; y++) {
        zx0 = xmin;
        for (x = 0; x < image_size.width; x++) {
            double zx = zx0;
            double zy = zy0;
            unsigned int i = 0;
            while (i < maxiter) {
                double zx2 = zx * zx;
                double zy2 = zy * zy;
                if (zx2 + zy2 > 4.0) {
                    break;
                }
                zy = 2.0 * zx * zy + cy;
                zx = zx2 - zy2 + cx;
                i++;
            }
            putpixel(&p, palette, i);
            zx0 += (xmax - xmin) / image_size.width;
        }
        zy0 += (ymax - ymin) / image_size.height;
    }
}

Následuje část naprogramovaná v Pythonu, ze které se volají výše uvedené funkce render_mandelbrot a render_julia:

import sys
from ctypes import (
    CDLL,
    c_double,
    c_int,
    c_uint,
    create_string_buffer,
    Structure,
)
 
from palette_mandmap import palette
 
import pygame
import pygame.locals
 
TITLE = "Renderer"
SCREEN_WIDTH = 600
SCREEN_HEIGHT = 300
IMAGE_WIDTH = 256
IMAGE_HEIGHT = 256
 
MAXITER = 150
 
 
def initialize_ui(title, width, height):
    """Initialize Pygame display, drawing surface, and clocks."""
    # set window title
    pygame.display.set_caption(title)
 
    # initialize window
    display = pygame.display.set_mode([width, height])
    display.fill((0, 0, 0))
 
    clock = pygame.time.Clock()
 
    return display, clock
 
 
def palette_to_buffer(p):
    s = create_string_buffer(len(p) * 3)
    i = 0
    for color in p:
        s[i] = color[0]
        s[i + 1] = color[1]
        s[i + 2] = color[2]
        i += 3
    return s
 
 
def event_loop(display, image1, image2, clock):
    while True:
        for event in pygame.event.get():
            if event.type == pygame.locals.QUIT:
                pygame.quit()
                sys.exit()
            if event.type == pygame.locals.KEYDOWN:
                if event.key == pygame.locals.K_ESCAPE:
                    pygame.quit()
                    sys.exit()
 
        # all events has been processed - update scene and redraw the screen
        display.blit(image1, (30, 20))
        display.blit(image2, (60 + image1.get_width(), 20))
 
        # and update the whole display
        pygame.display.update()
        clock.tick(25)
 
 
def image_from_buffer(buffer, width, height, fmt):
    return pygame.image.frombytes(bytes(buffer), (width, height), fmt)
 
 
class ImageSize(Structure):
    _fields_ = [("width", c_uint), ("height", c_uint)]
 
 
def main():
    image_size = ImageSize(IMAGE_WIDTH, IMAGE_HEIGHT)
    pal = palette_to_buffer(palette)
 
    display, clock = initialize_ui(TITLE, SCREEN_WIDTH, SCREEN_HEIGHT)
 
    # try to load dynamically linked library
    renderer = CDLL("./renderer.so")
 
    # create buffer for raster image
    buffer = create_string_buffer(4 * IMAGE_WIDTH * IMAGE_HEIGHT)
 
    # render Mandelbrot set into buffer
    renderer.render_mandelbrot(image_size, pal, buffer, c_int(MAXITER))
    image1 = image_from_buffer(buffer, IMAGE_WIDTH, IMAGE_HEIGHT, "RGBX")
 
    cx = -0.171119200000000013445
    cy = 0.657309400000000000000
 
    # render Julia set into buffer
    renderer.render_julia(
        image_size,
        pal,
        buffer,
        c_double(cx),
        c_double(cy),
        c_int(MAXITER),
    )
    image2 = image_from_buffer(buffer, IMAGE_WIDTH, IMAGE_HEIGHT, "RGBX")
 
    event_loop(display, image1, image2, clock)
 
 
if __name__ == "__main__":
    main()
 
 
# finito

11. Nativní funkce akceptující ukazatel na strukturu

Struktura, kterou do funkcí naprogramovaných v jazyku C předáváme, je prozatím velmi malá, protože obsahuje pouze dvě hodnoty typu unsigned int:

typedef struct {
    unsigned int width;
    unsigned int height;
} image_size_t;

Ovšem v případě, že by se do struktury přidávaly další prvky, je na čase se ptát, zda nebude výhodnější nepředávat celou strukturu hodnotou, ale odkazem (tedy přes ukazatel). V takovém případě se samotné funkce změní jen nepatrně – modifikuje se pochopitelně jejich hlavička a přístup k prvkům struktury bude realizován operátorem → a nikoli operátorem tečky (větší je sémantický rozdíl – nyní je struktura měnitelná):

void render_mandelbrot(image_size_t *image_size,
                       const unsigned char *palette, unsigned char *pixels,
                       unsigned int maxiter) {
    ...
    ...
    ...
    for (y = 0; y < image_size->height; y++) {
        ...
        for (x = 0; x < image_size->width; x++) {
            ...
            ...
            ...
            cx += (xmax - xmin) / image_size->width;
        }
        ...
        cy += (ymax - ymin) / image_size->height;
    }
}

12. Předání ukazatele na strukturu z Pythonu do nativních funkcí

Pokud nativní funkce volané z Pythonu vyžadují jako některý svůj parametr ukazatel na strukturu, musíme být schopní takový ukazatel získat přímo v Pythonu. Celý postup je ve skutečnosti poměrně jednoduchý.

Nejprve musíme naimportovat mj. i funkci pointer z modulu ctypes:

from ctypes import (
    CDLL,
    c_double,
    c_int,
    c_uint,
    create_string_buffer,
    Structure,
    pointer,
)

Samotná definice třídy představující strukturu se nijak nemění:

class ImageSize(Structure):
    _fields_ = [("width", c_uint), ("height", c_uint)]

Totéž platí i pro získání instance této třídy:

image_size = ImageSize(IMAGE_WIDTH, IMAGE_HEIGHT)

Ovšem při volání nativních funkcí předáváme ukazatel – viz podtržená část kódu:

pal = palette_to_buffer(palette)
 
# try to load dynamically linked library
renderer = CDLL("./renderer.so")
 
# create buffer for raster image
buffer = create_string_buffer(4 * IMAGE_WIDTH * IMAGE_HEIGHT)
 
# render Mandelbrot set into buffer
renderer.render_mandelbrot(pointer(image_size), pal, buffer, c_int(MAXITER))

Naprosto totéž provedeme i při volání nativní funkce pro výpočet a vykreslení Juliovy množiny:

cx = -0.171119200000000013445
cy = 0.657309400000000000000
 
# render Julia set into buffer
renderer.render_julia(
    pointer(image_size),
    pal,
    buffer,
    c_double(cx),
    c_double(cy),
    c_int(MAXITER),
)

13. Úplný zdrojový kód dnešního čtvrtého demonstračního příkladu: céčková i Pythonní část

Dnešní třetí demonstrační příklad se změnami popsanými v předchozí dvojici kapitol, je opět rozdělený na céčkovou a Pythonní část. Céčková část vypadá takto:

typedef struct {
    unsigned int width;
    unsigned int height;
} image_size_t;
 
void putpixel(unsigned char **pixel, const unsigned char *palette,
              int color_index) {
    int color_offset = color_index * 3;
    unsigned char *pal = (unsigned char *)(palette + color_offset);

    *(*pixel)++ = *pal++;
    *(*pixel)++ = *pal++;
    *(*pixel)++ = *pal;
    (*pixel)++;
}
 
void render_mandelbrot(image_size_t *image_size,
                       const unsigned char *palette, unsigned char *pixels,
                       unsigned int maxiter) {
    int x, y;
    double cx, cy;
    double xmin = -2.0, ymin = -1.5, xmax = 1.0, ymax = 1.5;
    unsigned char *p = pixels;
 
    cy = ymin;
    for (y = 0; y < image_size->height; y++) {
        cx = xmin;
        for (x = 0; x < image_size->width; x++) {
            double zx = 0.0;
            double zy = 0.0;
            unsigned int i = 0;
            while (i < maxiter) {
                double zx2 = zx * zx;
                double zy2 = zy * zy;
                if (zx2 + zy2 > 4.0) {
                    break;
                }
                zy = 2.0 * zx * zy + cy;
                zx = zx2 - zy2 + cx;
                i++;
            }
            putpixel(&p, palette, i);
            cx += (xmax - xmin) / image_size->width;
        }
        cy += (ymax - ymin) / image_size->height;
    }
}
 
void render_julia(image_size_t *image_size,
                  const unsigned char *palette, unsigned char *pixels,
                  unsigned int maxiter, double cx, double cy) {
    int x, y;
    double zx0, zy0;
    double xmin = -1.5, ymin = -1.5, xmax = 1.5, ymax = 1.5;
    unsigned char *p = pixels;
 
    zy0 = ymin;
    for (y = 0; y < image_size->height; y++) {
        zx0 = xmin;
        for (x = 0; x < image_size->width; x++) {
            double zx = zx0;
            double zy = zy0;
            unsigned int i = 0;
            while (i < maxiter) {
                double zx2 = zx * zx;
                double zy2 = zy * zy;
                if (zx2 + zy2 > 4.0) {
                    break;
                }
                zy = 2.0 * zx * zy + cy;
                zx = zx2 - zy2 + cx;
                i++;
            }
            putpixel(&p, palette, i);
            zx0 += (xmax - xmin) / image_size->width;
        }
        zy0 += (ymax - ymin) / image_size->height;
    }
}

Následuje část naprogramovaná v Pythonu, ze které se volají nativní funkce:

import sys
from ctypes import (
    CDLL,
    c_double,
    c_int,
    c_uint,
    create_string_buffer,
    Structure,
    pointer,
)
 
from palette_mandmap import palette
 
import pygame
import pygame.locals
 
TITLE = "Renderer"
SCREEN_WIDTH = 600
SCREEN_HEIGHT = 300
IMAGE_WIDTH = 256
IMAGE_HEIGHT = 256
 
MAXITER = 150
 
 
def initialize_ui(title, width, height):
    """Initialize Pygame display, drawing surface, and clocks."""
    # set window title
    pygame.display.set_caption(title)
 
    # initialize window
    display = pygame.display.set_mode([width, height])
    display.fill((0, 0, 0))
 
    clock = pygame.time.Clock()
 
    return display, clock
 
 
def palette_to_buffer(p):
    s = create_string_buffer(len(p) * 3)
    i = 0
    for color in p:
        s[i] = color[0]
        s[i + 1] = color[1]
        s[i + 2] = color[2]
        i += 3
    return s
 
 
def event_loop(display, image1, image2, clock):
    while True:
        for event in pygame.event.get():
            if event.type == pygame.locals.QUIT:
                pygame.quit()
                sys.exit()
            if event.type == pygame.locals.KEYDOWN:
                if event.key == pygame.locals.K_ESCAPE:
                    pygame.quit()
                    sys.exit()
 
        # all events has been processed - update scene and redraw the screen
        display.blit(image1, (30, 20))
        display.blit(image2, (60 + image1.get_width(), 20))
 
        # and update the whole display
        pygame.display.update()
        clock.tick(25)
 
 
def image_from_buffer(buffer, width, height, fmt):
    return pygame.image.frombytes(bytes(buffer), (width, height), fmt)
 
 
class ImageSize(Structure):
    _fields_ = [("width", c_uint), ("height", c_uint)]
 
 
def main():
    image_size = ImageSize(IMAGE_WIDTH, IMAGE_HEIGHT)
    pal = palette_to_buffer(palette)
 
    display, clock = initialize_ui(TITLE, SCREEN_WIDTH, SCREEN_HEIGHT)
 
    # try to load dynamically linked library
    renderer = CDLL("./renderer.so")
 
    # create buffer for raster image
    buffer = create_string_buffer(4 * IMAGE_WIDTH * IMAGE_HEIGHT)
 
    # render Mandelbrot set into buffer
    renderer.render_mandelbrot(pointer(image_size), pal, buffer, c_int(MAXITER))
    image1 = image_from_buffer(buffer, IMAGE_WIDTH, IMAGE_HEIGHT, "RGBX")
 
    cx = -0.171119200000000013445
    cy = 0.657309400000000000000
 
    # render Julia set into buffer
    renderer.render_julia(
        pointer(image_size),
        pal,
        buffer,
        c_double(cx),
        c_double(cy),
        c_int(MAXITER),
    )
    image2 = image_from_buffer(buffer, IMAGE_WIDTH, IMAGE_HEIGHT, "RGBX")
 
    event_loop(display, image1, image2, clock)
 
 
if __name__ == "__main__":
    main()
 
 
# finito

14. Struktura s hodnotami použitými při výpočtech fraktálů

Výpočet Mandelbrotovy množiny a Juliových množin je možné ovlivnit různými parametry. V první řadě se jedná o údaj o maximálním počtu iterací a u Juliových množin taktéž o hodnotu komplexní konstanty c. Tyto parametry jsme prozatím předávali samostatně: jako maxiter, cx a cy (pojmenování reálné a imaginární složky komplexní konstanty c). Ovšem nic nám nebrání v tom, abychom tyto parametry předali společně v jediné (později rozšiřitelné) struktuře. Její tvar může být následující:

typedef struct {
    unsigned int maxiter;
    double cx;
    double cy;
} rendering_params_t;

Tuto strukturu můžeme předat referencí, tedy přes ukazatel. Samotné výpočty se změní jen nepatrně:

void render_mandelbrot(image_size_t *image_size,
                       const unsigned char *palette, unsigned char *pixels,
                       rendering_params_t *rendering_params) {
    ...
    ...
    ...
    while (i < rendering_params->maxiter) {
        ...
        ...
        ...
    }
    ...
    ...
    ...
}

a:

void render_julia(image_size_t *image_size,
                  const unsigned char *palette, unsigned char *pixels,
                  rendering_params_t *rendering_params) {
    ...
    ...
    ...
    while (i < rendering_params->maxiter) {
        ...
        ...
        ...
        zy = 2.0 * zx * zy + rendering_params->cy;
        zx = zx2 - zy2 + rendering_params->cx;
        ...
        ...
        ...
    }
    ...
    ...
    ...
}

Na straně Pythonu vytvoříme příslušnou třídu, která bude strukturu reprezentovat:

class RenderingParams(Structure):
    _fields_ = [("maxiter", c_uint), ("cx", c_double), ("cy", c_double)]

Inicializace a předání přes ukazatel bude vypadat následovně:

rendering_params = RenderingParams(MAXITER, cx, cy)
...
...
...
# try to load dynamically linked library
renderer = CDLL("./renderer.so")
...
...
...
# render Mandelbrot set into buffer
renderer.render_mandelbrot(pointer(image_size), pal, buffer, pointer(rendering_params))
...
...
...
# render Julia set into buffer
renderer.render_julia(pointer(image_size), pal, buffer, pointer(rendering_params))
...
...
...
# finito

15. Úplný zdrojový kód dnešního pátého demonstračního příkladu: céčková i Pythonní část

Dnešní pátý demonstrační příklad se změnami popsanými v předchozí kapitole je opět rozdělený na céčkovou a Pythonní část. Céčková část vypadá takto (povšimněte si definice dvou struktur na jeho začátku):

typedef struct {
    unsigned int width;
    unsigned int height;
} image_size_t;
 
typedef struct {
    unsigned int maxiter;
    double cx;
    double cy;
} rendering_params_t;
 
void putpixel(unsigned char **pixel, const unsigned char *palette,
              int color_index) {
    int color_offset = color_index * 3;
    unsigned char *pal = (unsigned char *)(palette + color_offset);

    *(*pixel)++ = *pal++;
    *(*pixel)++ = *pal++;
    *(*pixel)++ = *pal;
    (*pixel)++;
}
 
void render_mandelbrot(image_size_t *image_size,
                       const unsigned char *palette, unsigned char *pixels,
                       rendering_params_t *rendering_params) {
    int x, y;
    double cx, cy;
    double xmin = -2.0, ymin = -1.5, xmax = 1.0, ymax = 1.5;
    unsigned char *p = pixels;
 
    cy = ymin;
    for (y = 0; y < image_size->height; y++) {
        cx = xmin;
        for (x = 0; x < image_size->width; x++) {
            double zx = 0.0;
            double zy = 0.0;
            unsigned int i = 0;
            while (i < rendering_params->maxiter) {
                double zx2 = zx * zx;
                double zy2 = zy * zy;
                if (zx2 + zy2 > 4.0) {
                    break;
                }
                zy = 2.0 * zx * zy + cy;
                zx = zx2 - zy2 + cx;
                i++;
            }
            putpixel(&p, palette, i);
            cx += (xmax - xmin) / image_size->width;
        }
        cy += (ymax - ymin) / image_size->height;
    }
}
 
void render_julia(image_size_t *image_size,
                  const unsigned char *palette, unsigned char *pixels,
                  rendering_params_t *rendering_params) {
    int x, y;
    double zx0, zy0;
    double xmin = -1.5, ymin = -1.5, xmax = 1.5, ymax = 1.5;
    unsigned char *p = pixels;
 
    zy0 = ymin;
    for (y = 0; y < image_size->height; y++) {
        zx0 = xmin;
        for (x = 0; x < image_size->width; x++) {
            double zx = zx0;
            double zy = zy0;
            unsigned int i = 0;
            while (i < rendering_params->maxiter) {
                double zx2 = zx * zx;
                double zy2 = zy * zy;
                if (zx2 + zy2 > 4.0) {
                    break;
                }
                zy = 2.0 * zx * zy + rendering_params->cy;
                zx = zx2 - zy2 + rendering_params->cx;
                i++;
            }
            putpixel(&p, palette, i);
            zx0 += (xmax - xmin) / image_size->width;
        }
        zy0 += (ymax - ymin) / image_size->height;
    }
}

Část naprogramovaná v Pythonu má tento tvar:

import sys
from ctypes import (
    CDLL,
    c_double,
    c_int,
    c_uint,
    create_string_buffer,
    Structure,
    pointer,
)
 
from palette_mandmap import palette
 
import pygame
import pygame.locals
 
TITLE = "Renderer"
SCREEN_WIDTH = 600
SCREEN_HEIGHT = 300
IMAGE_WIDTH = 256
IMAGE_HEIGHT = 256
 
MAXITER = 150
 
 
def initialize_ui(title, width, height):
    """Initialize Pygame display, drawing surface, and clocks."""
    # set window title
    pygame.display.set_caption(title)
 
    # initialize window
    display = pygame.display.set_mode([width, height])
    display.fill((0, 0, 0))
 
    clock = pygame.time.Clock()
 
    return display, clock
 
 
def palette_to_buffer(p):
    s = create_string_buffer(len(p) * 3)
    i = 0
    for color in p:
        s[i] = color[0]
        s[i + 1] = color[1]
        s[i + 2] = color[2]
        i += 3
    return s
 
 
def event_loop(display, image1, image2, clock):
    while True:
        for event in pygame.event.get():
            if event.type == pygame.locals.QUIT:
                pygame.quit()
                sys.exit()
            if event.type == pygame.locals.KEYDOWN:
                if event.key == pygame.locals.K_ESCAPE:
                    pygame.quit()
                    sys.exit()
 
        # all events has been processed - update scene and redraw the screen
        display.blit(image1, (30, 20))
        display.blit(image2, (60 + image1.get_width(), 20))
 
        # and update the whole display
        pygame.display.update()
        clock.tick(25)
 
 
def image_from_buffer(buffer, width, height, fmt):
    return pygame.image.frombytes(bytes(buffer), (width, height), fmt)
 
 
class ImageSize(Structure):
    _fields_ = [("width", c_uint), ("height", c_uint)]
 
 
class RenderingParams(Structure):
    _fields_ = [("maxiter", c_uint), ("cx", c_double), ("cy", c_double)]
 
 
def main():
    image_size = ImageSize(IMAGE_WIDTH, IMAGE_HEIGHT)
 
    cx = -0.171119200000000013445
    cy = 0.657309400000000000000
    rendering_params = RenderingParams(MAXITER, cx, cy)
 
    pal = palette_to_buffer(palette)
 
    display, clock = initialize_ui(TITLE, SCREEN_WIDTH, SCREEN_HEIGHT)
 
    # try to load dynamically linked library
    renderer = CDLL("./renderer.so")
 
    # create buffer for raster image
    buffer = create_string_buffer(4 * IMAGE_WIDTH * IMAGE_HEIGHT)
 
    # render Mandelbrot set into buffer
    renderer.render_mandelbrot(
        pointer(image_size), pal, buffer, pointer(rendering_params)
    )
    image1 = image_from_buffer(buffer, IMAGE_WIDTH, IMAGE_HEIGHT, "RGBX")
 
    # render Julia set into buffer
    renderer.render_julia(pointer(image_size), pal, buffer, pointer(rendering_params))
    image2 = image_from_buffer(buffer, IMAGE_WIDTH, IMAGE_HEIGHT, "RGBX")
 
    event_loop(display, image1, image2, clock)
 
 
if __name__ == "__main__":
    main()
 
 
# finito

16. Správa paměti

Prozatím jsme si ukázali tu nejjednodušší formu komunikace mezi skriptem psaným v Pythonu a nativními funkcemi. Tato komunikace spočívala v tom, že veškeré objekty byly alokovány na straně Pythonu a následně byly předávány do volaných nativních funkcí. Mohlo by se tedy zdát, že se vůbec nemusíme starat o správu paměti. V praxi je to samozřejmě mnohem komplikovanější a mohou nastat situace, kdy je nutné, aby paměť byla alokována na straně céčkovských funkcí (a vrácena přes ukazatel) nebo aby se prováděla explicitní dealokace paměti pro objekty vytvořené jak na straně Pythonu, tak i na straně céčka. To celou komunikaci skutečně komplikuje a vlastně tak do Pythonu (částečně) zavádíme některé postupy, které zde nebylo nutné realizovat. V navazující kapitole si ukážeme některé nekorektní operace, zejména pokus o dealokaci objektů (provedené v céčkových funkcích), které nejsou dealokovatelné nebo jsou naopak explicitně dealokovány na straně Pythonu. Později si ukážeme jedno možné řešení tohoto problému, které ovšem není ani zdaleka ideální.

17. Ukázka nekorektní práce s pamětí

Nativní funkce, které byly použity v předchozích demonstračních příkladech, nyní upravíme, a to konkrétně do takové podoby, aby se z volaly standardní funkce free s předáním ukazatelů na obě datové struktury (image_size_t * a rendering_params_t *). To je pochopitelně zcela nekorektní chování:

#include <stdlib.h>
 
typedef struct {
    unsigned int width;
    unsigned int height;
} image_size_t;
 
typedef struct {
    unsigned int maxiter;
    double cx;
    double cy;
} rendering_params_t;
 
 
void render_mandelbrot(image_size_t *image_size,
                       const unsigned char *palette, unsigned char *pixels,
                       rendering_params_t *rendering_params) {
    free(image_size);
    free(rendering_params);
}
 
void render_julia(image_size_t *image_size,
                  const unsigned char *palette, unsigned char *pixels,
                  rendering_params_t *rendering_params) {
    free(image_size);
    free(rendering_params);
}

Bude zajímavé zjistit, jak se bude aplikace chovat, pokud obě funkce zavoláme z Pythonu a předáme jim ukazatele na struktury:

pygame 2.6.1 (SDL 2.28.4, Python 3.12.10)
Hello from the pygame community. https://www.pygame.org/contribute.html
free(): invalid pointer
 
Command terminated

Nekorektní chování – pokus o uvolnění paměti na zásobníku – byl odhalen a aplikace byla ukončena.

Taktéž nebude dovolena dvojí dealokace paměti s bitmapou:

#include <stdlib.h>
 
typedef struct {
    unsigned int width;
    unsigned int height;
} image_size_t;
 
typedef struct {
    unsigned int maxiter;
    double cx;
    double cy;
} rendering_params_t;
 
 
void render_mandelbrot(image_size_t *image_size,
                       const unsigned char *palette, unsigned char *pixels,
                       rendering_params_t *rendering_params) {
    free(pixels);
}

void render_julia(image_size_t *image_size,
                  const unsigned char *palette, unsigned char *pixels,
                  rendering_params_t *rendering_params) {
    free(pixels);
}

Chyba bude v tomto případě odlišná, ale opět je snadno detekovatelná standardními knihovnami a jejich runtime systémem:

pygame 2.6.1 (SDL 2.28.4, Python 3.12.10)
Hello from the pygame community. https://www.pygame.org/contribute.html
double free or corruption (!prev)
 
Command terminated

18. Zarovnání a výplně ve strukturách

V praxi při realizaci komunikace mezi programem psaným v Pythonu a nativními funkcemi psanými (zejména) v jazyku C narazíme ještě na jeden problém: jak zajistit korektní inicializaci struktury, ve které jsou prvky různým způsobem zarovnány a kam jsou popřípadě vloženy nějaké výplně. Prozatím jsme na tento problém nenarazili, protože knihovna ctypes počítá s tím, že u céčkových struktur budou zachována výchozí pravidla pro zarovnání/výplně. Ovšem v praxi můžeme narazit i na struktury, ve kterých je zarovnání ovlivněno programátorem (a to ještě nepočítáme s existencí bitových polí). V tomto případě se pochopitelně musí Python nové struktuře s jinou velikostí a jinými offsety prvků přizpůsobit. Jak toho je možné dosáhnout, si vysvětlíme v samostatném (již mnohem stručnějším) článku.

docker + kubernetes školení s dotací tip

19. Repositář s demonstračními příklady

V minulém i dnešním článku popsané demonstrační příklady naleznete na GitHubu:

# Příklad Stručný popis Adresa
  1 Vyplnění rastrových obrázků vzorkem přímo z Pythonu  
1 show.py Vyplnění rastrových obrázků vzorkem přímo z Pythonu https://github.com/tisnik/most-popular-python-libs/blob/master/ctypes/show.py
       
  2 Zavolání nativní funkce pro vyplnění obrázku vzorkem  
1 Makefile Makefile pro překlad kódu renderer.c do dynamicky sdílené knihovny https://github.com/tisnik/most-popular-python-libs/blob/master/ctypes/e­xample1/Makefile
2 renderer.c https://github.com/tisnik/most-popular-python-libs/blob/master/ctypes/e­xample1/renderer.c
3 show.py zobrazení dvojice testovacích obrázků https://github.com/tisnik/most-popular-python-libs/blob/master/ctypes/e­xample1/show.py
       
  3 Vyplnění rastrového obrázku s využitím barvové palety  
5 Makefile Makefile pro překlad kódu renderer.c do dynamicky sdílené knihovny https://github.com/tisnik/most-popular-python-libs/blob/master/ctypes/e­xample2/Makefile
5 palette_mandmap.py definice barvové palety s 256 barvami https://github.com/tisnik/most-popular-python-libs/blob/master/ctypes/e­xample2/palette_mandmap.py
6 renderer.c vyplnění rastrového obrázku s využitím barvové palety https://github.com/tisnik/most-popular-python-libs/blob/master/ctypes/e­xample2/renderer.c
7 show.py zobrazení testovacího obrázku s paletou https://github.com/tisnik/most-popular-python-libs/blob/master/ctypes/e­xample2/show.py
       
  4 Výpočet Mandelbrotovy a Juliovy množiny implementovaný v jazyku C  
8 Makefile Makefile pro překlad kódu renderer.c do dynamicky sdílené knihovny https://github.com/tisnik/most-popular-python-libs/blob/master/ctypes/e­xample3/Makefile
9 palette_mandmap.py definice barvové palety s 256 barvami https://github.com/tisnik/most-popular-python-libs/blob/master/ctypes/e­xample3/palette_mandmap.py
10 renderer.c výpočet Mandelbrotovy a Juliovy množiny implementovaný v C https://github.com/tisnik/most-popular-python-libs/blob/master/ctypes/e­xample3/renderer.c
11 show.py realizace výpočtu a zobrazení Mandelbrotovy a Juliovy množiny https://github.com/tisnik/most-popular-python-libs/blob/master/ctypes/e­xample3/show.py
       
  5 Interaktivní změna tvaru Juliovy množiny  
12 Makefile Makefile pro překlad kódu renderer.c do dynamicky sdílené knihovny https://github.com/tisnik/most-popular-python-libs/blob/master/ctypes/e­xample4/Makefile
13 palette_mandmap.py definice barvové palety s 256 barvami https://github.com/tisnik/most-popular-python-libs/blob/master/ctypes/e­xample4/palette_mandmap.py
14 renderer.c výpočet Mandelbrotovy a Juliovy množiny implementovaný v C https://github.com/tisnik/most-popular-python-libs/blob/master/ctypes/e­xample4/renderer.c
15 show.py interaktivní změna hodnoty C při výpočtu Juliovy množiny https://github.com/tisnik/most-popular-python-libs/blob/master/ctypes/e­xample4/show.py
       
  6 Využití jednoho (sdíleného) bufferu pro oba obrázky  
16 Makefile Makefile pro překlad kódu renderer.c do dynamicky sdílené knihovny https://github.com/tisnik/most-popular-python-libs/blob/master/ctypes/e­xample5/Makefile
17 palette_mandmap.py definice barvové palety s 256 barvami https://github.com/tisnik/most-popular-python-libs/blob/master/ctypes/e­xample5/palette_mandmap.py
18 renderer.c výpočet Mandelbrotovy a Juliovy množiny implementovaný v C https://github.com/tisnik/most-popular-python-libs/blob/master/ctypes/e­xample5/renderer.c
19 show.py využití jednoho (sdíleného) bufferu https://github.com/tisnik/most-popular-python-libs/blob/master/ctypes/e­xample5/show.py
       
  7 Využití dvojice bufferů, jeden buffer pro každý obrázek  
20 Makefile Makefile pro překlad kódu renderer.c do dynamicky sdílené knihovny https://github.com/tisnik/most-popular-python-libs/blob/master/ctypes/e­xample6/Makefile
21 palette_mandmap.py definice barvové palety s 256 barvami https://github.com/tisnik/most-popular-python-libs/blob/master/ctypes/e­xample6/palette_mandmap.py
22 renderer.c výpočet Mandelbrotovy a Juliovy množiny implementovaný v C https://github.com/tisnik/most-popular-python-libs/blob/master/ctypes/e­xample6/renderer.c
23 show.py využití dvojice rozličných bufferů https://github.com/tisnik/most-popular-python-libs/blob/master/ctypes/e­xample6/show.py
       
  8 Přístup k pixelům obrázku po 32bitových slovech  
24 Makefile Makefile pro překlad kódu renderer.c do dynamicky sdílené knihovny https://github.com/tisnik/most-popular-python-libs/blob/master/ctypes/e­xample7/Makefile
25 palette_mandmap.py definice barvové palety s 256 barvami https://github.com/tisnik/most-popular-python-libs/blob/master/ctypes/e­xample7/palette_mandmap.py
26 renderer.c výpočet Mandelbrotovy a Juliovy množiny implementovaný v C https://github.com/tisnik/most-popular-python-libs/blob/master/ctypes/e­xample7/renderer.c
27 show.py využití dvojice rozličných bufferů https://github.com/tisnik/most-popular-python-libs/blob/master/ctypes/e­xample7/show.py
       
28 example8/Makefile Makefile pro překlad kódu renderer.c do dynamicky sdílené knihovny https://github.com/tisnik/most-popular-python-libs/blob/master/ctypes/e­xample8/Makefile
29 example8/palette_mandmap.py definice barvové palety s 256 barvami https://github.com/tisnik/most-popular-python-libs/blob/master/ctypes/e­xample8/palette_mandmap.py
30 example8/renderer.c výpočet Mandelbrotovy a Juliovy množiny implementovaný v C https://github.com/tisnik/most-popular-python-libs/blob/master/ctypes/e­xample8/renderer.c
31 example8/show.py volání funkcí render_mandelbrot a render_julia s předáním všech potřebných parametrů https://github.com/tisnik/most-popular-python-libs/blob/master/ctypes/e­xample8/show.py
       
28 example9/Makefile Makefile pro překlad kódu renderer.c do dynamicky sdílené knihovny https://github.com/tisnik/most-popular-python-libs/blob/master/ctypes/e­xample9/Makefile
29 example9/palette_mandmap.py definice barvové palety s 256 barvami https://github.com/tisnik/most-popular-python-libs/blob/master/ctypes/e­xample9/palette_mandmap.py
30 example9/renderer.c úprava předchozího příkladu: předání maximálního počtu iterací https://github.com/tisnik/most-popular-python-libs/blob/master/ctypes/e­xample9/renderer.c
31 example9/show.py volání funkcí render_mandelbrot a render_julia s předáním všech potřebných parametrů https://github.com/tisnik/most-popular-python-libs/blob/master/ctypes/e­xample9/show.py
       
28 exampleA/Makefile Makefile pro překlad kódu renderer.c do dynamicky sdílené knihovny https://github.com/tisnik/most-popular-python-libs/blob/master/ctypes/e­xampleA/Makefile
29 exampleA/palette_mandmap.py definice barvové palety s 256 barvami https://github.com/tisnik/most-popular-python-libs/blob/master/ctypes/e­xampleA/palette_mandmap.py
30 exampleA/renderer.c úprava předchozího příkladu: předání velikosti obrázku formou struktury https://github.com/tisnik/most-popular-python-libs/blob/master/ctypes/e­xampleA/renderer.c
31 exampleA/show.py volání funkcí render_mandelbrot a render_julia s předáním všech potřebných parametrů https://github.com/tisnik/most-popular-python-libs/blob/master/ctypes/e­xampleA/show.py
       
28 exampleB/Makefile Makefile pro překlad kódu renderer.c do dynamicky sdílené knihovny https://github.com/tisnik/most-popular-python-libs/blob/master/ctypes/e­xampleB/Makefile
29 exampleB/palette_mandmap.py definice barvové palety s 256 barvami https://github.com/tisnik/most-popular-python-libs/blob/master/ctypes/e­xampleB/palette_mandmap.py
30 exampleB/renderer.c úprava předchozího příkladu: předání velikosti obrázku formou ukazatele na strukturu https://github.com/tisnik/most-popular-python-libs/blob/master/ctypes/e­xampleB/renderer.c
31 exampleB/show.py volání funkcí render_mandelbrot a render_julia s předáním všech potřebných parametrů https://github.com/tisnik/most-popular-python-libs/blob/master/ctypes/e­xampleB/show.py
       
28 exampleC/Makefile Makefile pro překlad kódu renderer.c do dynamicky sdílené knihovny https://github.com/tisnik/most-popular-python-libs/blob/master/ctypes/e­xampleC/Makefile
29 exampleC/palette_mandmap.py definice barvové palety s 256 barvami https://github.com/tisnik/most-popular-python-libs/blob/master/ctypes/e­xampleC/palette_mandmap.py
30 exampleC/renderer.c úprava předchozího příkladu: předání parametrů pro výpočet formou ukazatele na strukturu https://github.com/tisnik/most-popular-python-libs/blob/master/ctypes/e­xampleC/renderer.c
31 exampleC/show.py volání funkcí render_mandelbrot a render_julia s předáním všech potřebných parametrů https://github.com/tisnik/most-popular-python-libs/blob/master/ctypes/e­xampleC/show.py
       
32 exampleD/Makefile Makefile pro překlad kódu renderer.c do dynamicky sdílené knihovny https://github.com/tisnik/most-popular-python-libs/blob/master/ctypes/e­xampleD/Makefile
33 exampleD/palette_mandmap.py definice barvové palety s 256 barvami https://github.com/tisnik/most-popular-python-libs/blob/master/ctypes/e­xampleD/palette_mandmap.py
34 exampleD/renderer.c pokus o uvolnění paměti s předanými strukturami https://github.com/tisnik/most-popular-python-libs/blob/master/ctypes/e­xampleD/renderer.c
35 exampleD/show.py volání funkcí render_mandelbrot a render_julia s předáním všech potřebných parametrů https://github.com/tisnik/most-popular-python-libs/blob/master/ctypes/e­xampleD/show.py
       
36 exampleE/Makefile Makefile pro překlad kódu renderer.c do dynamicky sdílené knihovny https://github.com/tisnik/most-popular-python-libs/blob/master/ctypes/e­xampleE/Makefile
37 exampleE/palette_mandmap.py definice barvové palety s 256 barvami https://github.com/tisnik/most-popular-python-libs/blob/master/ctypes/e­xampleE/palette_mandmap.py
38 exampleE/renderer.c pokus o uvolnění paměti s předanou bitmapou https://github.com/tisnik/most-popular-python-libs/blob/master/ctypes/e­xampleE/renderer.c
39 exampleE/show.py volání funkcí render_mandelbrot a render_julia s předáním všech potřebných parametrů https://github.com/tisnik/most-popular-python-libs/blob/master/ctypes/e­xampleE/show.py

Navíc si pro úplnost uveďme demonstrační příklady použité v článcích o knihovně cffi. I v těchto článcích jsme se totiž o ctypes zmiňovali:

# 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/ca­ll_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/ca­ll_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/ca­ll_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/ca­ll_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/ca­ll_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/ca­ll_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/ca­ll_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/ca­ll_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/ma­ke_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/gre­eter/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/gre­eter/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/gre­eter/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/gre­eter/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/gre­eter/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/gre­eter/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/gre­eter/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/gre­eter/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/gre­eter/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/gre­eter/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/swap­per/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/swap­per/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/swap­per/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/swap­per/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/swap­per/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/ca­ll_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/ca­ll_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/ma­ke_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
       
33 greeter_h/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/gre­eter_h/greeter.c
34 greeter_h/greeter.h prototyp (předběžná deklarace) funkce greeter https://github.com/tisnik/most-popular-python-libs/blob/master/cffi/gre­eter_h/greeter.h
35 greeter_h/call_via_cffi4.py zavolání céčkovské knihovny z jazyka Python https://github.com/tisnik/most-popular-python-libs/blob/master/cffi/gre­eter_h/call_via_cffi4.py
36 greeter_h/call_via_cffi.sh nastavení cest a spuštění Pythonovského skriptu https://github.com/tisnik/most-popular-python-libs/blob/master/cffi/gre­eter_h/call_via_cffi.sh
37 greeter_h/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/gre­eter_h/make_library.sh
38 greeter_h/clean.sh skript pro smazání objektového souboru i dynamicky linkované knihovny https://github.com/tisnik/most-popular-python-libs/blob/master/cffi/gre­eter_h/clean.sh
       
39 greeter_h2/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/gre­eter_h2/greeter.c
40 greeter_h2/greeter.h prototyp (předběžná deklarace) funkce greeter obalená v testu na existenci symbolu/makra https://github.com/tisnik/most-popular-python-libs/blob/master/cffi/gre­eter_h2/greeter.h
41 greeter_h2/call_via_cffi5.py zavolání céčkovské knihovny z jazyka Python https://github.com/tisnik/most-popular-python-libs/blob/master/cffi/gre­eter_h2/call_via_cffi5.py
42 greeter_h2/call_via_cffi.sh nastavení cest a spuštění Pythonovského skriptu https://github.com/tisnik/most-popular-python-libs/blob/master/cffi/gre­eter_h2/call_via_cffi.sh
43 greeter_h2/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/gre­eter_h2/make_library.sh
44 greeter_h2/clean.sh skript pro smazání objektového souboru i dynamicky linkované knihovny https://github.com/tisnik/most-popular-python-libs/blob/master/cffi/gre­eter_h2/clean.sh
       
45 greeter_h3/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/gre­eter_h3/greeter.c
46 greeter_h3/greeter.h test na existenci symbolu/makra, pokud makro neexistuje, provede se vložení dalšího souboru https://github.com/tisnik/most-popular-python-libs/blob/master/cffi/gre­eter_h3/greeter.h
47 greeter_h3/_greeter.h prototyp (předběžná deklarace) funkce greeter bez dalších informací https://github.com/tisnik/most-popular-python-libs/blob/master/cffi/gre­eter_h3/_greeter.h
48 greeter_h3/call_via_cffi5.py zavolání céčkovské funkce z knihovny z jazyka Python https://github.com/tisnik/most-popular-python-libs/blob/master/cffi/gre­eter_h3/call_via_cffi5.py
49 greeter_h3/call_via_cffi.sh nastavení cest a spuštění Pythonovského skriptu https://github.com/tisnik/most-popular-python-libs/blob/master/cffi/gre­eter_h3/call_via_cffi.sh
50 greeter_h3/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/gre­eter_h3/make_library.sh
51 greeter_h3/clean.sh skript pro smazání objektového souboru i dynamicky linkované knihovny https://github.com/tisnik/most-popular-python-libs/blob/master/cffi/gre­eter_h3/clean.sh
       
52 greeter_build/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/gre­eter_build/greeter.c
53 greeter_build/greeter.h prototyp (předběžná deklarace) funkce greeter bez dalších informací https://github.com/tisnik/most-popular-python-libs/blob/master/cffi/gre­eter_build/greeter.h
54 greeter_build/call_via_cffi7.py skript pro překlad céčkovské funkce, vytvoření dynamicky linkované knihovny a zavolání funkce z této knihovny https://github.com/tisnik/most-popular-python-libs/blob/master/cffi/gre­eter_build/call_via_cffi7­.py
55 greeter_build/clean.sh skript pro smazání objektového souboru i dynamicky linkované knihovny https://github.com/tisnik/most-popular-python-libs/blob/master/cffi/gre­eter_build/clean.sh
       
56 vector_printer/vector_printer.c funkce psaná v C, která akceptuje jako svůj parametr strukturu https://github.com/tisnik/most-popular-python-libs/blob/master/cffi/vec­tor_printer/vector_printer­.c
57 vector_printer/vector_printer.h prototyp (předběžná deklarace) funkce print_vector bez dalších informací https://github.com/tisnik/most-popular-python-libs/blob/master/cffi/vec­tor_printer/vector_printer­.h
58 vector_printer/call_via_cffi.sh zavolání céčkovské funkce z knihovny z jazyka Python https://github.com/tisnik/most-popular-python-libs/blob/master/cffi/vec­tor_printer/call_via_cffi­.sh
59 vector_printer/call_via_cffi.py nastavení cest a spuštění Pythonovského skriptu https://github.com/tisnik/most-popular-python-libs/blob/master/cffi/vec­tor_printer/call_via_cffi­.py
60 vector_printer/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/vec­tor_printer/make_library.sh
61 vector_printer/clean.sh skript pro smazání objektového souboru i dynamicky linkované knihovny https://github.com/tisnik/most-popular-python-libs/blob/master/cffi/vec­tor_printer/clean.sh
       
62 vector_printer2/vector_printer.c funkce psaná v C, která akceptuje jako svůj parametr ukazatel na strukturu https://github.com/tisnik/most-popular-python-libs/blob/master/cffi/vec­tor_printer2/vector_printer­.c
63 vector_printer2/vector_printer.h prototyp (předběžná deklarace) funkce print_vector bez dalších informací https://github.com/tisnik/most-popular-python-libs/blob/master/cffi/vec­tor_printer2/vector_printer­.h
64 vector_printer2/call_via_cffi.sh zavolání céčkovské funkce z knihovny z jazyka Python https://github.com/tisnik/most-popular-python-libs/blob/master/cffi/vec­tor_printer2/call_via_cffi­.sh
65 vector_printer2/call_via_cffi.py nastavení cest a spuštění Pythonovského skriptu https://github.com/tisnik/most-popular-python-libs/blob/master/cffi/vec­tor_printer2/call_via_cffi­.py
66 vector_printer2/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/vec­tor_printer2/make_library­.sh
67 vector_printer2/clean.sh skript pro smazání objektového souboru i dynamicky linkované knihovny https://github.com/tisnik/most-popular-python-libs/blob/master/cffi/vec­tor_printer2/clean.sh
       
68 array_printer1/array_printer.c funkce naprogramovaná v C, která akceptuje pole s prvky typu float https://github.com/tisnik/most-popular-python-libs/blob/master/cffi/arra­y_printer1/array_printer.c
69 array_printer1/array_printer.h prototyp (předběžná deklarace) funkce print_array bez dalších informací https://github.com/tisnik/most-popular-python-libs/blob/master/cffi/arra­y_printer1/array_printer.h
70 array_printer1/call_via_cffi.py zavolání céčkovské funkce z knihovny z jazyka Python https://github.com/tisnik/most-popular-python-libs/blob/master/cffi/arra­y_printer1/call_via_cffi.sh
71 array_printer1/call_via_cffi.sh nastavení cest a spuštění Pythonovského skriptu https://github.com/tisnik/most-popular-python-libs/blob/master/cffi/arra­y_printer1/call_via_cffi.py
72 array_printer1/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/arra­y_printer1/make_library.sh
73 array_printer1/clean.sh skript pro smazání objektového souboru i dynamicky linkované knihovny https://github.com/tisnik/most-popular-python-libs/blob/master/cffi/arra­y_printer1/clean.sh
       
74 array_printer2/array_printer.c funkce naprogramovaná v C, která akceptuje pole s prvky typu float https://github.com/tisnik/most-popular-python-libs/blob/master/cffi/arra­y_printer2/array_printer.c
75 array_printer2/array_printer.h prototyp (předběžná deklarace) funkce print_array bez dalších informací https://github.com/tisnik/most-popular-python-libs/blob/master/cffi/arra­y_printer2/array_printer.h
76 array_printer2/call_via_cffi.py zavolání céčkovské funkce z knihovny z jazyka Python https://github.com/tisnik/most-popular-python-libs/blob/master/cffi/arra­y_printer2/call_via_cffi.sh
77 array_printer2/call_via_cffi.sh nastavení cest a spuštění Pythonovského skriptu https://github.com/tisnik/most-popular-python-libs/blob/master/cffi/arra­y_printer2/call_via_cffi.py
78 array_printer2/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/arra­y_printer2/make_library.sh
79 array_printer2/clean.sh skript pro smazání objektového souboru i dynamicky linkované knihovny https://github.com/tisnik/most-popular-python-libs/blob/master/cffi/arra­y_printer2/clean.sh
       
80 array_printer3/array_printer.c funkce naprogramovaná v C, která akceptuje pole s prvky typu float https://github.com/tisnik/most-popular-python-libs/blob/master/cffi/arra­y_printer3/array_printer.c
81 array_printer3/array_printer.h prototyp (předběžná deklarace) funkce print_array bez dalších informací https://github.com/tisnik/most-popular-python-libs/blob/master/cffi/arra­y_printer3/array_printer.h
82 array_printer3/call_via_cffi.py zavolání céčkovské funkce z knihovny z jazyka Python https://github.com/tisnik/most-popular-python-libs/blob/master/cffi/arra­y_printer3/call_via_cffi.sh
83 array_printer3/call_via_cffi.sh nastavení cest a spuštění Pythonovského skriptu https://github.com/tisnik/most-popular-python-libs/blob/master/cffi/arra­y_printer3/call_via_cffi.py
84 array_printer3/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/arra­y_printer3/make_library.sh
85 array_printer3/clean.sh skript pro smazání objektového souboru i dynamicky linkované knihovny https://github.com/tisnik/most-popular-python-libs/blob/master/cffi/arra­y_printer3/clean.sh
       
86 array_printer4/array_printer.c funkce naprogramovaná v C, která akceptuje pole s prvky typu float https://github.com/tisnik/most-popular-python-libs/blob/master/cffi/arra­y_printer4/array_printer.c
87 array_printer4/array_printer.h prototyp (předběžná deklarace) funkce print_array bez dalších informací https://github.com/tisnik/most-popular-python-libs/blob/master/cffi/arra­y_printer4/array_printer.h
88 array_printer4/call_via_cffi.py zavolání céčkovské funkce z knihovny z jazyka Python https://github.com/tisnik/most-popular-python-libs/blob/master/cffi/arra­y_printer4/call_via_cffi.sh
89 array_printer4/call_via_cffi.sh nastavení cest a spuštění Pythonovského skriptu https://github.com/tisnik/most-popular-python-libs/blob/master/cffi/arra­y_printer4/call_via_cffi.py
90 array_printer4/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/arra­y_printer4/make_library.sh
91 array_printer4/clean.sh skript pro smazání objektového souboru i dynamicky linkované knihovny https://github.com/tisnik/most-popular-python-libs/blob/master/cffi/arra­y_printer4/clean.sh
       
92 array_printer5/array_printer.c funkce naprogramovaná v C, která akceptuje pole s prvky typu vector_t https://github.com/tisnik/most-popular-python-libs/blob/master/cffi/arra­y_printer5/array_printer.c
93 array_printer5/array_printer.h prototyp (předběžná deklarace) funkce print_array bez dalších informací https://github.com/tisnik/most-popular-python-libs/blob/master/cffi/arra­y_printer5/array_printer.h
94 array_printer5/call_via_cffi.py zavolání céčkovské funkce z knihovny z jazyka Python https://github.com/tisnik/most-popular-python-libs/blob/master/cffi/arra­y_printer5/call_via_cffi.sh
95 array_printer5/call_via_cffi.sh nastavení cest a spuštění Pythonovského skriptu https://github.com/tisnik/most-popular-python-libs/blob/master/cffi/arra­y_printer5/call_via_cffi.py
96 array_printer5/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/arra­y_printer5/make_library.sh
97 array_printer5/clean.sh skript pro smazání objektového souboru i dynamicky linkované knihovny https://github.com/tisnik/most-popular-python-libs/blob/master/cffi/arra­y_printer5/clean.sh
       
92 array_printer6/array_printer.c funkce naprogramovaná v C, která akceptuje dvourozměrné pole s prvky typu float https://github.com/tisnik/most-popular-python-libs/blob/master/cffi/arra­y_printer6/array_printer.c
93 array_printer6/array_printer.h prototyp (předběžná deklarace) funkce print_array bez dalších informací https://github.com/tisnik/most-popular-python-libs/blob/master/cffi/arra­y_printer6/array_printer.h
94 array_printer6/call_via_cffi.py zavolání céčkovské funkce z knihovny z jazyka Python https://github.com/tisnik/most-popular-python-libs/blob/master/cffi/arra­y_printer6/call_via_cffi.sh
95 array_printer6/call_via_cffi.sh nastavení cest a spuštění Pythonovského skriptu https://github.com/tisnik/most-popular-python-libs/blob/master/cffi/arra­y_printer6/call_via_cffi.py
96 array_printer6/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/arra­y_printer6/make_library.sh
97 array_printer6/clean.sh skript pro smazání objektového souboru i dynamicky linkované knihovny https://github.com/tisnik/most-popular-python-libs/blob/master/cffi/arra­y_printer6/clean.sh
       
98 array_printer7/array_printer.c funkce naprogramovaná v C, která akceptuje dvourozměrné pole s prvky typu float https://github.com/tisnik/most-popular-python-libs/blob/master/cffi/arra­y_printer7/array_printer.c
99 array_printer7/array_printer.h prototyp (předběžná deklarace) funkce print_array bez dalších informací https://github.com/tisnik/most-popular-python-libs/blob/master/cffi/arra­y_printer7/array_printer.h
100 array_printer7/call_via_cffi.py zavolání céčkovské funkce z knihovny z jazyka Python https://github.com/tisnik/most-popular-python-libs/blob/master/cffi/arra­y_printer7/call_via_cffi.sh
101 array_printer7/call_via_cffi.sh nastavení cest a spuštění Pythonovského skriptu https://github.com/tisnik/most-popular-python-libs/blob/master/cffi/arra­y_printer7/call_via_cffi.py
102 array_printer7/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/arra­y_printer7/make_library.sh
103 array_printer7/clean.sh skript pro smazání objektového souboru i dynamicky linkované knihovny https://github.com/tisnik/most-popular-python-libs/blob/master/cffi/arra­y_printer7/clean.sh

20. Odkazy na Internetu

  1. ctypes – A foreign function library for Python
    https://docs.python.org/3/li­brary/ctypes.html
  2. Pygame: display
    https://www.pygame.org/doc­s/ref/display.html
  3. Pygame: event
    https://www.pygame.org/doc­s/ref/event.html
  4. Pygame: image
    https://www.pygame.org/doc­s/ref/image.html
  5. Pygame: clock
    https://www.pygame.org/doc­s/ref/time.html#pygame.ti­me.Clock
  6. Fraktály v počítačové grafice XII
    https://www.root.cz/clanky/fraktaly-v-pocitacove-grafice-xii/
  7. Fraktály v počítačové grafice XIII
    https://www.root.cz/clanky/fraktaly-v-pocitacove-grafice-xiii/
  8. Fraktály v počítačové grafice XIV
    https://www.root.cz/clanky/fraktaly-v-pocitacove-grafice-xiv/
  9. CFFI documentation
    https://cffi.readthedocs.i­o/en/latest/
  10. cffi 1.15.1 na PyPi
    https://pypi.org/project/cffi/
  11. Python Bindings: Calling C or C++ From Python
    https://realpython.com/python-bindings-overview/
  12. Interfacing with C/C++ Libraries
    https://docs.python-guide.org/scenarios/clibs/
  13. Cython, pybind11, cffi – which tool should you choose?
    http://blog.behnel.de/posts/cython-pybind11-cffi-which-tool-to-choose.html
  14. Python FFI with ctypes and cffi
    https://eli.thegreenplace­.net/2013/03/09/python-ffi-with-ctypes-and-cffi
  15. 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/
  16. 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/
  17. Programovací jazyk Rust: použití FFI pro volání funkcí z nativních knihoven
    https://www.root.cz/clanky/pro­gramovaci-jazyk-rust-pouziti-ffi-pro-volani-funkci-z-nativnich-knihoven/
  18. Programovací jazyk Rust: použití FFI při předávání struktur
    https://www.root.cz/clanky/pro­gramovaci-jazyk-rust-pouziti-ffi-pri-predavani-struktur/
  19. Programovací jazyk Rust: použití FFI pro volání funkcí z nativních knihoven (2. část)
    https://www.root.cz/clanky/pro­gramovaci-jazyk-rust-pouziti-ffi-pro-volani-funkci-z-nativnich-knihoven-2-cast/
  20. Dynamic-link library
    https://en.wikipedia.org/wiki/Dynamic-link_library
  21. Úvod do jazyka C: Deklarace funkcí
    https://www.fi.muni.cz/us­r/jkucera/pb071/sl5.htm
  22. Using standard library headers with CFFI
    https://stackoverflow.com/qu­estions/57481873/using-standard-library-headers-with-cffi
  23. Preparing and Distributing modules
    https://cffi.readthedocs.i­o/en/latest/cdef.html
  24. C Arrays
    https://www.programiz.com/c-programming/c-arrays
  25. C Arrays
    https://www.w3schools.com/c/c_a­rrays.php
  26. Array of Structures in C
    https://overiq.com/c-programming-101/array-of-structures-in-c/#google_vignette
  27. C Structures (structs)
    https://www.w3schools.com/c/c_struc­ts.php
  28. C Structs and pointers
    https://www.programiz.com/c-programming/c-structures-pointers
Neutrální ikona do widgetu na odběr článků ze seriálů

Zajímá vás toto téma? Chcete se o něm dozvědět víc?

Objednejte si upozornění na nově vydané články do vašeho mailu. Žádný článek vám tak neuteče.


Autor článku

Vystudoval VUT FIT a v současné době pracuje na projektech vytvářených v jazycích Python a Go.