Hướng dẫn can we use c++ and python together? - chúng ta có thể sử dụng c ++ và python cùng nhau không?

Hướng dẫn can we use c++ and python together? - chúng ta có thể sử dụng c ++ và python cùng nhau không?

Python và C, hai trong số những ngôn ngữ phổ biến và lớn nhất đã ra khỏi lập trình. Nhiều ngôn ngữ có thể giao tiếp với nhau rất dễ dàng, nhưng với Python và C thì hơi khó khăn. Nhưng trước tiên hãy bắt đầu với lý do tại sao chúng ta muốn điều này?

Không có nghi ngờ gì về điều đó rằng cả hai ngôn ngữ này đều mạnh mẽ và vô cùng hữu ích. Và đôi khi nó được đền đáp để có hiệu suất thô của ngôn ngữ C đang được sử dụng trong dự án Python, nó chắc chắn có thể giúp các thủ tục như giảm thời gian phản hồi và xử lý.

Những gì chúng ta sẽ cần

Python và C. Đó là nó.

Mật mã

Trong ví dụ này, tôi sẽ sử dụng chức năng Fibonacci đơn giản để chứng minh tất cả.


#include 

// create the function like you normally would in C
int CFib(int n){
    if(n < 2)
        return n;
    else return CFib(n - 1) + CFib(n - 2)
}
// this function will be binding our python version and our C version together
// will only take one and only one non-keyword arguement
static PyObject* fib(PyObject* self, PyObject* args) {
    int n;
    if(!PyArg_ParseTuple(args, "i", &n))
        return NULL;
     return Py_BuildValue("i", CFib(n))
}

Nhập chế độ FullScreenen EXIT Mode FullScreen

Trong mã, chúng ta có thể thấy rằng chúng ta yêu cầu tệp tiêu đề

import ModuleName # really should've chosen a better name
ModuleName.CFib(2)
4, điều này chứa tất cả các phương thức, hàm, thuộc tính có liên quan, v.v. Chúng ta cần cho phép hai ngôn ngữ hoạt động cùng nhau. Trước tiên, chúng tôi bắt đầu bằng cách tạo chức năng bình thường trong C, sau đó sử dụng các phương thức từ tệp tiêu đề Python, chúng tôi tạo lại nó, nhưng với một vài đối số nữa.

Như bạn có thể thấy chúng tôi đang sử dụng các nhà xây dựng và trình phân tích cú pháp bên trong chức năng. Chúng giao tiếp giữa cả hai ngôn ngữ để tạo các phiên bản Python của (các) hàm trong C.

Ngoài ra, chúng tôi cần một tập lệnh thiết lập nhỏ được viết bằng Python

from distutils.core import setup, Extension

setup(name='ModuleName', version='1.0', ext_modules=[Extension('ModuleName', ['Fib.c'])])

Nhập chế độ FullScreenen EXIT Mode FullScreen

Trong mã, chúng ta có thể thấy rằng chúng ta yêu cầu tệp tiêu đề

import ModuleName # really should've chosen a better name
ModuleName.CFib(2)
4, điều này chứa tất cả các phương thức, hàm, thuộc tính có liên quan, v.v. Chúng ta cần cho phép hai ngôn ngữ hoạt động cùng nhau. Trước tiên, chúng tôi bắt đầu bằng cách tạo chức năng bình thường trong C, sau đó sử dụng các phương thức từ tệp tiêu đề Python, chúng tôi tạo lại nó, nhưng với một vài đối số nữa.

Như bạn có thể thấy chúng tôi đang sử dụng các nhà xây dựng và trình phân tích cú pháp bên trong chức năng. Chúng giao tiếp giữa cả hai ngôn ngữ để tạo các phiên bản Python của (các) hàm trong C.

import ModuleName # really should've chosen a better name
ModuleName.CFib(2)
5
import ModuleName # really should've chosen a better name
ModuleName.CFib(2)
6
this will then allow you to call your module from any other python project.

Ngoài ra, chúng tôi cần một tập lệnh thiết lập nhỏ được viết bằng Python

import ModuleName # really should've chosen a better name
ModuleName.CFib(2)

Nhập chế độ FullScreenen EXIT Mode FullScreen

