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

каким будет самый быстрый способ создания привязки Python к библиотеке c/" class="blnk">C или c++?

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

14 ответов


вы должны взглянуть на импульс.Питон. Вот краткое введение, взятое с их веб-сайта:

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


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

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

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

#include <iostream>

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

поскольку ctypes может разговаривать только с функциями C, вам нужно предоставить те, которые объявляют их как extern "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

самый быстрый способ сделать это с помощью глоток.

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

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

interface файл:

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


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

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

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

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

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

#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.о"):

#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 файл определения интерфейса ("код.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 (The "_" вопросы):

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

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


появилась pybind11, что похоже на облегченную версию импульс.Питон и совместим со всеми современными компиляторами c++:

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


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


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


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

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

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


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


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

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

установите его с помощью pip:

    $ pip install cppyy

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

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


вопрос в том, как вызвать функцию из C в Python, если я правильно понял. Тогда лучше всего использовать Ctypes (BTW portable во всех вариантах 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

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


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

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

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

https://github.com/nicodjimenez/python2cpp

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


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

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

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

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