programing

Python에서 C/C++를 호출하시겠습니까?

bestcode 2022. 7. 10. 21:21
반응형

Python에서 C/C++를 호출하시겠습니까?

C 또는 C++ 라이브러리에 대한 Python 바인딩을 구축하는 가장 빠른 방법은 무엇입니까?

(이 문제가 되면 Windows를 사용하고 있습니다.)

ctypes 모듈은 표준 라이브러리의 일부이기 때문에 swig보다 안정적이고 광범위하게 사용할 수 있습니다.그것은 항상 나에게 문제를 주는 경향이 있었습니다.

ctypes에서는 python에 대한 컴파일 시간 의존성을 충족해야 합니다.또한 ctype을 가진 python에서도 바인딩을 실행할 수 있습니다.

foo.cpp라는 파일에 대화하는 간단한 C++ 예제 클래스가 있다고 가정합니다.

#include <iostream>

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

ctype은 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

Boost를 봐야 합니다.Python. 다음은 그들의 웹사이트에서 얻은 짧은 소개입니다.

Boost Python Library는 Python과 C++의 인터페이스를 위한 프레임워크입니다.C++ 클래스의 함수와 오브젝트를 Python에 빠르고 심리스하게 노출할 수 있습니다.또, 특별한 툴은 사용하지 않고, C++ 컴파일러만 사용할 수 있습니다.C++ 인터페이스를 비내접적으로 랩하도록 설계되어 있기 때문에 랩하기 위해 C++ 코드를 변경할 필요가 없어 Boost가 됩니다.Python은 타사 라이브러리를 Python에 노출하는 데 이상적입니다.라이브러리의 고급 메타프로그래밍 기술을 사용하면 사용자의 구문이 간소화되므로 래핑 코드가 일종의 선언적 인터페이스 정의 언어(IDL)처럼 보입니다.

, there있있 there 도 있다.pybind11가벼운 버전의 Boost와 비슷합니다.Python 및 모든 최신 C++ 컴파일러와 호환됩니다.

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

최신 C++의 경우 cppyy를 사용합니다.http://cppyy.readthedocs.io/en/latest/

Clang/LLVM의 C++ 인터프리터인 Cling을 기반으로 합니다.바인딩은 런타임에 실행되며 추가 중간 언어는 필요하지 않습니다.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 fragment를 자동으로 로드하여 작성할 수 있으므로 설치된 패키지 사용자가 다음 작업을 쉽게 수행할 수 있습니다.

    $ 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의 저자입니다.

pybind11 최소 실행 가능 예시

pybind11은 이전에 https://stackoverflow.com/a/38542539/895245에서 언급되었지만, 구체적인 사용 예시와 구현에 대한 몇 가지 논의를 여기서 하고 싶습니다.

모두 pybind11을 추천하는 이유는 매우 사용하기 쉽기 때문입니다.헤더만 포함하면 pybind11은 템플릿 매직으로 Python에 공개하고 싶은 C++ 클래스를 검사하고 투명하게 실행할 수 있기 때문입니다.

이 템플릿매직의 단점은 pybind11을 사용하는 파일에 몇 초간 추가되는 즉시 컴파일이 느려진다는 것입니다.를 들어 이 문제에 대한 조사를 참조하십시오.PyTorch는 동의한다.이 문제를 해결하기 위한 제안서는 https://github.com/pybind/pybind11/pull/2445에서 작성되었습니다.

다음은 pybind11이 얼마나 멋진지 느낄 수 있는 최소한의 실행 가능한 예입니다.

class_test.cpp

#include <string>

#include <pybind11/pybind11.h>

struct ClassTest {
    ClassTest(const std::string &name, int i) : name(name), i(i) { }
    void setName(const std::string &name_) { name = name_; }
    const std::string getName() const { return name + "z"; }
    void setI(const int i) { this->i = i; }
    const int getI() const { return i + 1; }
    std::string name;
    int i;
};

namespace py = pybind11;

PYBIND11_PLUGIN(class_test) {
    py::module m("my_module", "pybind11 example plugin");
    py::class_<ClassTest>(m, "ClassTest")
        .def(py::init<const std::string &, int>())
        .def("setName", &ClassTest::setName)
        .def("getName", &ClassTest::getName)
        .def_readwrite("name", &ClassTest::name)
        .def("setI", &ClassTest::setI)
        .def("getI", &ClassTest::getI)
        .def_readwrite("i", &ClassTest::i);
    return m.ptr();
}

class_test_main.화이

#!/usr/bin/env python3

import class_test

my_class_test = class_test.ClassTest("abc", 1);
print(my_class_test.getName())
print(my_class_test.getI())
my_class_test.setName("012")
my_class_test.setI(2)
print(my_class_test.getName())
print(my_class_test.getI())
assert(my_class_test.getName() == "012z")
assert(my_class_test.getI() == 3)

컴파일 및 실행:

#!/usr/bin/env bash
set -eux
sudo apt install pybind11-dev
g++ `python3-config --cflags` -shared -std=c++11 -fPIC class_test.cpp \
  -o class_test`python3-config --extension-suffix` `python3-config --libs`