Trong mã, chúng ta có thể thấy rằng chúng ta yêu cầu tệp tiêu đề

import ModuleName # really should've chosen a better name
ModuleName.CFib(2)
4, điều này chứa tất cả các phương thức, hàm, thuộc tính có liên quan, v.v. Chúng ta cần cho phép hai ngôn ngữ hoạt động cùng nhau. Trước tiên, chúng tôi bắt đầu bằng cách tạo chức năng bình thường trong C, sau đó sử dụng các phương thức từ tệp tiêu đề Python, chúng tôi tạo lại nó, nhưng với một vài đối số nữa.

Như bạn có thể thấy chúng tôi đang sử dụng các nhà xây dựng và trình phân tích cú pháp bên trong chức năng. Chúng giao tiếp giữa cả hai ngôn ngữ để tạo các phiên bản Python của (các) hàm trong C.
Click me!
In addition this is a very basic version, you can follow up on how to go more indepth over at The Python tutorial site

Ngoài ra, chúng tôi cần một tập lệnh thiết lập nhỏ được viết bằng Python


Chuỗi C-Wide, null kết thúc thành Python Unicode hoặc null thành không.

u#.so (for shared object). On Windows machines, you typically see .dll (for dynamically linked library).

Py_unicode*+int

Chuỗi rộng C và chiều dài thành Python Unicode, hoặc NULL thành không.

  • W#

  • Đọc/Viết bộ đệm một đoạn đơn sang địa chỉ C và độ dài.

z

Giống như S, cũng không chấp nhận không (đặt c char* thành null).

z#

  • Giống như S#, cũng không chấp nhận không (đặt c char* thành null).

  • (...)

  • theo ...

  • Xây dựng python tuple từ các giá trị c.

[...]

Xây dựng danh sách Python từ các giá trị C.

{...}

Xây dựng từ điển Python từ các giá trị C, các khóa và giá trị xen kẽ.

Mã {...} xây dựng từ điển từ số lượng giá trị C chẵn, các khóa và giá trị xen kẽ. Ví dụ: py_buildvalue ("{Issi}", 23, "zig", "zag", 42) trả về một từ điển như Python's {23: 'zig', 'zag': 42}.

Bất kỳ mã nào bạn viết bằng bất kỳ ngôn ngữ được biên dịch nào như C, C ++ hoặc Java có thể được tích hợp hoặc nhập vào một tập lệnh Python khác. Mã này được coi là một "phần mở rộng".

Một mô -đun mở rộng Python không gì khác hơn là một thư viện C bình thường. Trên các máy Unix, các thư viện này thường kết thúc bằng .so (đối với đối tượng được chia sẻ). Trên máy Windows, bạn thường thấy .dll (cho thư viện được liên kết động).None value. The Python headers define a macro, Py_RETURN_NONE, that does this for us.

Điều kiện tiên quyết để viết phần mở rộng

Để bắt đầu viết tiện ích mở rộng của mình, bạn sẽ cần các tệp tiêu đề Python.

static PyObject *module_func(PyObject *self, PyObject *args) {
   /* Do your stuff here. */
   Py_RETURN_NONE;
}

Trên các máy Unix, điều này thường yêu cầu cài đặt gói dành riêng cho nhà phát triển như Python2.5-DEV.

Người dùng Windows nhận được các tiêu đề này như là một phần của gói khi họ sử dụng trình cài đặt Python nhị phân.

Ngoài ra, người ta cho rằng bạn có kiến ​​thức tốt về C hoặc C ++ để viết bất kỳ tiện ích mở rộng Python nào bằng lập trình C.

struct PyMethodDef {
   char *ml_name;
   PyCFunction ml_meth;
   int ml_flags;
   char *ml_doc;
};

