связка Python + C/C++

Вопросы написания собственного программного кода (на любых языках)

Модератор: Olej

Аватара пользователя
Olej
Писатель
Сообщения: 21338
Зарегистрирован: 24 сен 2011, 14:22
Откуда: Харьков
Контактная информация:

связка Python + C/C++

Непрочитанное сообщение Olej » 13 авг 2013, 00:08

Эта тема переползла сюда вот отсюда: Осваиваем Python по-быстрому.

Но кое-какие примеры к ней были обсуждены и раньше, в теме: Python:
меня в экспериментах пока интересовал пока только один нетривиальный вопрос: как создать модуль Python написав его на C/C++?
Вынести обсуждение "Python + C/C++" имеет несколько смыслов:
1. чтобы не загружать этим Python-обсуждения, потому, что многим "чистым" программистам Python это и ненужно и непонятно...
2. из-за объёмности темы: придумано достаточно много разных инструментов (проектов) по "механизации" написания интерфейсов из Python в C код.
3. интерес может представлять не только "прямые" интерфейсы - написание модулей Python на языке C, но и "инверсные" - написание Python фрагментов (скриптов, плагинов, модулей) к проектам написанным на C.

P.S. Мне из языковых расширений C-проектов на интерпретирующих языках, из того, что попадалось на глаза, показались интересными и заслуживающими внимания, кроме интерфейса к Python, только интерфейсы а). к языку Lua и б). к JavaScript - в рализации проекта SpiderMonkey в составе Mozilla.
Итого: 3 шт. ;-)

Темы всё это достаточно объёмные и сложные.

Аватара пользователя
Olej
Писатель
Сообщения: 21338
Зарегистрирован: 24 сен 2011, 14:22
Откуда: Харьков
Контактная информация:

Re: связка Python + C/C++

Непрочитанное сообщение Olej » 13 авг 2013, 02:23

Olej писал(а): 2. ... придумано достаточно много разных инструментов (проектов) по "механизации" написания интерфейсов из Python в C код.
Как минимум из широко используемых и обсуждаемых это:

- использование пакета swig (это ссылка на его документацию)... этот пакет есть в репозитарии практически каждого дистрибутива Linux, так что его и собирать его не надо...

- использование пакета Python distutils, хороший и достаточный пример обсуждается здесь: Python + C API simple example memory leak

- использование проекта Cython ... возможность использования C++, некоторое поверхностное обсуждение см. здесь: Как подключить модуль на C при помощи cython?

- использование библиотеки Boost.Python в составе Boost для взаимных коммуникаций C++ с Python - доступна достаточная документация в составе Boost, и описания с примерами с точки зрения Python.

Интересно проделать параллельные примеры с использованием разных инструментов и сравнить их меж собой...

Аватара пользователя
Olej
Писатель
Сообщения: 21338
Зарегистрирован: 24 сен 2011, 14:22
Откуда: Харьков
Контактная информация:

Re: связка Python + C/C++

Непрочитанное сообщение Olej » 13 авг 2013, 17:28

Olej писал(а): - использование пакета swig (это ссылка на его документацию)... этот пакет есть в репозитарии практически каждого дистрибутива Linux, так что его и собирать его не надо...
SWIG, как мне кажется, это самый универсальный интерфейс, но одновременно и самый капризный в использовании.
Универсальность определяется тем, что (см. сайт проекта) :
SWIG is a software development tool that connects programs written in C and C++ with a variety of high-level programming languages. SWIG is used with different types of target languages including common scripting languages such as Perl, PHP, Python, Tcl and Ruby. The list of supported languages also includes non-scripting languages such as C#, Common Lisp (CLISP, Allegro CL, CFFI, UFFI), D, Go language, Java including Android, Lua, Modula-3, OCAML, Octave and R.
... интерфейс к добрым 2-м десяткам языков программирования.
Но за универсальность всегда нужно платить сложностью!

Проект активно развивается:
Recent News
2013/0
5/27 - SWIG-2.0.10 released
Идея SWIG в том, что кроме реализации файла модуля (.c) на C (или C++ - .cc), нужно:
- всего лишь записать интерфейсный файл (.i) на специальном макроязыке SWIG
- SWIG сгенерирует интерфейсные файлы...
- язык, для которого генерируется, указывается параметром команды SWIG, а интерфейсный файл (.i) не зависит от того, в какой язык (Python, LISP, Modula-3 etc.) из него будут генерить интерфейс ... это даже удивляет :-o

