Как стать автором
Обновить

Расширение для python на с++ с использованием swig и qmake

Написание расширения для python на с++ задача не совсем тривиальная, так как python предлагает писать расширения на C. А как быть если нужно сделать wrapper для с++ кода или C не подходит по религиозным соображениям? После некоторого гугления я нашел аж 2 инструмента для создания расширений для языка python: sip и swig. При помощи первого сделан PyQt, второй же интересен тем, что позволяет делать обертки нативного кода не только для python, но и для perl, ruby, java и еще целой кучи языков разной нужности и полезности. Свой выбор я остановил на swig, поскольку к нему есть неплохая документация и сам он довольно прост в использовании.

Приведенный ниже код тестировался и писался в 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())
Теги:
Хабы:
Данная статья не подлежит комментированию, поскольку её автор ещё не является полноправным участником сообщества. Вы сможете связаться с автором только после того, как он получит приглашение от кого-либо из участников сообщества. До этого момента его username будет скрыт псевдонимом.