./class_test_main.py

Stdout 출력:

abcz
2
012z
3

다음과 같이 잘못된 유형을 사용하려고 하면:

my_class_test.setI("abc")

예상대로 터집니다.

Traceback (most recent call last):
  File "/home/ciro/test/./class_test_main.py", line 9, in <module>
    my_class_test.setI("abc")
TypeError: setI(): incompatible function arguments. The following argument types are supported:
    1. (self: my_module.ClassTest, arg0: int) -> None

Invoked with: <my_module.ClassTest object at 0x7f2980254fb0>, 'abc'

pybind11을 할 수 .ClassTestC++ Python!

코드로부터 Pybind11의 C++를 자동적으로 인식한다.name는 입니다.std::string Python "Python"에str★★★★★★ 。

파일 이 '파일이 .class_test.cpython-36m-x86_64-linux-gnu.so 중 하나죠.class_test_main.py automatic automatic automatic automatic automatic automatic for for for for for for for for for for for for for for for for for 의 정의점으로 자동 됩니다.class_test네이티브로 정의되어 있습니다.

네이티브 Python API를 사용하여 같은 작업을 수작업으로 수행하려고 할 때에만 이것이 얼마나 멋진지 알 수 있습니다.예를 들어 코드 수가 약 10배 많은 이 예를 참조하십시오.https://github.com/cirosantilli/python-cheat/blob/4f676f62e87810582ad53b2fb426b74eae52aad5/py_from_c/pure.c 이 예에서는 C가 어떻게 동작하는지 알 수 있습니다.코드는 Python 클래스를 포함하는 모든 정보(멤버, 메서드, 추가 메타데이터...)로 고통스럽고 명시적으로 정의해야 합니다.다음 항목도 참조하십시오.

은 pybind11과 합니다.Boost.Python이는 https://stackoverflow.com/a/145436/895245에서 언급되었지만 Boost 프로젝트 내에서의 부풀림으로부터 해방되었기 때문에 보다 미미합니다.

pybind11은 기존 C++ 코드의 Python 바인딩을 주로 작성하기 위해 Python에서 C++ 타입을 노출하는 경량 헤더 전용 라이브러리입니다.목표와 구문은 뛰어난 Boost와 유사합니다.David Abrahams의 Python 라이브러리: 컴파일 타임 인스펙션을 사용하여 유형 정보를 추론하여 기존 확장 모듈의 보일러 플레이트 코드를 최소화합니다.

Boost의 주요 문제Python - 그리고 이와 유사한 프로젝트를 만드는 이유는 Boost입니다.Boost는 현존하는 거의 모든 C++ 컴파일러에서 작동하는 매우 크고 복잡한 유틸리티 라이브러리 스위트입니다.이 호환성에는 비용이 듭니다.가장 오래되고 버그가 많은 컴파일러 샘플을 지원하려면 난해한 템플릿 트릭과 회피책이 필요합니다.C++11 호환 컴파일러를 폭넓게 이용할 수 있게 된 지금, 이 무거운 기계는 지나치게 크고 불필요한 의존관계가 되고 있습니다.

이 라이브러리를 Boost의 작은 자체 버전이라고 생각하면 됩니다.바인딩 생성과 관련이 없는 모든 것을 제거한 Python.코멘트가 없으면 코어 헤더 파일은 최대 4K줄의 코드만 필요하며 Python(2.7 또는 3.x 또는 PyPy 2.7 >= 5.7)과 C++ 표준 라이브러리에 의존합니다.이 콤팩트한 실장은 새로운 C++11 언어 기능(특히 튜플, 람다 함수 및 바리에다 템플릿) 덕분에 가능했습니다.이 라이브러리는 설립 이후 Boost를 넘어 성장했습니다.Python은 여러 가지 방법으로 많은 일반적인 상황에서 훨씬 더 간단한 바인딩 코드를 제공합니다.

또한 pybind11은 현재 Microsoft Python C 바인딩 매뉴얼(https://docs.microsoft.com/en-us/visualstudio/python/working-with-c-cpp-python-in-visual-studio?view=vs-2019 (http://filename)에 기재되어 있는 유일한 대체 대체 소프트웨어입니다.

Ubuntu 18.04, pybind11 2.0.1, Python 3.6.8, GCC 7.4.0에서 테스트 완료.

저는 이 페이지에서 Python <-> C++ 바인딩으로 여행을 시작했습니다.고수준의 데이터 타입(Python 리스트와 다차원 STL 벡터)을 링크하기 위해서입니다:-)

ctype과 boost를 모두 기반으로 솔루션을 시도했습니다.python(소프트웨어 엔지니어가 아님) 높은 수준의 데이터 타입 바인딩이 필요할 때는 복잡하지만 SWIG는 훨씬 단순하다는 것을 알게 되었습니다.

따라서 이 예에서는 SWIG를 사용하고 있으며 Linux에서 테스트되었습니다(단, SWIG는 사용 가능하며 Windows에서도 널리 사용됩니다).