Вот, для примера, простейший пример (first.tgz), каталог до генерации:

Код: Выделить всё

bash-4.2$ ls
first.hist  Makefile  make_py.sh  ownmod.c  ownmod.i  test.py
К делу здесь относятся только 2 файла (Makefile & make_py.sh - это скрипты сборки, test.py - тестовая программа на Python которой будем проверять что получится), это:
- файл C кода реализующего новый модуль - ownmod.c:

Код: Выделить всё

#include <stdio.h>
void echo( void ) {
   printf( "вывод из экспортированного кода!\n" );
}
- файл описания интерфейса к этому модулю - ownmod.i:

Код: Выделить всё

%module ownmod
extern void echo( void );
Всё!
В таком простейшем случае всё просто до примитивного!

Вся генерация (в Makefile или скрипте make_py.sh):

Код: Выделить всё

bash-4.2$ make
swig -python -module ownmod ownmod.i
gcc -c -fpic  ownmod_wrap.c ownmod.c -DHAVE_CONFIG_H -I/usr/include/python2.7 -I/usr/include/python2.7
gcc -shared ownmod.o ownmod_wrap.o -o _ownmod.so
rm *.o
После чего создаётся динамическая библиотека (DLL) _ownmod.so (обратите внимание на подчёркивание!), и мы можем из Python-теста тестировать модуль ... файл test.py :

Код: Выделить всё

#!/usr/bin/python
# -*- coding: utf-8 -*-
import ownmod
ownmod.echo()
Выполнение:

Код: Выделить всё

bash-4.2$ ./test.py 
вывод из экспортированного кода!
Вот так вот всё просто! ;-)
Вложения
first.tgz
(5.96 КБ) 486 скачиваний

Аватара пользователя
Olej
Писатель
Сообщения: 21338
Зарегистрирован: 24 сен 2011, 14:22
Откуда: Харьков
Контактная информация:

Re: связка Python + C/C++

Непрочитанное сообщение Olej » 13 авг 2013, 20:57

далее, SWIG ...
Olej писал(а): как мне кажется, это самый универсальный интерфейс, но одновременно и самый капризный в использовании.
...
... интерфейс к добрым 2-м десяткам языков программирования.
...
- всего лишь записать интерфейсный файл (.i) на специальном макроязыке SWIG
- SWIG сгенерирует интерфейсные файлы...
- язык, для которого генерируется, указывается параметром команды SWIG, а интерфейсный файл (.i) не зависит от того, в какой язык (Python, LISP, Modula-3 etc.) из него будут генерить интерфейс ... это даже удивляет :-o
Вот это очень интересное место...
Для рассмотрения того, как из файла описаний интерфейсов .i генерятся сами обёртки (wrap-еры) для самых разных языков, очень удобно рассмотреть множество примеров, приводимых в самом проекте SWIG. Но этих примеров нет в пакетных установках, нужно взять архив исходных текстов SWIG:

1. берём здесь, там 5Mb всего то:

Код: Выделить всё

bash-4.2$ ls -l swig-2.0.10.tar.gz
-rw-rw-r-- 1 olej olej 5302787 авг.  13 16:08 swig-2.0.10.tar.gz
2. разархивируем в любом месте ($HOME etc.), 32Mb всё дерево файлов:

Код: Выделить всё

bash-4.2$ tar -zxvf swig-2.0.10.tar.gz
...
bash-4.2$ du -hs swig-2.0.10
32M     swig-2.0.10


3. т.к. я не хочу устанавливать SWIG из исходников, а только хочу пособирать и поизучать примеры из подкаталога Examples, то я в корне дерева должен сделать (для создания Makefile):

Код: Выделить всё

bash-4.2$ time ./configure
checking build system type... i686-pc-linux-gnu
...
real    0m19.763s
user    0m5.302s
sys     0m3.393s


4. а дальше в любой свой каталог перетаскиваю любой пример из Examples + туда же сгенерированный в корне Makefile (переименовав Makefile примера, например, в Makefile.lua), в родном Makefile.lua может придётся переименовать на ваши пути:

Код: Выделить всё

#TOP        = ../..
TOP        = ./
#SWIG       = $(TOP)/../preinst-swig
SWIG       = /usr/bin/swig
...
5. меня всегда интересует язык Lua ... для сборки примеров в системе должны быть установлены (из репозитария) пакеты (для наличия lua.h):

Код: Выделить всё

