Вызов C / C ++ из Python? - PullRequest
       103

Вызов C / C ++ из Python?

464 голосов
/ 28 сентября 2008

Какой самый быстрый способ создать привязку Python к библиотеке C или C ++?

(я использую Windows, если это имеет значение.)

Ответы [ 14 ]

588 голосов
/ 28 сентября 2008

ctypes является частью стандартной библиотеки и поэтому является более стабильным и широко доступным, чем swig , что всегда вызывало у меня проблемы .

С помощью ctypes вам нужно удовлетворить любую зависимость времени компиляции от python, и ваша привязка будет работать на любом питоне, имеющем ctypes, а не только на том, с которым он был скомпилирован.

Предположим, у вас есть простой пример класса C ++, с которым вы хотите поговорить, в файле с именем foo.cpp:

#include <iostream>

class Foo{
    public:
        void bar(){
            std::cout << "Hello" << std::endl;
        }
};

Так как ctypes может общаться только с функциями C, вам нужно предоставить тех, кто объявляет их как внешние "C"

extern "C" {
    Foo* Foo_new(){ return new Foo(); }
    void Foo_bar(Foo* foo){ foo->bar(); }
}

Далее вы должны скомпилировать это в общую библиотеку

g++ -c -fPIC foo.cpp -o foo.o
g++ -shared -Wl,-soname,libfoo.so -o libfoo.so  foo.o

И, наконец, вы должны написать свою оболочку Python (например, в fooWrapper.py)

from ctypes import cdll
lib = cdll.LoadLibrary('./libfoo.so')

class Foo(object):
    def __init__(self):
        self.obj = lib.Foo_new()

    def bar(self):
        lib.Foo_bar(self.obj)

Если у вас есть это, вы можете назвать это как

f = Foo()
f.bar() #and you will see "Hello" on the screen
147 голосов
/ 28 сентября 2008

Вы должны взглянуть на Boost.Python . Вот краткое введение, взятое с их сайта:

Boost Python Library - это инфраструктура для взаимодействия Python и C ++. Это позволяет вам быстро и без проблем выставлять классы C ++ функции и объекты в Python, и наоборот, не используя специальные инструменты - просто ваш компилятор C ++. Он предназначен для упаковки интерфейсов C ++ не навязчиво, так что вам не придется менять код C ++ в все для того, чтобы обернуть его, что делает Boost.Python идеальным для разоблачения Сторонние библиотеки на Python. Библиотека использует передовые Метапрограммирование упрощает его синтаксис для пользователей, так что код переноса выглядит как декларативный интерфейс язык определения (IDL).

51 голосов
/ 28 сентября 2008

Самый быстрый способ сделать это - использовать SWIG .

Пример из учебника SWIG :

/* File : example.c */
int fact(int n) {
    if (n <= 1) return 1;
    else return n*fact(n-1);
}

Файл интерфейса:

/* example.i */
%module example
%{
/* Put header files here or function declarations like below */
extern int fact(int n);
%}

extern int fact(int n);

Сборка модуля Python в Unix:

swig -python example.i
gcc -fPIC -c example.c example_wrap.c -I/usr/local/include/python2.7
gcc -shared example.o example_wrap.o -o _example.so

Использование:

>>> import example
>>> example.fact(5)
120

Обратите внимание, что у вас должен быть python-dev. Также в некоторых системах заголовочные файлы python будут находиться в /usr/include/python2.7 в зависимости от способа его установки.

Из учебника:

SWIG - довольно полный C ++ -компилятор с поддержкой практически всех языковых функций. Это включает в себя предварительную обработку, указатели, классы, наследование и даже шаблоны C ++. SWIG также можно использовать для упаковки структур и классов в прокси-классы на целевом языке, что очень естественным образом раскрывает основные функции.

45 голосов
/ 26 мая 2014

Я начал свое путешествие с привязки Python <-> C ++ с этой страницы с целью связать высокоуровневые типы данных (многомерные векторы STL со списками Python): -)

Испытав решения на основе ctypes и boost.python (и не будучи инженером-программистом), я нашел их сложными, когда требуется связывание типов данных высокого уровня, в то время как я нашли SWIG гораздо проще для таких случаев.

Таким образом, в этом примере используется SWIG, и он был протестирован в Linux (но SWIG доступен и также широко используется в Windows).

Цель состоит в том, чтобы сделать функцию C ++ доступной для Python, которая принимает матрицу в форме 2D-вектора STL и возвращает среднее значение каждой строки (как 1D-вектор STL).

Код на C ++ ("code.cpp") выглядит следующим образом:

#include <vector>
#include "code.h"

using namespace std;

vector<double> average (vector< vector<double> > i_matrix) {

  // Compute average of each row..
  vector <double> averages;
  for (int r = 0; r < i_matrix.size(); r++){
    double rsum = 0.0;
    double ncols= i_matrix[r].size();
    for (int c = 0; c< i_matrix[r].size(); c++){
      rsum += i_matrix[r][c];
    }
    averages.push_back(rsum/ncols);
  }
  return averages;
}

