Показанная раньше техника создания модулей Python на C/C++ понятна и достаточно хорошо описана в документации в документации: передача объектов от исполняющей системы Python (параметры), преобразование значений переменных C/C++ в объекты Python (возврат значений)... Недостающие детали можно подсмотреть в заголовочном файле <python*/modsupport.h>. Но всё-таки такая ручная техника создания модулей на C/C++ достаточно громоздкая. Для её упрощения создано несколько инструментальных пакетов, автоматизирующих и упрощающих эту деятельность (Cython, SWIG и другие). Мной опробовано (пока?) только:
#include <stdlib.h>
int* frequency( char s[] ) {
int *freq;
char *ptr;
freq = (int*)( calloc( 256, sizeof( int ) ) );
if( freq != NULL )
for( ptr = s; *ptr; ptr++ )
freq[ *ptr ] += 1;
return freq;
}
Теперь мы не пишем в коде программную реализацию интерфейса к этой функции C, как в предыдущем рассмотрении, а составляем интерфейсный файл SWIG (freq.i, расширение i):
%module freq
%typemap(out) int* {
int i;
$result = PyTuple_New( 256 );
for( i = 0; i < 256; i++ )
PyTuple_SetItem( $result, i, PyLong_FromLong( $1[ i ] ) );
free( $1 );
}
extern int* frequency( char s[] );
Даже вот такая, несколько усложнённая форма описания, связана только с тем фактом, что С-функция frequency() динамически приобретает память, и после использования её нужно вернуть во избежание утечек памяти. Во многих же практических случаях (см. далее) интерфейсные описания и того проще. После создания мы обрабатываем файл описания вот таким образом:
$ swig -python freq.i
В результате чего генерируются файла исходного кода интерфейса модуля (freq_wrap.c). Из имеющихся файлов уже можно непосредственно собирать динамическую библиотеку, реализующую модуль Python:
$ gcc -c -fpic freq_wrap.c freq.c -DHAVE_CONFIG_H -I/usr/include/python2.7 -I/usr/lib/python2.7/co
$ ld -shared freq_wrap.o freq.o -o _freq.so
Всё! Нам остаётся только написать тестирующее приложение Python (файл frtest.py) для непосредственного импорта созданного модуля:
#!/usr/bin/python
# -*- coding: utf-8 -*-
from sys import argv
import freq
sarg = lambda : ( len( argv ) > 1 and argv[ 1 ] ) or str( input( "string?: " ) )
F = freq.frequency( sarg() )
s = ""
for i in range( 256 ):
if F[ i ] != 0: s = s + ( "x%x:%d " % ( i, F[ i ] ) )
print s
В итоге мы получили работоспособный модуль Python (с именем freq), пусть не очень умный, но в достаточной мере иллюстрирующий технику использования SWIG:
$ python frtest.py "1234123121 abc ab a"
x20:3 x31:4 x32:3 x33:2 x34:1 x61:3 x62:2 x63:1
%module fileio
extern FILE *fopen(char *, char *);
extern int fclose(FILE *);
extern unsigned fread(void *ptr, unsigned size, unsigned nobj, FILE *);
extern unsigned fwrite(void *ptr, unsigned size, unsigned nobj, FILE *);
extern void *malloc(int nbytes);
extern void free(void *);
Больше ничего нам, собственно, и не придётся делать:
$ swig -python fileio.i
$ gcc -c -fpic fileio_wrap.c -DHAVE_CONFIG_H -I/usr/include/python2.7 -I/usr/lib/python2.7/config
$ ld -shared fileio_wrap.o -lc -o _fileio.so
Тестовая задача (fiotest.py) на Python:
#!/usr/bin/python
# -*- coding: utf-8 -*-
from fileio import *
import fileio
from sys import argv
def filecopy( source, target ): # Copy a file
sum = long( 0 )
f1 = fopen( source, "r" )
if f1 == None: return -1
f2 = fopen( target, "w" )
buffer = malloc( 8192 )
nbytes = fread( buffer, 1, 8192, f1 )
while( nbytes > 0 ):
fwrite( buffer, 1, nbytes, f2 )
sum = sum + nbytes
nbytes = fread( buffer, 1, 8192, f1 )
free( buffer )
fclose( f1 )
fclose( f2 )
return sum
n = filecopy( ( len( argv ) > 1 and argv[ 1 ] ) or "in.txt", \
( len( argv ) > 2 and argv[ 2 ] ) or "out.txt" )
print "скопировано %d байт" % n
И вот её функционирование:
$ python fiotest.py in.txt out.txt
скопировано 178 байт
$ ls -l *.txt
-rw-rw-r-- 1 olej olej 178 июня 21 23:48 in.txt
-rw-rw-r-- 1 olej olej 178 июня 22 00:14 out.txt
Использование SWIG.
В качестве упражнения я выбрал для разбирательства весьма простую задачу: подсчитать частоту вхождения в произвольный текст символов (256 символов из основной таблицы ASCII, только 1-байтовых, для упрощения). Реализующий C-код имеет вид (файл freq.c):#include <stdlib.h>
int* frequency( char s[] ) {
int *freq;
char *ptr;
freq = (int*)( calloc( 256, sizeof( int ) ) );
if( freq != NULL )
for( ptr = s; *ptr; ptr++ )
freq[ *ptr ] += 1;
return freq;
}
Теперь мы не пишем в коде программную реализацию интерфейса к этой функции C, как в предыдущем рассмотрении, а составляем интерфейсный файл SWIG (freq.i, расширение i):
%module freq
%typemap(out) int* {
int i;
$result = PyTuple_New( 256 );
for( i = 0; i < 256; i++ )
PyTuple_SetItem( $result, i, PyLong_FromLong( $1[ i ] ) );
free( $1 );
}
extern int* frequency( char s[] );
Даже вот такая, несколько усложнённая форма описания, связана только с тем фактом, что С-функция frequency() динамически приобретает память, и после использования её нужно вернуть во избежание утечек памяти. Во многих же практических случаях (см. далее) интерфейсные описания и того проще. После создания мы обрабатываем файл описания вот таким образом:
$ swig -python freq.i
В результате чего генерируются файла исходного кода интерфейса модуля (freq_wrap.c). Из имеющихся файлов уже можно непосредственно собирать динамическую библиотеку, реализующую модуль Python:
$ gcc -c -fpic freq_wrap.c freq.c -DHAVE_CONFIG_H -I/usr/include/python2.7 -I/usr/lib/python2.7/co
$ ld -shared freq_wrap.o freq.o -o _freq.so
Всё! Нам остаётся только написать тестирующее приложение Python (файл frtest.py) для непосредственного импорта созданного модуля:
#!/usr/bin/python
# -*- coding: utf-8 -*-
from sys import argv
import freq
sarg = lambda : ( len( argv ) > 1 and argv[ 1 ] ) or str( input( "string?: " ) )
F = freq.frequency( sarg() )
s = ""
for i in range( 256 ):
if F[ i ] != 0: s = s + ( "x%x:%d " % ( i, F[ i ] ) )
print s
В итоге мы получили работоспособный модуль Python (с именем freq), пусть не очень умный, но в достаточной мере иллюстрирующий технику использования SWIG:
$ python frtest.py "1234123121 abc ab a"
x20:3 x31:4 x32:3 x33:2 x34:1 x61:3 x62:2 x63:1
Прямой доступ к библиотекам C/C++.
В этой технике интересно создание модулей прямых интерфейсов к уже существующим ранее целевым библиотекам (.so) C/C++ (при отсутствии их аналогов в Python), это вообще практически без написания кода. В порядке иллюстрации сказанного я сделаю интерфейсный модуль к файловым операциям C. Файл интерфейсного описания SWIG (fileio.i) имеет вид:%module fileio
extern FILE *fopen(char *, char *);
extern int fclose(FILE *);
extern unsigned fread(void *ptr, unsigned size, unsigned nobj, FILE *);
extern unsigned fwrite(void *ptr, unsigned size, unsigned nobj, FILE *);
extern void *malloc(int nbytes);
extern void free(void *);
Больше ничего нам, собственно, и не придётся делать:
$ swig -python fileio.i
$ gcc -c -fpic fileio_wrap.c -DHAVE_CONFIG_H -I/usr/include/python2.7 -I/usr/lib/python2.7/config
$ ld -shared fileio_wrap.o -lc -o _fileio.so
Тестовая задача (fiotest.py) на Python:
#!/usr/bin/python
# -*- coding: utf-8 -*-
from fileio import *
import fileio
from sys import argv
def filecopy( source, target ): # Copy a file
sum = long( 0 )
f1 = fopen( source, "r" )
if f1 == None: return -1
f2 = fopen( target, "w" )
buffer = malloc( 8192 )
nbytes = fread( buffer, 1, 8192, f1 )
while( nbytes > 0 ):
fwrite( buffer, 1, nbytes, f2 )
sum = sum + nbytes
nbytes = fread( buffer, 1, 8192, f1 )
free( buffer )
fclose( f1 )
fclose( f2 )
return sum
n = filecopy( ( len( argv ) > 1 and argv[ 1 ] ) or "in.txt", \
( len( argv ) > 2 and argv[ 2 ] ) or "out.txt" )
print "скопировано %d байт" % n
И вот её функционирование:
$ python fiotest.py in.txt out.txt
скопировано 178 байт
$ ls -l *.txt
-rw-rw-r-- 1 olej olej 178 июня 21 23:48 in.txt
-rw-rw-r-- 1 olej olej 178 июня 22 00:14 out.txt