bash-4.2$ sudo yum install lua lua-devel
...
bash-4.2$ lua -v
Lua 5.1.4  Copyright (C) 1994-2008 Lua.org, PUC-Rio
Всё! Любой пример можно собирать и изучать:

Код: Выделить всё

bash-4.2$ make -f Makefile.lua
make -f .//Makefile SRCS='example.c' SWIG='/usr/bin/swig' \
TARGET='example' INTERFACE='example.i' lua
make[1]: Вход в каталог `/home/olej/2013_WORK/Python/CPython/swig/lua'
/usr/bin/swig -lua  example.i
gcc -c -fpic -I/usr/include  example_wrap.c example.c
gcc -shared -I/usr/include  example.o  example_wrap.o   -o example.so
make[1]: Выход из каталога `/home/olej/2013_WORK/Python/CPython/swig/lua'
make -f .//Makefile lua_run
make[1]: Вход в каталог `/home/olej/2013_WORK/Python/CPython/swig/lua'
runme.lua
make[1]: runme.lua: Команда не найдена
make[1]: *** [lua_run] Ошибка 127
make[1]: Выход из каталога `/home/olej/2013_WORK/Python/CPython/swig/lua'
make: *** [check] Ошибка 2

bash-4.2$ ls
example.c  example.i  example.o  example.so  example_wrap.c  example_wrap.o  lua.hist  Makefile  Makefile.0  Makefile.lua  runme.lua
Всё собралось.
Небольшая ошибочка (сообщение) - это связано с запуском тестового примера.
Но сделаем это так:

Код: Выделить всё

bash-4.2$ lua runme.lua
The gcd of      42      and     105     is      21
Foo =   3
Foo =   3.1415926
P.S. Почистить следы всего сделанного - так:

Код: Выделить всё

bash-4.2$ make -f Makefile.lua clean
make -f .//Makefile lua_clean
make[1]: Вход в каталог `/home/olej/2013_WORK/Python/CPython/swig/lua'
rm -f *_wrap* *~ .~* mylua
rm -f core 
rm -f *.o *.so
make[1]: Выход из каталога `/home/olej/2013_WORK/Python/CPython/swig/lua'
P.P.S. Там великое множество примеров, и разобраться с ними - это простейший способ досконально понять SWIG.
Вложения
lua.tgz
(11.24 КБ) 494 скачивания

Аватара пользователя
Olej
Писатель
Сообщения: 21338
Зарегистрирован: 24 сен 2011, 14:22
Откуда: Харьков
Контактная информация:

Re: связка Python + C/C++

Непрочитанное сообщение Olej » 13 авг 2013, 22:43

... и ещё раз о SWIG ... думаю, что последний ;-)

Интересно, что с SWIG элементарно легко сделать интерфейс (Python и любой другой) не только к своему C коду (собственному написанному), но и к любой уже существующей библиотеке, будь то стандартной, реализующей POSIX вызовы C/C++, хоть к целевой/прикладной, представленной от стороннего производителя.

Я так сделаю интерфейс к стандартным файловым операциям C - определяется только файл интерфейса fileio.i, вообще без какого-либо файла C-кода:

Код: Выделить всё

%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 *);
А вместо файла реализации C на сборке (в Makefile) подключается уже готовая библиотека, содержащая реализации этих вызовов:

Код: Выделить всё

all:    $(TITLE).i
        swig -python $(TITLE).i
        gcc -c -fpic $(TITLE)_wrap.c -DHAVE_CONFIG_H -I/usr/include/python2.7 -I/usr/lib/python2.7/config
        ld -shared $(TITLE)_wrap.o -lc -o _$(TITLE).so
        rm -f *.o
Тестовое приложение:

Код: Выделить всё

#!/usr/bin/python
# -*- coding: utf-8 -*-

from fileio import *
import fileio
from sys import argv

# Copy a file 
def filecopy( source, target ):
    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
Его выполнение - копируются файлы файловыми операциями C, хотя никакой реализации для них мы и не писали:

Код: Выделить всё

bash-4.2$ make test
python fiotest.py in.txt out.txt
скопировано 178 байт
bash-4.2$ ls -l *.txt
-rw-rw-r-- 1 olej olej 178 июня  21 23:48 in.txt
-rw-rw-r-- 1 olej olej 178 авг.  13 22:41 out.txt
Вложения
fileio.tgz
(1.25 КБ) 493 скачивания

Аватара пользователя
Olej
Писатель
Сообщения: 21338
Зарегистрирован: 24 сен 2011, 14:22
Откуда: Харьков
Контактная информация:

