อะไรจะเป็นวิธีที่เร็วที่สุดในการสร้าง Python ที่เชื่อมโยงกับไลบรารี C หรือ C ++
(ฉันใช้ Windows หากเรื่องนี้)
อะไรจะเป็นวิธีที่เร็วที่สุดในการสร้าง Python ที่เชื่อมโยงกับไลบรารี C หรือ C ++
(ฉันใช้ Windows หากเรื่องนี้)
คำตอบ:
คุณควรมีลักษณะที่Boost.Python นี่คือคำแนะนำสั้น ๆ ที่นำมาจากเว็บไซต์ของพวกเขา:
Boost Python Library เป็นเฟรมเวิร์กสำหรับเชื่อมต่อ Python และ C ++ ช่วยให้คุณสามารถเปิดเผยฟังก์ชันและวัตถุของคลาส C ++ ได้อย่างรวดเร็วและต่อเนื่องไปยัง Python และในทางกลับกันโดยไม่ต้องใช้เครื่องมือพิเศษ - เพียงแค่คอมไพเลอร์ C ++ ของคุณ มันถูกออกแบบมาเพื่อรวมส่วนต่อประสาน C ++ เข้าด้วยกันโดยไม่เป็นการรุกล้ำดังนั้นคุณไม่ควรเปลี่ยนรหัส C ++ เลยเพื่อห่อหุ้มทำให้ Boost.Python เหมาะสำหรับการเปิดเผยไลบรารีของบุคคลที่สามเป็น Python การใช้เทคนิค metaprogramming ขั้นสูงของห้องสมุดทำให้ผู้ใช้เข้าใจง่ายยิ่งขึ้นดังนั้นรหัสการตัดจะมีลักษณะของภาษาที่นิยามอินเตอร์เฟสแบบอินเทอร์เฟซ (IDL)
ctypesโมดูลเป็นส่วนหนึ่งของห้องสมุดมาตรฐานและดังนั้นจึงมีเสถียรภาพมากขึ้นและสามารถใช้ได้อย่างกว้างขวางกว่าswigซึ่งมีแนวโน้มเสมอที่จะให้ฉันปัญหา
ด้วย ctypes คุณจะต้องตอบสนองการพึ่งพาเวลาในการคอมไพล์ด้วยไพ ธ อนและการผูกของคุณจะทำงานกับไพ ธ อนใด ๆ ที่มี ctypes ไม่ใช่เฉพาะคอมไพล์ที่ถูกคอมไพล์ด้วย
สมมติว่าคุณมีคลาสตัวอย่าง C ++ อย่างง่ายที่คุณต้องการคุยด้วยในไฟล์ชื่อ foo.cpp:
#include <iostream>
class Foo{
public:
void bar(){
std::cout << "Hello" << std::endl;
}
};
เนื่องจาก ctypes สามารถพูดคุยกับฟังก์ชั่น 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
และในที่สุดคุณต้องเขียนตัวห่อหลาม (เช่นใน 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
extern "C" { __declspec(dllexport) Foo* Foo_new(){ return new Foo(); } __declspec(dllexport) void Foo_bar(Foo* foo){ foo->bar(); } }
Foo_delete
ฟังก์ชั่นและเรียกมันว่าอย่างใดอย่างหนึ่งจาก destructor หลามหรือห่อวัตถุในทรัพยากร
วิธีที่เร็วที่สุดที่จะทำเช่นนี้คือการใช้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 นอกจากนี้ในบางระบบไฟล์ส่วนหัวของไพ ธ อนจะอยู่ใน /usr/include/python2.7 ตามวิธีที่คุณติดตั้ง
จากบทช่วยสอน:
SWIG เป็นคอมไพเลอร์ C ++ ที่ค่อนข้างสมบูรณ์พร้อมรองรับคุณสมบัติเกือบทุกภาษา ซึ่งรวมถึงการดำเนินการล่วงหน้าพอยน์เตอร์คลาสการสืบทอดและแม้แต่เทมเพลต C ++ SWIG ยังสามารถใช้กับโครงสร้างแพ็กเกจและคลาสเป็นคลาสพร็อกซีในภาษาเป้าหมาย - เปิดเผยฟังก์ชันการทำงานพื้นฐานในลักษณะที่เป็นธรรมชาติ
ฉันเริ่มการเดินทางของฉันใน Python <-> C ++ ที่มีผลผูกพันจากหน้านี้โดยมีวัตถุประสงค์เพื่อเชื่อมโยงชนิดข้อมูลระดับสูง (พหุมิติ STL หลายมิติกับรายการ Python) :-)
ต้องลองวิธีแก้ปัญหาตามทั้งctypesและboost.python (และไม่ใช่วิศวกรซอฟต์แวร์) ฉันพบว่ามันซับซ้อนเมื่อจำเป็นต้องใช้การเชื่อมข้อมูลระดับสูงในขณะที่ฉันได้พบว่าSWIGง่ายกว่ามากสำหรับกรณีดังกล่าว
ตัวอย่างนี้ใช้ SWIG และได้รับการทดสอบใน Linux (แต่ SWIG นั้นมีให้ใช้และใช้กันอย่างแพร่หลายใน Windows ด้วย)
วัตถุประสงค์คือเพื่อให้ฟังก์ชัน C ++ พร้อมใช้งานสำหรับ Python ที่ใช้เมทริกซ์ในรูปแบบของเวกเตอร์ 2D STL และส่งกลับค่าเฉลี่ยของแต่ละแถว (เป็นเวกเตอร์ STL 1D)
รหัสใน 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
นอกจากนี้ยังมีpybind11
ซึ่งคล้ายกับBoost.Pythonรุ่นน้ำหนักเบาและเข้ากันได้กับคอมไพเลอร์ C ++ ที่ทันสมัยทั้งหมด:
Pytorch
pytorch.org/tutorials/advanced/cpp_extension.htmlทำงานอย่างสมบูรณ์บนVS Community
Windows
ตรวจสอบPyrexหรือCython เป็นภาษาที่เหมือนกับ Python สำหรับการเชื่อมต่อระหว่าง C / C ++ และ Python
สำหรับ 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
swig
, หรือctypes
boost.python
แทนที่จะต้องเขียนโค้ดเพื่อให้ python ทำงานกับ c ++ code ของคุณ ... python ทำงานอย่างหนักเพื่อหา c ++ สมมติว่ามันใช้งานได้จริง
บทความนี้อ้างว่า Python เป็นความต้องการของนักวิทยาศาสตร์โดยทั่วไปบอกว่า: ต้นแบบแรกทุกอย่างใน Python จากนั้นเมื่อคุณต้องการเร่งความเร็วส่วนให้ใช้ SWIG และแปลส่วนนี้เป็น C
ฉันไม่เคยใช้มัน แต่ผมเคยได้ยินสิ่งที่ดีเกี่ยวctypes ถ้าคุณกำลังพยายามที่จะใช้มันกับ C ++ ให้แน่ใจว่าชื่อ mangling extern "C"
หลบเลี่ยงผ่าน ขอบคุณสำหรับความคิดเห็น Florian Bösch
ฉันคิดว่า cffi สำหรับ python สามารถเป็นตัวเลือกได้
เป้าหมายคือโทรหารหัส C จาก Python คุณควรจะสามารถทำได้โดยไม่ต้องเรียนรู้ภาษาที่ 3: ทุก ๆ ทางเลือกต้องการให้คุณเรียนรู้ภาษาของตนเอง (Cython, SWIG) หรือ API (ctypes) ดังนั้นเราจึงลองสมมติว่าคุณรู้จัก Python และ C และย่อส่วนเล็กน้อยของ API ที่คุณต้องเรียนรู้
คำถามคือวิธีการเรียกใช้ฟังก์ชัน C จาก Python ถ้าฉันเข้าใจถูกต้อง ทางออกที่ดีที่สุดคือ 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
สำหรับคำแนะนำรายละเอียดของคุณอาจต้องการที่จะอ้างถึงบทความบล็อกของฉัน
หนึ่งในเอกสารหลามอย่างเป็นทางการมีรายละเอียดเกี่ยวกับการขยายหลามใช้ C แม้จะไม่มีการใช้SWIGแต่ก็ค่อนข้างตรงไปตรงมาและทำงานได้ดีบน Windows
Cython เป็นวิธีที่แน่นอนที่จะไปเว้นแต่คุณคาดว่าจะมีการเขียน wraps Java ซึ่งในกรณีนี้อาจจะดีกว่า SWIG
ฉันแนะนำให้ใช้runcython
อรรถประโยชน์บรรทัดคำสั่งมันทำให้กระบวนการใช้ Cython ง่ายมาก หากคุณต้องการส่งข้อมูลที่มีโครงสร้างไปยัง C ++ ลองดูที่ห้องสมุด protobuf ของ Google มันสะดวกมาก
นี่คือตัวอย่างเล็กน้อยที่ฉันทำซึ่งใช้ทั้งสองเครื่องมือ
https://github.com/nicodjimenez/python2cpp
หวังว่ามันจะเป็นจุดเริ่มต้นที่มีประโยชน์
ก่อนอื่นคุณควรตัดสินใจว่าเป้าหมายของคุณคืออะไร เอกสารงูใหญ่อย่างเป็นทางการในการขยายและการฝังล่ามงูใหญ่ได้กล่าวถึงข้างต้นผมสามารถเพิ่มดีภาพรวมของส่วนขยายไบนารี กรณีการใช้งานสามารถแบ่งออกเป็น 3 ประเภท:
เพื่อที่จะให้มุมมองที่กว้างขึ้นสำหรับความสนใจอื่น ๆ และเนื่องจากคำถามเริ่มต้นของคุณค่อนข้างคลุมเครือ ("ถึง C หรือ C ++ ไลบรารี่") ฉันคิดว่าข้อมูลนี้อาจเป็นที่สนใจของคุณ ในลิงก์ด้านบนคุณสามารถอ่านข้อเสียของการใช้ส่วนขยายไบนารีและทางเลือกอื่นได้
นอกเหนือจากคำตอบอื่น ๆ ที่แนะนำถ้าคุณต้องการโมดูลคันเร่งคุณสามารถลองNumba ใช้งานได้ "โดยการสร้างรหัสเครื่องที่ดีที่สุดโดยใช้โครงสร้างพื้นฐานคอมไพเลอร์ LLVM ณ เวลานำเข้ารันไทม์หรือแบบคงที่ (ใช้เครื่องมือ pycc ที่รวมอยู่)"
ฉันชอบ cppyy ทำให้ง่ายต่อการขยาย Python ด้วยรหัส C ++ เพิ่มประสิทธิภาพอย่างมากเมื่อต้องการ
มันมีประสิทธิภาพและตรงไปตรงมาใช้ง่ายมาก
นี่คือตัวอย่างของวิธีที่คุณสามารถสร้างอาร์เรย์ numpy และส่งผ่านไปยังฟังก์ชันสมาชิกคลาสใน C ++
cppyy_test.py
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 ++ ซ้ำได้ตลอดเวลา
pybind11 ตัวอย่างน้อยที่สุด runnable
pybind11 ได้รับการกล่าวถึงก่อนหน้านี้ที่https://stackoverflow.com/a/38542539/895245แต่ฉันต้องการยกตัวอย่างการใช้งานที่เป็นรูปธรรมและอภิปรายเพิ่มเติมเกี่ยวกับการใช้งานที่นี่
ทั้งหมดและทั้งหมดฉันขอแนะนำ pybind11 เพราะมันใช้งานง่ายจริง ๆ : คุณเพียงแค่ใส่ส่วนหัวและจากนั้น pybind11 ใช้เทมเพลตเวทย์มนตร์เพื่อตรวจสอบคลาส C ++ ที่คุณต้องการเปิดเผยให้ Python และเห็นได้อย่างชัดเจน
ข้อเสียของเทมเพลตนี้คือการรวบรวมช้าลงทันทีเพิ่มสองสามวินาทีในไฟล์ใด ๆ ที่ใช้ pybind11 ดูตัวอย่างการตรวจสอบที่ทำกับปัญหานี้ PyTorch ตกลง
นี่เป็นตัวอย่างที่ทำงานได้น้อยที่สุดเพื่อให้คุณรู้สึกว่า pybind11 ยอดเยี่ยมคืออะไร:
class_test.cpp
#include <string>
#include <pybind11/pybind11.h>
struct ClassTest {
ClassTest(const std::string &name) : name(name) { }
void setName(const std::string &name_) { name = name_; }
const std::string &getName() const { return name; }
std::string name;
};
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 &>())
.def("setName", &ClassTest::setName)
.def("getName", &ClassTest::getName)
.def_readwrite("name", &ClassTest::name);
return m.ptr();
}
class_test_main.py
#!/usr/bin/env python3
import class_test
my_class_test = class_test.ClassTest("abc");
print(my_class_test.getName())
my_class_test.setName("012")
print(my_class_test.getName())
assert(my_class_test.getName() == my_class_test.name)
รวบรวมและเรียกใช้:
#!/usr/bin/env bash
set -eux
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
ตัวอย่างนี้แสดงให้เห็นว่า pybind11 ช่วยให้คุณเปิดเผยClassTest
คลาส C ++ กับ Python ได้อย่างง่ายดาย! การคอมไพล์สร้างไฟล์ที่มีชื่อclass_test.cpython-36m-x86_64-linux-gnu.so
ซึ่งclass_test_main.py
รับโดยอัตโนมัติเป็นจุดนิยามสำหรับclass_test
โมดูลที่กำหนดโดยกำเนิด
บางทีการตระหนักถึงความยอดเยี่ยมที่ว่านี้เป็นเพียงการจมถ้าคุณพยายามทำสิ่งเดียวกันด้วยมือกับ Python API ดั้งเดิมดูตัวอย่างของการทำเช่นนี้ซึ่งมีรหัสเพิ่มเติมอีกประมาณ 10x: 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.Python ที่ยอดเยี่ยมโดย David Abrahams: เพื่อลดรหัสสำเร็จรูปในโมดูลส่วนขยายแบบดั้งเดิมโดยการอนุมานข้อมูลประเภทโดยใช้การรวบรวมข้อมูลในเวลารวบรวม
ปัญหาหลักของ Boost.Python และสาเหตุของการสร้างโครงการที่คล้ายกันนั้นคือ Boost Boost เป็นชุดของยูทิลิตี้ไลบรารีขนาดใหญ่และซับซ้อนที่ทำงานร่วมกับคอมไพเลอร์ C ++ เกือบทุกตัวที่มีอยู่ ความเข้ากันได้นี้มีค่าใช้จ่าย: เทคนิคเทมเพลต arcane และวิธีแก้ไขปัญหาเป็นสิ่งจำเป็นเพื่อสนับสนุนตัวอย่างคอมไพเลอร์ที่เก่าแก่ที่สุด ตอนนี้คอมไพเลอร์ที่เข้ากันได้กับ C ++ 11 มีวางจำหน่ายอย่างกว้างขวางแล้วเครื่องจักรกลหนักนี้จึงกลายเป็นสิ่งที่ต้องพึ่งพามากและไม่จำเป็น
คิดว่าไลบรารี่นี้เป็น Boost.Python เวอร์ชันที่มีอยู่ในตัวเองพร้อมกับทุกอย่างที่ถูกถอดออกซึ่งไม่เกี่ยวข้องกับการสร้างผูกพัน หากไม่มีความคิดเห็นไฟล์ส่วนหัวหลักต้องการเพียงบรรทัดของโค้ด ~ 4K และขึ้นอยู่กับ Python (2.7 หรือ 3.x หรือ PyPy2.7> = 5.7) และไลบรารีมาตรฐาน C ++ การใช้งานขนาดกะทัดรัดนี้เป็นไปได้ด้วยคุณสมบัติภาษา C ++ 11 ใหม่บางอย่าง (โดยเฉพาะ: tuples, ฟังก์ชั่นแลมบ์ดาและเท็มเพลตแบบแปรผัน) นับตั้งแต่การสร้างไลบรารีนี้ได้เติบโตเกินกว่า 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 ( ไฟล์เก็บถาวร )
ทดสอบกับ Ubuntu 18.04, pybind11 2.0.1, Python 3.6.8, GCC 7.4.0