Эквивалентный заголовок ("code.h"):

#ifndef _code
#define _code

#include <vector>

std::vector<double> average (std::vector< std::vector<double> > i_matrix);

#endif

Сначала мы скомпилируем код C ++ для создания объектного файла:

g++ -c -fPIC code.cpp

Затем мы определяем файл определения интерфейса SWIG ("code.i") для наших функций C ++.

%module code
%{
#include "code.h"
%}
%include "std_vector.i"
namespace std {

  /* On a side note, the names VecDouble and VecVecdouble can be changed, but the order of first the inner vector matters! */
  %template(VecDouble) vector<double>;
  %template(VecVecdouble) vector< vector<double> >;
}

%include "code.h"

Используя SWIG, мы генерируем исходный код интерфейса C ++ из файла определения интерфейса SWIG.

swig -c++ -python code.i

Наконец, мы скомпилировали сгенерированный исходный файл интерфейса C ++ и соединили все вместе, чтобы сгенерировать разделяемую библиотеку, которая напрямую импортируется Python (значение «_»):

g++ -c -fPIC code_wrap.cxx  -I/usr/include/python2.7 -I/usr/lib/python2.7
g++ -shared -Wl,-soname,_code.so -o _code.so code.o code_wrap.o

Теперь мы можем использовать функцию в скриптах Python:

#!/usr/bin/env python

import code
a= [[3,5,7],[8,10,12]]
print a
b = code.average(a)
print "Assignment done"
print a
print b
27 голосов
/ 28 сентября 2008

Проверьте Pyrex или Cython . Это Python-подобные языки для взаимодействия между C / C ++ и Python.

20 голосов
/ 23 июля 2016

Существует также pybind11, который похож на облегченную версию Boost.Python и совместим со всеми современными компиляторами C ++:

https://pybind11.readthedocs.io/en/latest/

18 голосов
/ 28 сентября 2008

В этой статье, утверждающей, что Python - это все, что нужно ученому , в основном говорится: Первый прототип всего на Python. Затем, когда вам нужно ускорить часть, используйте SWIG и переведите эту часть в C.

15 голосов
/ 28 сентября 2008

Я никогда не использовал его, но слышал хорошие слова о ctypes . Если вы пытаетесь использовать его с C ++, избегайте искажения имени с помощью extern "C". Спасибо за комментарий, Флориан Бёш.

14 голосов
/ 06 марта 2018

Для современного C ++ используйте cppyy: http://cppyy.readthedocs.io/en/latest/

Он основан на Cling, интерпретаторе C ++ для Clang / LLVM. Привязки выполняются во время выполнения, и дополнительный промежуточный язык не требуется. Благодаря Clang он поддерживает C ++ 17.

Установите его, используя пункт:

    $ pip install cppyy

Для небольших проектов просто загрузите соответствующую библиотеку и интересующие вас заголовки. Например. взять код из примера ctypes, этот поток, но разделен на разделы заголовка и кода:

    $ cat foo.h
    class Foo {
    public:
        void bar();
    };

    $ cat foo.cpp
    #include "foo.h"
    #include <iostream>

    void Foo::bar() { std::cout << "Hello" << std::endl; }

Скомпилируйте это:

    $ g++ -c -fPIC foo.cpp -o foo.o
    $ g++ -shared -Wl,-soname,libfoo.so -o libfoo.so  foo.o

и используйте его:

    $ python
    >>> import cppyy
    >>> cppyy.include("foo.h")
    >>> cppyy.load_library("foo")
    >>> from cppyy.gbl import Foo
    >>> f = Foo()
    >>> f.bar()
    Hello
    >>>

Большие проекты поддерживаются с автоматической загрузкой подготовленной информации об отражении и фрагментов cmake для их создания, так что пользователи установленных пакетов могут просто запустить:

    $ python
    >>> import cppyy
    >>> f = cppyy.gbl.Foo()
    >>> f.bar()
    Hello
    >>>

Благодаря LLVM возможны расширенные функции, такие как автоматическое создание шаблона. Чтобы продолжить пример:

    >>> v = cppyy.gbl.std.vector[cppyy.gbl.Foo]()
    >>> v.push_back(f)
    >>> len(v)
    1
    >>> v[0].bar()
    Hello
    >>>

Примечание: я автор cppyy.

11 голосов
/ 05 ноября 2013

Я думаю, что CFFI для Python может быть вариантом.

Цель - вызвать C-код из Python. Вы должны быть в состоянии сделать это без изучения третьего языка: каждая альтернатива требует от вас изучать собственный язык (Cython, SWIG) или API (ctypes). Итак, мы попробовали предположить, что вы знаете Python и C и минимизировать лишние биты API, который вам нужно выучить.

http://cffi.readthedocs.org/en/release-0.7/

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...