Đầu tiên nhìn vào một phần mở rộng Python

  • Đối với cái nhìn đầu tiên của bạn về mô -đun mở rộng Python, bạn cần nhóm mã thành bốn phần - − This is the name of the function as the Python interpreter presents when it is used in Python programs.

  • Tệp tiêu đề Python.H. − This must be the address to a function that has any one of the signatures described in previous seection.

  • Các chức năng C bạn muốn hiển thị là giao diện từ mô -đun của bạn. − This tells the interpreter which of the three signatures ml_meth is using.

    • Một bảng ánh xạ tên của các chức năng của bạn khi các nhà phát triển Python thấy chúng đến các hàm C bên trong mô -đun mở rộng.

    • Một chức năng khởi tạo.

    • Tệp tiêu đề python.h

  • Bạn cần bao gồm tệp tiêu đề python.h trong tệp nguồn C của bạn, cho phép bạn truy cập vào API Python nội bộ được sử dụng để kết nối mô -đun của bạn vào trình thông dịch. − This is the docstring for the function, which could be NULL if you do not feel like writing one.

Bảng này cần phải được chấm dứt với một sentinel bao gồm các giá trị null và 0 cho các thành viên thích hợp.

Thí dụ

Đối với hàm được xác định ở trên, chúng tôi có bảng ánh xạ phương thức sau-

static PyMethodDef module_methods[] = {
   { "func", (PyCFunction)module_func, METH_NOARGS, NULL },
   { NULL, NULL, 0, NULL }
};

Hàm khởi tạo

Phần cuối cùng của mô -đun mở rộng của bạn là chức năng khởi tạo. Hàm này được gọi bởi trình thông dịch Python khi mô -đun được tải. Cần có chức năng được đặt tên là initModule, trong đó mô -đun là tên của mô -đun.initModule, where Module is the name of the module.

Chức năng khởi tạo cần được xuất từ ​​thư viện bạn sẽ xây dựng. Các tiêu đề Python xác định pymodinit_func để bao gồm các câu thần chú thích hợp để điều đó xảy ra cho môi trường cụ thể mà chúng ta đang biên dịch. Tất cả bạn phải làm là sử dụng nó khi xác định chức năng.

Hàm khởi tạo C của bạn thường có cấu trúc tổng thể sau -

PyMODINIT_FUNC initModule() {
   Py_InitModule3(func, module_methods, "docstring...");
}

Đây là mô tả về hàm py_initmodule3 -

  • Func - Đây là chức năng được xuất. − This is the function to be exported.

  • MODULE_METHODS - Đây là tên bảng ánh xạ được xác định ở trên. − This is the mapping table name defined above.

  • DocString - Đây là nhận xét bạn muốn đưa ra trong phần mở rộng của mình. − This is the comment you want to give in your extension.

Đặt tất cả những điều này lại với nhau trông giống như sau -

#include 

static PyObject *module_func(PyObject *self, PyObject *args) {
   /* Do your stuff here. */
   Py_RETURN_NONE;
}

static PyMethodDef module_methods[] = {
   { "func", (PyCFunction)module_func, METH_NOARGS, NULL },
   { NULL, NULL, 0, NULL }
};

PyMODINIT_FUNC initModule() {
   Py_InitModule3(func, module_methods, "docstring...");
}

Thí dụ

Một ví dụ đơn giản sử dụng tất cả các khái niệm trên -

#include 

static PyObject* helloworld(PyObject* self) {
   return Py_BuildValue("s", "Hello, Python extensions!!");
}

static char helloworld_docs[] =
   "helloworld( ): Any message you want to put here!!\n";

static PyMethodDef helloworld_funcs[] = {
   {"helloworld", (PyCFunction)helloworld, 
      METH_NOARGS, helloworld_docs},
      {NULL}
};

void inithelloworld(void) {
   Py_InitModule3("helloworld", helloworld_funcs,
                  "Extension module example!");
}

Ở đây, hàm Py_BuildValue được sử dụng để xây dựng giá trị Python. Lưu mã trên trong tệp Hello.c. Chúng ta sẽ thấy cách biên dịch và cài đặt mô -đun này để được gọi từ tập lệnh Python.

Xây dựng và cài đặt các tiện ích mở rộng

Gói Distutils làm cho nó rất dễ dàng để phân phối các mô -đun Python, cả các mô -đun Python và Mở rộng thuần túy, theo một cách tiêu chuẩn. Các mô -đun được phân phối ở dạng nguồn và được xây dựng và cài đặt thông qua tập lệnh thiết lập thường được gọi là setup.py như sau.

Đối với mô -đun ở trên, bạn cần chuẩn bị script setup.py sau -