Re: связка Python + C/C++

Непрочитанное сообщение Olej » 13 авг 2013, 22:56

Olej писал(а):... и ещё раз о SWIG ... думаю, что последний ;-)
Но в каждой бочке мёда должна быть своя ложка дёгтя :lol: - макроязык SWIG для написания .i файлов может быть весьма мудрёным для случаев чуть посложнее простых, рассмотренных выше. Например, когда мы в проектируемом модуле Python, пишущемся на C, планируем выделять память для структур данных, которые после вызова нужно уничтожать. Например, возврат из функции кортежа...

Пример: в кортеж из 256 элементов занести частоту с которой встречалась каждая из 256 букв ASCII входного текста.

- файл 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;
}


- файл freq.i - ну а здесь ... чёрт ногу сломит :shock: :

Код: Выделить всё

%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[] );
Тестовая задача (там слегка намудрено из-за моей любви к функциональному программированию ;-) ... , но, в общем, всё понятно) :

Код: Выделить всё

#!/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 + "'%c'[0x%x]:%d " % ( i, i, f[ i ] )
print s
И вот как это выглядит:

Код: Выделить всё

bash-4.2$ ./test.py 123
'1'[0x31]:1 '2'[0x32]:1 '3'[0x33]:1
bash-4.2$ ./test.py abrakadabra
'a'[0x61]:5 'b'[0x62]:2 'd'[0x64]:1 'k'[0x6b]:1 'r'[0x72]:2
bash-4.2$ ./test.py "1234123121 abc ab a"
' '[0x20]:3 '1'[0x31]:4 '2'[0x32]:3 '3'[0x33]:2 '4'[0x34]:1 'a'[0x61]:3 'b'[0x62]:2 'c'[0x63]:1
Всё.
Я думаю, показанных примеров и ссылок - более чем достаточно, чтобы разобраться со всеми плюсами и минусами SWIG.
Вложения
frequen.tgz
(5.13 КБ) 490 скачиваний

Аватара пользователя
Olej
Писатель
Сообщения: 21338
Зарегистрирован: 24 сен 2011, 14:22
Откуда: Харьков
Контактная информация:

Re: связка Python + C/C++

Непрочитанное сообщение Olej » 14 авг 2013, 00:48

Olej писал(а):... и ещё раз о SWIG ... думаю, что последний ;-)
Ну, и ещё о SWIG ;-) : SWIG есть в реализации как для Windows, так и для Linux, хотя я разбирался и здесь показывал примеры только под Linux.

Аватара пользователя
Olej
Писатель
Сообщения: 21338
Зарегистрирован: 24 сен 2011, 14:22
Откуда: Харьков
Контактная информация:

Re: связка Python + C/C++

Непрочитанное сообщение Olej » 14 авг 2013, 00:52

Следующий альтернативный инструмент для написания модулей под Python на C - это уже пакет Distutils, это уже из чисто Python-овских проектов (поставляемый в составе стандартной библиотеки Python) .
Olej писал(а): - использование пакета Python distutils, хороший и достаточный пример обсуждается здесь: Python + C API simple example memory leak
Это, вообще то говоря, штатное средство дистрибьюции модулей Python (именно написанных на Python), но у них там есть такое понятие как "extension module" - это расщирения на других языках.

Но его использование я покажу как-то в другой раз... когда сделаю примеры ;-)

Документация по Distutils - здесь.

Аватара пользователя
Olej
Писатель
Сообщения: 21338
Зарегистрирован: 24 сен 2011, 14:22
Откуда: Харьков
Контактная информация:

Re: связка Python + C/C++

Непрочитанное сообщение Olej » 14 авг 2013, 18:02

Olej писал(а):Следующий альтернативный инструмент для написания модулей под Python на C - это уже пакет Distutils, это уже из чисто Python-овских проектов (поставляемый в составе стандартной библиотеки Python) .
Distutils является фактически стандартным менеджером пакетной системы Pthyton, так же как yum или apt являются менеджерами пакетных систем Fedora или Debian (кроме Distutils есть в Python ещё несколько, 3-4 пакетов того же назначения, но они являются последующим развитием Distutils). В силу этого, функции Distutils широкие, и создание (обеспечение создания) пакетов для Python на других языках - только одна из побочных функций пакета, а раз так, то и возможности его в этом ограничены (из документации):
Since the Distutils currently only support C, C++, and Objective-C extensions, these are normally C/C++/Objective-C source files. (Be sure to use appropriate extensions to distinguish C++source files: .cc and .cpp seem to be recognized by both Unix and Windows compilers.)

