it-swarm.com.ru

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

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

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

465
shoosh

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

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

147
Ralph

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

В случае ctypes вам необходимо удовлетворить любую зависимость времени компиляции от python, и ваша привязка будет работать с любым 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
588
Florian Bösch

Самый быстрый способ сделать это - использовать 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 также можно использовать для упаковки структур и классов в прокси-классы на целевом языке, что очень естественным образом раскрывает базовые функции.

51
Ben Hoffstein

Я начал свое путешествие с привязки 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
45
Antonello

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

27
Jason Baker

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

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

21
Tom Wenseleers

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

18
Yuval F

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

15
John

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

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

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

    $ 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.

14
Wim Lavrijsen

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

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

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

11
mrgloom

Один из официальных документов Python содержит сведения о расширение Python с использованием C/C++ . Даже без использования SWIG , это довольно просто и прекрасно работает в Windows.

7
Andrew Edgecombe

Вопрос в том, как вызвать функцию C из Python, если я правильно понял. Тогда лучшим выбором будет Ctypes (кстати, переносимый во всех вариантах Python).

>>> from ctypes import *
>>> libc = cdll.msvcrt
>>> print libc.time(None)
1438069008
>>> printf = libc.printf
>>> printf("Hello, %s\n", "World!")
Hello, World!
14
>>> printf("%d bottles of beer\n", 42)
42 bottles of beer
19

Для подробного руководства вы можете обратиться к моя статья в блоге .

5
Jadav Bheda

Сначала вы должны решить, какова ваша конкретная цель. Официальная Python документация по расширение и встраивание интерпретатора Python была упомянута выше, я могу добавить хороший обзор двоичных расширений , Варианты использования можно разделить на 3 категории:

  • ускорительные модули : работать быстрее, чем эквивалентный чистый Python код, запускаемый в CPython.
  • модули-оболочки : для представления существующих интерфейсов C в коде Python.
  • низкоуровневый доступ к системе : для доступа к низкоуровневым функциям среды выполнения CPython, операционной системы или базового оборудования.

Чтобы дать более широкую перспективу для других заинтересованных лиц, и поскольку ваш первоначальный вопрос немного расплывчатый ("к библиотеке C или C++"), я думаю, что эта информация может быть вам интересна. По ссылке выше вы можете прочитать о недостатках использования бинарных расширений и их альтернатив.

Помимо других предложенных ответов, если вы хотите ускорительный модуль, вы можете попробовать Numba . Он работает "путем генерации оптимизированного машинного кода с использованием инфраструктуры компилятора LLVM во время импорта, во время выполнения или статически (с помощью включенного инструмента pycc)".

5
Yaroslav Nikitenko

Определенно, Cython - это путь, если вы не планируете писать обертки Java, и в этом случае SWIG может быть предпочтительнее.

Я рекомендую использовать утилиту командной строки runcython, она делает процесс использования Cython чрезвычайно простым. Если вам нужно передать структурированные данные в C++, посмотрите библиотеку Protobuf от Google, это очень удобно.

Вот минимальные примеры, которые я сделал, которые используют оба инструмента:

https://github.com/nicodjimenez/python2cpp

Надеюсь, что это может быть полезной отправной точкой.

5
nicodjimenez