from distutils.core import setup, Extension

setup(name='ModuleName', version='1.0', ext_modules=[Extension('ModuleName', ['Fib.c'])])
0

Bây giờ, hãy sử dụng lệnh sau, sẽ thực hiện tất cả các bước biên dịch và liên kết cần thiết, với các lệnh và cờ trình liên kết đúng và cờ, và sao chép thư viện động kết quả thành một thư mục thích hợp -

from distutils.core import setup, Extension

setup(name='ModuleName', version='1.0', ext_modules=[Extension('ModuleName', ['Fib.c'])])
1

Trên các hệ thống dựa trên UNIX, rất có thể bạn sẽ cần chạy lệnh này dưới dạng root để có quyền ghi vào thư mục gói trang web. Đây thường không phải là một vấn đề trên Windows.

Nhập tiện ích mở rộng

Khi bạn đã cài đặt tiện ích mở rộng của mình, bạn sẽ có thể nhập và gọi tiện ích mở rộng đó trong tập lệnh Python của bạn như sau -

from distutils.core import setup, Extension

setup(name='ModuleName', version='1.0', ext_modules=[Extension('ModuleName', ['Fib.c'])])
2

Điều này sẽ tạo ra kết quả sau -

from distutils.core import setup, Extension

setup(name='ModuleName', version='1.0', ext_modules=[Extension('ModuleName', ['Fib.c'])])
3

Chuyển các tham số chức năng

Vì rất có thể bạn sẽ muốn xác định các chức năng chấp nhận các đối số, bạn có thể sử dụng một trong các chữ ký khác cho các chức năng C. Ví dụ: chức năng sau, chấp nhận một số tham số, sẽ được xác định như thế này -

from distutils.core import setup, Extension

setup(name='ModuleName', version='1.0', ext_modules=[Extension('ModuleName', ['Fib.c'])])
4

Bảng phương thức chứa một mục cho chức năng mới sẽ trông giống như thế này -

from distutils.core import setup, Extension

setup(name='ModuleName', version='1.0', ext_modules=[Extension('ModuleName', ['Fib.c'])])
5

Bạn có thể sử dụng chức năng API Pyarg_ParSetuple để trích xuất các đối số từ một con trỏ PyObject được truyền vào hàm C.

Đối số đầu tiên cho pyarg_parsetuple là đối số Args. Đây là đối tượng bạn sẽ phân tích cú pháp. Đối số thứ hai là một chuỗi định dạng mô tả các đối số khi bạn mong đợi chúng xuất hiện. Mỗi đối số được biểu diễn bởi một hoặc nhiều ký tự trong chuỗi định dạng như sau.

from distutils.core import setup, Extension

setup(name='ModuleName', version='1.0', ext_modules=[Extension('ModuleName', ['Fib.c'])])
6

Biên dịch phiên bản mới của mô -đun của bạn và nhập nó cho phép bạn gọi chức năng mới với bất kỳ số lượng đối số nào thuộc bất kỳ loại -

from distutils.core import setup, Extension

setup(name='ModuleName', version='1.0', ext_modules=[Extension('ModuleName', ['Fib.c'])])
7

Bạn có thể có thể đưa ra nhiều biến thể hơn nữa.

Hàm pyarg_parsetuple

Dưới đây là chữ ký tiêu chuẩn cho hàm pyarg_parsetuple -PyArg_ParseTuple function −

from distutils.core import setup, Extension

setup(name='ModuleName', version='1.0', ext_modules=[Extension('ModuleName', ['Fib.c'])])
8

Hàm này trả về 0 cho các lỗi và giá trị không bằng 0 cho thành công. Tuple là pyobject* đó là đối số thứ hai của hàm C. Ở đây định dạng là một chuỗi C mô tả các đối số bắt buộc và tùy chọn.

Dưới đây là danh sách các mã định dạng cho chức năng pyarg_parsetuple -PyArg_ParseTuple function −