Т.е. это, конечно не SWIG. Но в функции setup() можно указать использовать для построения SWIG (там же в документации):
However, you can also include SWIG interface (.i) files in the list; the build_ext command knows how to deal with SWIG extensions: it will run SWIG on the interface file and compile the resulting C/C++ file into your extension.
Для построения любого иноязычного пакета для Distutils нужно только определить параметры функции setup(), обычно в файле setup.py, после чего вызывается команда:

Код: Выделить всё

$ python setup.py build
...
Для примера (и сравнения) я приложу полную копию проекта first, уже построенного средствами SWIG выше.

- файл setup.py, здесь главное - определение имени файла C-кода (или списка имён нескольких файлов):

Код: Выделить всё

from distutils.core import setup, Extension

module1 = Extension( 'ownmod',
                     sources = ['ownmod.c'] )

setup( name = 'ownmod',
       version = '1.1',
       description = 'This is a first package',
       ext_modules = [module1]
     )
- файл C-кода ownmod.c (это всего лишь printf(), но вот тут начинаются пляски с бубном ;-) ):

Код: Выделить всё

#include <Python.h>

static PyObject* py_echo( PyObject* self, PyObject* args ) {
    printf( "вывод из экспортированного кода!\n" );
    return Py_None;
}

static PyMethodDef ownmod_methods[] = {
    { "echo", py_echo, METH_NOARGS, "echo function" },
    { NULL, NULL }
};

PyMODINIT_FUNC initownmod() {
    (void)Py_InitModule( "ownmod", ownmod_methods );
}
Дальше совсем просто:

Код: Выделить всё

bash-4.2$ python setup.py build
running build
running build_ext
building 'ownmod' extension
creating build
creating build/temp.linux-i686-2.7
gcc -pthread -fno-strict-aliasing -O2 -g -pipe -Wall -Wp,-D_FORTIFY_SOURCE=2 -fexceptions -fstack-protector --param=ssp-buffer-size=4 -m32 -march=i686 -mtune=atom -fasynchronous-unwind-tables -D_GNU_SOURCE -fPIC -fwrapv -DNDEBUG -O2 -g -pipe -Wall -Wp,-D_FORTIFY_SOURCE=2 -fexceptions -fstack-protector --param=ssp-buffer-size=4 -m32 -march=i686 -mtune=atom -fasynchronous-unwind-tables -D_GNU_SOURCE -fPIC -fwrapv -fPIC -I/usr/include/python2.7 -c ownmod.c -o build/temp.linux-i686-2.7/ownmod.o
creating build/lib.linux-i686-2.7
gcc -pthread -shared -Wl,-z,relro build/temp.linux-i686-2.7/ownmod.o -L/usr/lib -lpython2.7 -o build/lib.linux-i686-2.7/ownmod.so
cp build/lib.linux-i686-2.7/ownmod.so ./
И вот тест выполнения (test.py):

Код: Выделить всё

#!/usr/bin/python
# -*- coding: utf-8 -*-

import ownmod
ownmod.echo()


И само выполнение:

Код: Выделить всё

bash-4.2$ python test.py
вывод из экспортированного кода!
Для сравнения и плюсов и минусов (разных способов построения одного и того же модуля) кажется вполне достаточно...
Вложения
first.tgz
(1.84 КБ) 482 скачивания

Аватара пользователя
Olej
Писатель
Сообщения: 21338
Зарегистрирован: 24 сен 2011, 14:22
Откуда: Харьков
Контактная информация:

Re: связка Python + C/C++

Непрочитанное сообщение Olej » 14 авг 2013, 18:21

Olej писал(а): - файл C-кода ownmod.c (это всего лишь printf(), но вот тут начинаются пляски с бубном ;-) ):
Все эти пляски с бубном досконально описаны в составе документации Python, в руководстве Extending and Embedding the Python Interpreter (я показал ссылку документа для версии Python 2.7.5, но там, прямо на странице, можете выбрать вариант описания, например, для 3.3).

Второй документ, который нужен для написания более-менее развитого кода C/C++, взаимодействующего с окружением Python - это Python/C API Reference Manual. Причём это уже описание - совершенно независимо от способа, которым будет строиться модуль, будь то Distutils, SWIG, или ещё что.

Ответить

Вернуться в «Программирование»

Кто сейчас на конференции

Сейчас этот форум просматривают: нет зарегистрированных пользователей и 9 гостей