Написание расширения для python на с++ задача не совсем тривиальная, так как python предлагает писать расширения на C. А как быть если нужно сделать wrapper для с++ кода или C не подходит по религиозным соображениям? После некоторого гугления я нашел аж 2 инструмента для создания расширений для языка python: sip и swig. При помощи первого сделан PyQt, второй же интересен тем, что позволяет делать обертки нативного кода не только для python, но и для perl, ruby, java и еще целой кучи языков разной нужности и полезности. Свой выбор я остановил на swig, поскольку к нему есть неплохая документация и сам он довольно прост в использовании.
Приведенный ниже код тестировался и писался в ArchLinux для python3(он же python3k), но должен легко собраться и заработать на любой другой платформе и другой версии интерпретатора.
Для начала напишем простой класс, который будем «заворичивать» в python:
adder.h:
adder.cpp:
Ничего сверхестественного этот код не делает и понятия не имеет о том, что скоро будет работать внутри python.
Магия начинается в интерфейсном файле swig, adder.i:
Итак, у нас есть класс на с++, есть интерфейсный файл для swig и желаение сделать из этого расширение для python. Для успешного удовлетворения этого желание необходимо:
И если первое решается довольно просто:
То второе уже несколько сложнее ибо у нас уже не один сpp-файл, а 2, и при сборке их надо слинковать с libpython и добавить путь для инклудов.
Вопрос с компиляцией я решил как заядлый qt'шник, с помощью qmake. Попутно также решилась проблема с вызовом swig при сборке. В итоге у меня получилось:
swig.pri:
adder.pro
Для сборки расширения все эти файлы необходимо сложить в одну директорию, затем в ней выполнить
После сборки в директории появятся файлы _adder.so(собственно бинарное расширение) и adder.py(python-обертка для него, сгенерирована swig'ом)
Проверить работу расширения можно выполнив в той же папке py-файл слудующиего содержания:
Приведенный ниже код тестировался и писался в ArchLinux для python3(он же python3k), но должен легко собраться и заработать на любой другой платформе и другой версии интерпретатора.
Для начала напишем простой класс, который будем «заворичивать» в python:
adder.h:
#ifndef ADDER_H
#define ADDER_H
class Adder{
public:
Adder();
void add(int);
int sum();
private:
int m_sum;
};
#endif // ADDER_H
adder.cpp:
#include <iostream>
#include "adder.h"
using namespace std;
Adder::Adder ()
{
m_sum=0;
}
void Adder::add(int x)
{
m_sum+=x;
}
int Adder::sum()
{
return m_sum;
}
Ничего сверхестественного этот код не делает и понятия не имеет о том, что скоро будет работать внутри python.
Магия начинается в интерфейсном файле swig, adder.i:
%module adder /* Название нашего модуля */
/*
Блок между %{ и }% копирутся в код обертки без изменений
*/
%{
#include "adder.h" /* Подключение хедера */
%}
/*
Тут может быть описание классов, функций и переменных, которые необходимо
"экспортировать". В нашем случае это весь класс, поэтому просто подлючеам
его хедер. Private и protected члены класса будут скрыты.
*/
%include "adder.h"
Итак, у нас есть класс на с++, есть интерфейсный файл для swig и желаение сделать из этого расширение для python. Для успешного удовлетворения этого желание необходимо:
- Получить из интерфейсного файла код обертки на с++ и python-код для взаимодействия с ней.
- Скомпилировать наш класс и полученный код враппера в библиотеку.
И если первое решается довольно просто:
$ swig -c++ -python -o adder_wrap.cpp adder.i
То второе уже несколько сложнее ибо у нас уже не один сpp-файл, а 2, и при сборке их надо слинковать с libpython и добавить путь для инклудов.
Вопрос с компиляцией я решил как заядлый qt'шник, с помощью qmake. Попутно также решилась проблема с вызовом swig при сборке. В итоге у меня получилось:
- pro-файла с интрукциями по сборке библиотеки
- pri-файл с инструкциями по вызову swig и правилами очистки.
swig.pri:
isEmpty(SWIG_DIR):SWIG_DIR = .
isEmpty(SWIG_CMD):SWIG_CMD = swig -python -c++
swig.name = SWIG ${QMAKE_FILE_IN}
swig.commands = $$SWIG_CMD -o $$SWIG_DIR/${QMAKE_FILE_BASE}_wrap.cpp ${QMAKE_FILE_NAME}
swig.variable_out = SOURCES
swig.output = $$SWIG_DIR/${QMAKE_FILE_BASE}_wrap.cpp
swig.input = SWIG_FILES
swig.clean = $$SWIG_DIR/${QMAKE_FILE_BASE}_wrap.cpp $$SWIG_DIR/${QMAKE_FILE_BASE}.py
QMAKE_EXTRA_COMPILERS += swig
adder.pro
TEMPLATE=lib
TARGET=_adder
CONFIG += plugin no_plugin_name_prefix
SOURCES+= adder.cpp
HEADERS+= adder.h
SWIG_FILES = adder.i
LIBS+=-lpython3//Для python 2.7 - LIBS+=-lpython2.7
INCLUDEPATH+=/usr/include/python3.2mu/ //Для python 2.7 - /usr/include/python2.7/
include(swig.pri)
Для сборки расширения все эти файлы необходимо сложить в одну директорию, затем в ней выполнить
$ qmake
$ make
После сборки в директории появятся файлы _adder.so(собственно бинарное расширение) и adder.py(python-обертка для него, сгенерирована swig'ом)
Проверить работу расширения можно выполнив в той же папке py-файл слудующиего содержания:
import adder
a=adder.Adder()
for i in range(10):
a.add(i)
print(a.sum())