목표는 행렬을 2D STL 벡터 형식으로 사용하고 각 행의 평균을 반환하는 C++ 함수를 Python에서 사용할 수 있도록 하는 것입니다(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;
}

동등한 헤더("코드")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 인터페이스 정의 파일("코드")을 정의합니다.(i")를 사용합니다.

%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를 사용하여 SWIG 인터페이스 정의 파일에서 C++ 인터페이스 소스 코드를 생성합니다.

swig -c++ -python code.i

마지막으로 생성된 C++ 인터페이스 소스 파일을 컴파일하고 모든 것을 링크하여 Python에 의해 직접 Import 가능한 공유 라이브러리를 생성합니다('_'는 다음과 같습니다).

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

가장 빠른 방법은 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);

Unix에서 Python 모듈 구축:

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를 사용하여 구조 및 클래스를 타깃 언어로 프록시 클래스로 패키징할 수도 있습니다.이것에 의해, 기본적인 기능이 지극히 자연스러운 방법으로 공개됩니다.

저는 cppy를 매우 좋아합니다. C++ 코드로 Python을 쉽게 확장할 수 있기 때문에 필요할 때 성능을 대폭 향상시킬 수 있습니다.

파워풀하고 솔직히 사용하기 매우 간단합니다.

여기에서는 numpy 배열을 생성하여 C++의 클래스 멤버 함수에 전달하는 예를 보여 줍니다.

cppy_test.화이

import cppyy
import numpy as np
cppyy.include('Buffer.h')


s = cppyy.gbl.Buffer()
numpy_array = np.empty(32000, np.float64)
s.get_numpy_array(numpy_array.data, numpy_array.size)
print(numpy_array[:20])

Buffer.h

struct Buffer {
  void get_numpy_array(double *ad, int size) {
    for( long i=0; i < size; i++)
        ad[i]=i;
  }
};

Python 모듈을 (CMake를 사용하여) 매우 쉽게 만들 수도 있습니다.이렇게 하면 항상 C++ 코드를 재컴파일하지 않아도 됩니다.

python용 cffi도 옵션이라고 생각합니다.

목표는 Python에서 C 코드를 호출하는 것입니다.제3외국어를 배우지 않아도 됩니다.대안에는 모두 독자적인 언어(Cython, SWIG) 또는 API(ctype)를 익힐 필요가 있습니다.그래서 우리는 당신이 Python과 C를 알고 있다고 가정하고 당신이 배워야 할 API의 추가 비트를 최소화하려고 했습니다.

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

문제는 제가 제대로 이해했다면 Python에서 C 함수를 호출하는 방법입니다.가장 좋은 방법은 Ctypes(BTW는 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

상세한 가이드에 대해서는, 제 블로그 기사를 참조해 주세요.

Java 래퍼를 작성할 예정이 없는 한 Cython이 단연 권장됩니다.이 경우 SWIG가 더 좋을 수 있습니다.

저는 '어울리다'를 하는 것을 합니다.runcython명령줄 유틸리티를 사용하면 Cython을 매우 쉽게 사용할 수 있습니다.구조화된 데이터를 C++에 전달해야 한다면 구글의 protobuf 라이브러리를 보면 매우 편리합니다.

다음은 두 가지 도구를 모두 사용한 최소한의 예입니다.

https://github.com/nicodjimenez/python2cpp

그것이 유용한 출발점이 되기를 바랍니다.

우선 당신의 특별한 목적이 무엇인지 결정해야 합니다.Python 인터프리터의 확장과 임베드에 관한 Python의 공식 문서는 위에서 언급되었으므로 바이너리 확장에 대한 좋은 개요를 추가할 수 있습니다.사용 사례는 다음 3가지 범주로 나눌 수 있습니다.

  • 액셀러레이터 모듈: CPython에서 실행되는 동등한 순수 Python 코드보다 더 빠르게 실행됩니다.
  • wrapper modules: 기존 C 인터페이스를 Python 코드에 노출합니다.
  • Low level system access: CPython 런타임, 운영체제 또는 기본 하드웨어의 하위 레벨 기능에 액세스합니다.

다른 관심사에 대해 좀 더 넓은 시야를 제공하기 위해, 그리고 당신의 첫 번째 질문은 약간 모호하기 때문에 ('C 또는 C++ 라이브러리'에 대한) 이 정보는 당신에게 흥미로울 수 있다고 생각합니다.위 링크에서 바이너리 확장자를 사용할 경우의 단점 및 그 대체 기능에 대해 알아볼 수 있습니다.

제시된 다른 답변과는 별도로 액셀러레이터 모듈을 원하는 경우 Numba를 사용해 보십시오.Import시, 런타임 또는 정적으로 (부속된 pycc 툴을 사용하여) LLVM 컴파일러 인프라스트럭처를 사용하여 최적화된 머신 코드를 생성함으로써 동작합니다.

언급URL : https://stackoverflow.com/questions/145270/calling-c-c-from-python

반응형