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을 할 수 .ClassTest
C++ 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 클래스를 포함하는 모든 정보(멤버, 메서드, 추가 메타데이터...)로 고통스럽고 명시적으로 정의해야 합니다.다음 항목도 참조하십시오.
- python-C++ 확장이 C++ 객체를 가져와 멤버 함수를 호출할 수 있습니까?
- Python 내장 인터프리터에 C++ 클래스 인스턴스 노출
- Python C Extension을 사용하는 클래스(메서드가 아님)의 완전하고 최소한의 예?
- Python을 C++에 삽입하고 Boost를 사용하여 C++ 코드에서 호출 메서드를 호출합니다.파이썬
- Python C++ 확장에서의 상속
은 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
'programing' 카테고리의 다른 글
"Unable to access jarfile" 오류의 원인은 무엇입니까? (0) | 2022.07.10 |
---|---|
Java 웹 앱에서 UTF-8을 작동시키는 방법 (0) | 2022.07.10 |
노드 모듈이 있는 경우 '@vue/cli-plugin-babel' 모듈 때문에 vue-cli-service가 충돌할 수 있습니까? (0) | 2022.07.10 |
Nuxt, Vuex 저장소를 별도의 파일로 분할하면 오류가 발생함: 알 수 없는 변환 유형: 로그인 (0) | 2022.07.10 |
Java Swing revalidate() vs repaint() (0) | 2022.07.10 |