ethanak <sweetha
...@buziaczek.pl> writes:
> Witam.
> Postanowiłem wreszcie napisać w pygtk coś więcej niż prosty dialog
> (ambitne postanowienie, nie?) i zaraz na wstępie się zaciąłem.
> Otóż "sercem" powstającego programiku jest pewna (moja prywatna)
> biblioteka pisana w C. Coś w stylu:
> struct mylib_conf *mylib_init(jakieś parametry);
> char *mylib_funkcja(struct mylib_conf *conf,jakieś parametry);
> void mylib_free(struct mylib_conf *conf);
> przy czym tych "mylib_funkcja" jest oczywiście więcej.
> Wszystko pięknie-ładnie, jak z Pythona do C parametry przekazać to się
> już naumiałem, jak wyniki pobrać też tylko nie mam pojęcia co mam zrobić
> z tym nieszczęsnym "conf". Jest to zupełnie prywatna struktura w
> bibliotece, aplikacja nie powinna się jej zawartością interesować tylko
> zapamiętać wskaźnik i bez wnikania w szczegóły przekazywać wywoływanym
> funkcjom.
> No i teraz pytanie: jak to ma wyglądać od strony Pythona, który o ile mi
> wiadomo takowych "wskaźników" nie przewiduje?
> W Iconie radziłem sobie naokoło (funkcja typu "init" zwracała string z
> szesnastkowym zapisem adresu) ale nie wydaje mi się to specjalnie
> szczęśliwym rozwiązaniem.
> Aha, wszystko to działa na Linuksie, na chwilę obecną inne systemy mnie
> nie interesują.
> Jakaś idea? Najchętniej coś do podpatrzenia...
Do takich zabaw dość przyjemny jest Cython [1] [2]. Generalnie pozwala
on w dość prosty sposób opakowywać biblioteki napisane w C.
Co do wskaźników, to zwykle przyjmuje się takie rozwiązanie,
iż struktury z C opakowywane są w klasy (a właściwie typy) Pythona
i dopiero nimi się posługujemy na poziomie Pythona.
Z poniższego przykładu powinieś się pokapować jak to działa.
Tu akurat opakowałem strukturę `dirent` z "dirent.h" typem `Directory`.
Czyli mamy taki prosty programik w C:
<code>
#include <stdio.h>
#include <sys/types.h>
#include <dirent.h>
int main()
{
DIR* pdir;
struct dirent* pdirent;
pdir = opendir(".");
pdirent = readdir(pdir);
while (pdirent != NULL)
{
printf("%s\n", pdirent->d_name);
pdirent = readdir(pdir);
}
closedir(pdir);
return 0;
}
</code>
Aby analogicznie użyć struktury `dirent` i funcji `opendir`, `readdir`
i `closedir` z poziomu Pythona, możemy zrobić coś takiego:
<code>
# pydir.pyx
cdef extern from "sys/types.h":
ctypedef unsigned long int __ino_t
ctypedef long int __off_t
cdef extern from "dirent.h":
ctypedef struct DIR
cdef struct dirent:
__ino_t d_ino
__off_t d_off
unsigned short int d_reclen
unsigned char d_type
char d_name[256]
DIR* opendir(char* __name)
dirent* readdir(DIR* __dirp)
int closedir(DIR* __dirp)
cdef class Directory:
cdef DIR* c_dir
cdef dirent* c_dirent
cdef object root
def __init__(self, root):
self.root = root
self.init()
cdef init(self):
self.c_dir = opendir(self.root)
self.c_dirent = readdir(self.c_dir)
def __iter__(self):
return self
def __next__(self):
if self.c_dirent != NULL:
dname = self.c_dirent.d_name
self.c_dirent = readdir(self.c_dir)
return dname
closedir(self.c_dir)
raise StopIteration
</code>
Z pliku `pydir.pyx` tworzymy rozszerzenie `pydir.so` Pythona przy pomocy
distutils, czyli takiego pliku `setup.py`:
<code>
# setup.py
from distutils.core import setup
from distutils.extension import Extension
from Cython.Distutils import build_ext as build_pyx
setup(name="PyDir",
ext_modules=[Extension('pydir', ['pydir.pyx'])],
cmdclass = {'build_ext': build_pyx})
</code>
$ python setup.py build_ext -i
W bieżącym katalogu powinien się utworzyć moduł `pydir.so`,
który możemy używać tak:
<code>
# test_pydir.py
import pydir
def listdir(root):
for dname in pydir.Directory(root):
print dname
listdir('.')
</code>
Prawda, że proste? ;)
Cython ma swoje ograniczenia (np. nie obsługuje generatorów), ale w większości
przypadków (przynajmniej moich) się sprawdza.
[1] http://www.cython.org/
[2] http://wiki.cython.org/
RW