Mã sốLoại cNghĩa
cchar Một chuỗi python có độ dài 1 trở thành c char.
dképMột phao python trở thành một c gấp đôi.
ftrôi nổiMột chiếc phao python trở thành một chiếc phao C.
tôiintMột python int trở thành một c int.
lDàiMột python int trở thành một C dài.
Ldài dàiMột con trăn int trở thành một C dài
OPyobject*Nhận được tài liệu tham khảo không null cho đối số Python.
Schar*Chuỗi python không nhúng null vào c char*.
S#char*+intBất kỳ chuỗi python đến địa chỉ C và độ dài.
T#char*+intBất kỳ chuỗi python đến địa chỉ C và độ dài.
T#Bộ đệm đơn chỉ đọc đến địa chỉ C và độ dài.u
Py_unicode*Python Unicode mà không có null nhúng vào C.u#
Py_unicode*+intchar*+intBất kỳ chuỗi python đến địa chỉ C và độ dài.
T#char*Chuỗi python không nhúng null vào c char*.
z#char*+intGiống như S#, cũng không chấp nhận không (đặt c char* thành null).
(...)theo ...Một chuỗi Python được coi là một đối số cho mỗi mục.
|& nbsp;Các đối số sau đây là tùy chọn.
:& nbsp;Các đối số sau đây là tùy chọn.
:& nbsp;Các đối số sau đây là tùy chọn.

:

Định dạng kết thúc, theo sau là tên chức năng cho thông báo lỗi.

from distutils.core import setup, Extension

setup(name='ModuleName', version='1.0', ext_modules=[Extension('ModuleName', ['Fib.c'])])
9

;

import ModuleName # really should've chosen a better name
ModuleName.CFib(2)
0

Định dạng kết thúc, theo sau là toàn bộ văn bản thông báo lỗi.

import ModuleName # really should've chosen a better name
ModuleName.CFib(2)
1

;

import ModuleName # really should've chosen a better name
ModuleName.CFib(2)
2

Định dạng kết thúc, theo sau là toàn bộ văn bản thông báo lỗi.

Trả lại giá trịPy_BuildValue function −

import ModuleName # really should've chosen a better name
ModuleName.CFib(2)
3

Py_BuildValue có một chuỗi định dạng giống như pyarg_parsetuple. Thay vì chuyển các địa chỉ của các giá trị bạn đang xây dựng, bạn vượt qua các giá trị thực tế. Dưới đây là một ví dụ cho thấy cách thực hiện chức năng Thêm -

Đây là những gì nó sẽ trông như thế nào nếu được thực hiện trong Python -

Bạn có thể trả về hai giá trị từ chức năng của mình như sau, điều này sẽ được kết hợp bằng cách sử dụng danh sách trong Python.Hàm Py_BuildValueDưới đây là chữ ký tiêu chuẩn cho hàm py_buildvalue -
Ở đây định dạng là một chuỗi C mô tả đối tượng Python để xây dựng. Các đối số sau đây của Py_BuildValue là các giá trị C mà từ đó kết quả được xây dựng. Kết quả PyObject* là một tài liệu tham khảo mới.Bảng sau liệt kê các chuỗi mã thường được sử dụng, trong đó không hoặc nhiều hơn được nối vào định dạng chuỗi.Mã số
Loại cNghĩac
charMột c char trở thành một chuỗi python có độ dài 1.d
képMột c gấp đôi trở thành một phao python.f
trôi nổiMột chiếc phao C trở thành một chiếc phao python.tôi
intMột c int trở thành một python int.l
DàiMột c int trở thành một python int.l
DàiMột C dài trở thành Python Int.N
Pyobject*Vượt qua một đối tượng Python và đánh cắp một tài liệu tham khảo.O
Vượt qua một đối tượng Python và incref nó như bình thường.char*+intO &
Chuyển đổi+void*Chuyển đổi tùy ýS
char*C 0-chấm dứt char* thành chuỗi python, hoặc null thành không.S#
C char* và độ dài thành chuỗi python, hoặc null thành không.char*+intu
Py_unicode*Vượt qua một đối tượng Python và đánh cắp một tài liệu tham khảo.O
z#char*+intGiống như S#, cũng không chấp nhận không (đặt c char* thành null).
(...)theo ...Vượt qua một đối tượng Python và incref nó như bình thường.
O &theo ...Chuyển đổi+void*
Chuyển đổi tùy ýtheo ...S

char*