欧美vvv,亚洲第一成人在线,亚洲成人欧美日韩在线观看,日本猛少妇猛色XXXXX猛叫

新聞資訊

    賬號(hào)為華為云開發(fā)者社區(qū)官方運(yùn)營賬號(hào),提供全面深入的云計(jì)算前景分析、豐富的技術(shù)干貨、程序樣例,分享華為云前沿資訊動(dòng)態(tài)

    本文分享自華為云社區(qū)《Python 綁定:從 Python 調(diào)用 C 或 C++ |【生長吧!Python!】》,原文作者:Yuchuan 。

    您是擁有想要從 Python 中使用的C或 C++ 庫的 Python 開發(fā)人員嗎?如果是這樣,那么Python 綁定允許您調(diào)用函數(shù)并將數(shù)據(jù)從 Python 傳遞到C或C++,讓您利用這兩種語言的優(yōu)勢(shì)。在本教程中,您將看到一些可用于創(chuàng)建 Python 綁定的工具的概述。

    在本教程中,您將了解:

    • 為什么要從 Python調(diào)用 C 或 C++
    • 如何在 C 和 Python 之間傳遞數(shù)據(jù)
    • 哪些工具和方法可以幫助您創(chuàng)建 Python 綁定

    本教程面向中級(jí) Python 開發(fā)人員。它假定讀者具備 Python 的基本知識(shí),并對(duì) C 或 C++ 中的函數(shù)和數(shù)據(jù)類型有所了解。您可以通過單擊下面的鏈接獲取本教程中將看到的所有示例代碼:

    Python Bindings概述

    在深入研究如何從 Python 調(diào)用 C之前,最好花一些時(shí)間了解為什么. 有幾種情況下,創(chuàng)建 Python 綁定來調(diào)用 C 庫是一個(gè)好主意:

    1. 您已經(jīng)擁有一個(gè)用 C++ 編寫的大型、經(jīng)過測試的穩(wěn)定庫,您想在 Python 中利用它。這可能是一個(gè)通信庫或一個(gè)與特定硬件對(duì)話的庫。它做什么并不重要。
    2. 您希望通過將關(guān)鍵部分轉(zhuǎn)換為 C來加速 Python 代碼的特定部分。 C 不僅具有更快的執(zhí)行速度,而且還允許您擺脫GIL的限制,前提是您小心。
    3. 您想使用 Python 測試工具對(duì)其系統(tǒng)進(jìn)行大規(guī)模測試。

    以上所有都是學(xué)習(xí)創(chuàng)建 Python 綁定以與 C 庫交互的重要原因。

    注意:在本教程中,您將創(chuàng)建到 C和C++ 的Python 綁定。大多數(shù)通用概念適用于兩種語言,因此除非兩種語言之間存在特定差異,否則將使用 C。通常,每個(gè)工具都支持 C或C++,但不能同時(shí)支持兩者。

    讓我們開始吧!

    編組數(shù)據(jù)類型

    等待!在開始編寫 Python 綁定之前,先看看 Python 和 C 如何存儲(chǔ)數(shù)據(jù)以及這會(huì)導(dǎo)致哪些類型的問題。首先,讓我們定義編組。這個(gè)概念由維基百科定義如下:

    將對(duì)象的內(nèi)存表示轉(zhuǎn)換為適合存儲(chǔ)或傳輸?shù)臄?shù)據(jù)格式的過程。

    出于您的目的,編組是 Python 綁定在準(zhǔn)備數(shù)據(jù)以將其從 Python 移動(dòng)到 C 或反之亦然時(shí)所做的工作。Python 綁定需要進(jìn)行編組,因?yàn)?Python 和 C 以不同的方式存儲(chǔ)數(shù)據(jù)。C 在內(nèi)存中以最緊湊的形式存儲(chǔ)數(shù)據(jù)。如果您使用uint8_t,那么它總共將只使用 8 位內(nèi)存。

    另一方面,在 Python 中,一切都是對(duì)象。這意味著每個(gè)整數(shù)在內(nèi)存中使用幾個(gè)字節(jié)。多少取決于您運(yùn)行的 Python 版本、操作系統(tǒng)和其他因素。這意味著您的 Python 綁定將需要為每個(gè)跨邊界傳遞的整數(shù)C 整數(shù)轉(zhuǎn)換為Python 整數(shù)。

    其他數(shù)據(jù)類型在這兩種語言之間具有相似的關(guān)系。讓我們依次來看看:

    • 整數(shù)存儲(chǔ)計(jì)數(shù)數(shù)字。Python 以任意精度存儲(chǔ)整數(shù),這意味著您可以存儲(chǔ)非常非常大的數(shù)字。C 指定整數(shù)的確切大小。在語言之間移動(dòng)時(shí)需要注意數(shù)據(jù)大小,以防止 Python 整數(shù)值溢出 C 整數(shù)變量。
    • 浮點(diǎn)數(shù)是帶有小數(shù)位的數(shù)字。Python 可以存儲(chǔ)比 C 大得多(和小得多)的浮點(diǎn)數(shù)。這意味著您還必須注意這些值以確保它們保持在范圍內(nèi)。
    • 復(fù)數(shù)是帶有虛部的數(shù)字。雖然 Python 具有內(nèi)置復(fù)數(shù),而 C 具有復(fù)數(shù),但沒有用于在它們之間編組的內(nèi)置方法。要封送復(fù)數(shù),您需要在 C 代碼中構(gòu)建struct或class來管理它們。
    • 字符串是字符序列。作為這樣一種常見的數(shù)據(jù)類型,當(dāng)您創(chuàng)建 Python 綁定時(shí),字符串將被證明是相當(dāng)棘手的。與其他數(shù)據(jù)類型一樣,Python 和 C 以完全不同的格式存儲(chǔ)字符串。(與其他數(shù)據(jù)類型不同,這也是 C 和 C++ 不同的領(lǐng)域,這增加了樂趣?。┠鷮⒀芯康拿總€(gè)解決方案都有略微不同的處理字符串的方法。
    • 布爾變量只能有兩個(gè)值。由于它們?cè)?C 中得到支持,因此將它們編組將被證明是相當(dāng)簡單的。

    除了數(shù)據(jù)類型轉(zhuǎn)換之外,在構(gòu)建 Python 綁定時(shí)還需要考慮其他問題。讓我們繼續(xù)探索它們。

    了解可變和不可變值

    除了所有這些數(shù)據(jù)類型之外,您還必須了解 Python 對(duì)象如何可變不可變。當(dāng)談到傳值傳引用時(shí),C 有一個(gè)類似的函數(shù)參數(shù)概念。在 C 中,所有參數(shù)都是按值傳遞的。如果要允許函數(shù)更改調(diào)用方中的變量,則需要傳遞指向該變量的指針。

    您可能想知道是否可以通過使用指針將不可變對(duì)象簡單地傳遞給 C 來繞過不可變限制。除非你走到丑陋和不可移植的極端,否則Python 不會(huì)給你一個(gè)指向 object 的指針,所以這行不通。如果您想用 C 修改 Python 對(duì)象,那么您需要采取額外的步驟來實(shí)現(xiàn)這一點(diǎn)。這些步驟將取決于您使用的工具,如下所示。

    因此,您可以將不變性添加到您創(chuàng)建 Python 綁定時(shí)要考慮的項(xiàng)目清單中。在創(chuàng)建此清單的宏偉之旅中,您的最后一站是如何處理 Python 和 C 處理內(nèi)存管理的不同方式。

    管理內(nèi)存

    C 和 Python管理內(nèi)存的方式不同。在 C 中,開發(fā)人員必須管理所有內(nèi)存分配并確保它們被釋放一次且僅一次。Python 使用垃圾收集器為您處理這個(gè)問題。

    雖然這些方法中的每一種都有其優(yōu)點(diǎn),但它確實(shí)為創(chuàng)建 Python 綁定添加了額外的麻煩。您需要知道每個(gè)對(duì)象的內(nèi)存分配在哪里,并確保它只在語言障礙的同一側(cè)被釋放。

    例如,當(dāng)您設(shè)置x=3. 用于此的內(nèi)存在 Python 端分配,需要進(jìn)行垃圾收集。幸運(yùn)的是,使用 Python 對(duì)象,很難做任何其他事情。看看 C 中的逆向,直接分配一塊內(nèi)存:

    int* iPtr=(int*)malloc(sizeof(int));

    執(zhí)行此操作時(shí),您需要確保在 C 中釋放此指針。這可能意味著手動(dòng)將代碼添加到 Python 綁定中以執(zhí)行此操作。

    這完善了您的一般主題清單。讓我們開始設(shè)置您的系統(tǒng),以便您可以編寫一些代碼!

    設(shè)置您的環(huán)境

    在本教程中,您將使用來自 Real Python GitHub 存儲(chǔ)庫的預(yù)先存在的 C 和 C++ 庫來展示每個(gè)工具的測試。目的是您將能夠?qū)⑦@些想法用于任何 C 庫。要遵循此處的所有示例,您需要具備以下條件:

    • 安裝的C++ 庫和命令行調(diào)用路徑的知識(shí)
    • Python開發(fā)工具
      • 對(duì)于 Linux,這是python3-dev或python3-devel包,具體取決于您的發(fā)行版。
      • 對(duì)于 Windows,有多個(gè)選項(xiàng)。
    • Python 3.6或更高版本
    • 一個(gè)虛擬環(huán)境(建議,但不要求)
    • invoke工具

    最后一個(gè)對(duì)你來說可能是新的,所以讓我們仔細(xì)看看它。

    使用invoke工具

    invoke是您將在本教程中用于構(gòu)建和測試 Python 綁定的工具。它具有類似的目的,make但使用 Python 而不是 Makefiles。您需要invoke使用pip以下命令在虛擬環(huán)境中安裝:

    $ python3 -m pip install invoke

    要運(yùn)行它,請(qǐng)鍵入invoke后跟要執(zhí)行的任務(wù):

    $ invoke build-cmult===================================================Building C Library
    * Complete

    要查看哪些任務(wù)可用,請(qǐng)使用以下--list選項(xiàng):

    $ invoke --list
    Available tasks:
    
      all              Build and run all tests
      build-cffi       Build the CFFI Python bindings
      build-cmult      Build the shared library for the sample C code
      build-cppmult    Build the shared library for the sample C++ code
      build-cython     Build the cython extension module
      build-pybind11   Build the pybind11 wrapper library
      clean            Remove any built objects
      test-cffi        Run the script to test CFFI
      test-ctypes      Run the script to test ctypes
      test-cython      Run the script to test Cython
      test-pybind11    Run the script to test PyBind11

    請(qǐng)注意,當(dāng)您查看定義任務(wù)的tasks.py文件時(shí)invoke,您會(huì)看到列出的第二個(gè)任務(wù)的名稱是build_cffi. 但是,來自的輸出將其--list顯示為build-cffi. 減號(hào) ( -) 不能用作 Python 名稱的一部分,因此該文件使用下劃線 ( _) 代替。

    對(duì)于您將檢查的每個(gè)工具,都會(huì)定義一個(gè)build-和一個(gè)test-任務(wù)。例如,要運(yùn)行 的代碼CFFI,您可以鍵入invoke build-cffi test-cffi。一個(gè)例外是ctypes,因?yàn)?沒有構(gòu)建階段ctypes。此外,為了方便,還添加了兩個(gè)特殊任務(wù):

    • invoke all 運(yùn)行所有工具的構(gòu)建和測試任務(wù)。
    • invoke clean 刪除任何生成的文件。

    既然您已經(jīng)對(duì)如何運(yùn)行代碼有所了解,那么在查看工具概述之前,讓我們先看一下您將要包裝的 C 代碼。

    C 或 C++ 源代碼

    在下面的每個(gè)示例部分中,您將為C 或 C++ 中的相同函數(shù)創(chuàng)建 Python 綁定。這些部分旨在讓您體驗(yàn)每種方法的外觀,而不是有關(guān)該工具的深入教程,因此您將封裝的函數(shù)很小。您將為其創(chuàng)建 Python 綁定的函數(shù)將 anint和 afloat作為輸入?yún)?shù)并返回一個(gè)float是兩個(gè)數(shù)字的乘積:

    // cmult.c
    float cmult(int int_param, float float_param) {
        float return_value=int_param * float_param;
        printf("    In cmult : int: %d float %.1f returning  %.1f\n", int_param,
                float_param, return_value);
        return return_value;
    }

    C 和 C++ 函數(shù)幾乎相同,它們之間的名稱和字符串略有不同。您可以通過單擊以下鏈接獲取所有代碼的副本:

    現(xiàn)在您已經(jīng)克隆了 repo 并安裝了工具,您可以構(gòu)建和測試這些工具。因此,讓我們深入了解下面的每個(gè)部分!

    ctypes

    您將從 開始ctypes,它是標(biāo)準(zhǔn)庫中用于創(chuàng)建 Python 綁定的工具。它提供了一個(gè)低級(jí)工具集,用于在 Python 和 C 之間加載共享庫和編組數(shù)據(jù)。

    它是如何安裝的

    的一大優(yōu)點(diǎn)ctypes是它是 Python 標(biāo)準(zhǔn)庫的一部分。它是在 Python 2.5 版中添加的,因此您很可能已經(jīng)擁有它。您可以import像使用sys或time模塊一樣。

    調(diào)用函數(shù)

    加載 C 庫和調(diào)用函數(shù)的所有代碼都將在 Python 程序中。這很棒,因?yàn)槟倪^程中沒有額外的步驟。您只需運(yùn)行您的程序,一切都會(huì)得到處理。要在 中創(chuàng)建 Python 綁定ctypes,您需要執(zhí)行以下步驟:

    1. 加載您的庫。
    2. 包裝一些輸入?yún)?shù)。
    3. 告訴 ctypes你函數(shù)的返回類型。

    您將依次查看其中的每一個(gè)。

    庫加載

    ctypes為您提供了多種加載共享庫的方法,其中一些是特定于平臺(tái)的。對(duì)于您的示例,您將ctypes.CDLL通過傳入所需共享庫的完整路徑來直接創(chuàng)建對(duì)象:

    # ctypes_test.py
    import ctypes
    import pathlib
    
    if __name__=="__main__":
        # Load the shared library into ctypes
        libname=pathlib.Path().absolute() / "libcmult.so"
        c_lib=ctypes.CDLL(libname)

    這適用于共享庫與 Python 腳本位于同一目錄中的情況,但在嘗試加載來自 Python 綁定以外的包的庫時(shí)要小心。在ctypes特定于平臺(tái)和特定情況的文檔中,有許多關(guān)于加載庫和查找路徑的詳細(xì)信息。

    注意:在庫加載過程中可能會(huì)出現(xiàn)許多特定于平臺(tái)的問題。最好在示例工作后進(jìn)行增量更改。

    現(xiàn)在您已將庫加載到 Python 中,您可以嘗試調(diào)用它!

    調(diào)用你的函數(shù)

    請(qǐng)記住,您的 C 函數(shù)的函數(shù)原型如下:

    // cmult.h
    float cmult(int int_param, float float_param);

    您需要傳入一個(gè)整數(shù)和一個(gè)浮點(diǎn)數(shù),并且可以期望得到一個(gè)浮點(diǎn)數(shù)返回。整數(shù)和浮點(diǎn)數(shù)在 Python 和 C 中都有本機(jī)支持,因此您希望這種情況適用于合理的值。

    將庫加載到 Python 綁定中后,該函數(shù)將成為 的屬性c_lib,即CDLL您之前創(chuàng)建的對(duì)象。您可以嘗試這樣稱呼它:

    x, y=6, 2.3
    answer=c_lib.cmult(x, y)

    哎呀!這不起作用。此行在示例 repo 中被注釋掉,因?yàn)樗×?。如果您嘗試使用該調(diào)用運(yùn)行,那么 Python 會(huì)報(bào)錯(cuò):

    $ invoke test-ctypes
    Traceback (most recent call last):
      File "ctypes_test.py", line 16, in <module>
        answer=c_lib.cmult(x, y)
    ctypes.ArgumentError: argument 2: <class 'TypeError'>: Don't know how to convert parameter 2

    看起來您需要說明ctypes任何不是整數(shù)的參數(shù)。ctypes除非您明確告訴它,否則您對(duì)該函數(shù)一無所知。任何未以其他方式標(biāo)記的參數(shù)都假定為整數(shù)。ctypes不知道如何將2.3存儲(chǔ)的值轉(zhuǎn)換為y整數(shù),所以它失敗了。

    要解決此問題,您需要c_float從號(hào)碼中創(chuàng)建一個(gè)。您可以在調(diào)用函數(shù)的行中執(zhí)行此操作:

    # ctypes_test.py
    answer=c_lib.cmult(x, ctypes.c_float(y))
    print(f"    In Python: int: {x} float {y:.1f} return val {answer:.1f}")

    現(xiàn)在,當(dāng)您運(yùn)行此代碼時(shí),它會(huì)返回您傳入的兩個(gè)數(shù)字的乘積:

    $ invoke test-ctypes
        In cmult : int: 6 float 2.3 returning  13.8
        In Python: int: 6 float 2.3 return val 48.0

    等一下……6乘以2.3不是48.0!

    事實(shí)證明,就像輸入?yún)?shù)一樣,ctypes 假設(shè)您的函數(shù)返回一個(gè)int. 實(shí)際上,您的函數(shù)返回 a float,它被錯(cuò)誤地編組。就像輸入?yún)?shù)一樣,您需要告訴ctypes使用不同的類型。這里的語法略有不同:

    # ctypes_test.py
    c_lib.cmult.restype=ctypes.c_float
    answer=c_lib.cmult(x, ctypes.c_float(y))
    print(f"    In Python: int: {x} float {y:.1f} return val {answer:.1f}")

    這應(yīng)該夠了吧。讓我們運(yùn)行整個(gè)test-ctypes目標(biāo),看看你有什么。請(qǐng)記住,輸出的第一部分是restype將函數(shù)固定為浮點(diǎn)數(shù)之前:

    $ invoke test-ctypes===================================================Building C Library
    * Complete===================================================Testing ctypes Module
        In cmult : int: 6 float 2.3 returning  13.8
        In Python: int: 6 float 2.3 return val 48.0
    
        In cmult : int: 6 float 2.3 returning  13.8
        In Python: int: 6 float 2.3 return val 13.8

    這樣更好!雖然第一個(gè)未更正的版本返回錯(cuò)誤的值,但您的固定版本與 C 函數(shù)一致。C 和 Python 都得到相同的結(jié)果!現(xiàn)在它可以工作了,看看為什么您可能想或不想使用ctypes.

    長處和短處

    ctypes與您將在此處檢查的其他工具相比,最大的優(yōu)勢(shì)在于它內(nèi)置于標(biāo)準(zhǔn)庫中。它還不需要額外的步驟,因?yàn)樗泄ぷ鞫际亲鳛?Python 程序的一部分完成的。

    此外,所使用的概念是低級(jí)的,這使得像您剛剛做的那樣的練習(xí)易于管理。然而,由于缺乏自動(dòng)化,更復(fù)雜的任務(wù)變得繁瑣。在下一部分中,您將看到一個(gè)工具,該工具為流程添加了一些自動(dòng)化。

    CFFI

    CFFI是Python的C 外來函數(shù)接口。生成 Python 綁定需要更自動(dòng)化的方法。CFFI有多種方式可以構(gòu)建和使用 Python 綁定。有兩種不同的選項(xiàng)可供選擇,為您提供四種可能的模式:

    • ABI vs API: API 模式使用 C 編譯器生成完整的 Python 模塊,而 ABI 模式加載共享庫并直接與其交互。在不運(yùn)行編譯器的情況下,獲取正確的結(jié)構(gòu)和參數(shù)很容易出錯(cuò)。該文檔強(qiáng)烈建議使用 API 模式。
    • 內(nèi)聯(lián) vs 外聯(lián):這兩種模式的區(qū)別在于速度和便利性之間的權(quán)衡:
      • 每次運(yùn)行腳本時(shí),內(nèi)聯(lián)模式都會(huì)編譯 Python 綁定。這很方便,因?yàn)槟恍枰~外的構(gòu)建步驟。但是,它確實(shí)會(huì)減慢您的程序速度。
      • Out-of-line 模式需要一個(gè)額外的步驟來一次性生成 Python 綁定,然后在每次程序運(yùn)行時(shí)使用它們。這要快得多,但這對(duì)您的應(yīng)用程序可能無關(guān)緊要。

    對(duì)于此示例,您將使用 API 外聯(lián)模式,它生成最快的代碼,并且通??雌饋眍愃朴谀鷮⒃诒窘坛毯竺鎰?chuàng)建的其他 Python 綁定。

    它是如何安裝的

    由于CFFI不是標(biāo)準(zhǔn)庫的一部分,您需要在您的機(jī)器上安裝它。建議您為此創(chuàng)建一個(gè)虛擬環(huán)境。幸運(yùn)的是,CFFI安裝有pip:

    $ python3 -m pip install cffi

    這會(huì)將軟件包安裝到您的虛擬環(huán)境中。如果您已經(jīng)從 安裝requirements.txt,那么應(yīng)該注意這一點(diǎn)。您可以requirements.txt通過訪問以下鏈接中的 repo 來查看:

    獲取示例代碼: 單擊此處獲取您將用于在本教程中了解 Python 綁定的示例代碼。

    現(xiàn)在你已經(jīng)CFFI安裝好了,是時(shí)候試一試了!

    調(diào)用函數(shù)

    與 不同的是ctypes,CFFI您正在創(chuàng)建一個(gè)完整的 Python 模塊。您將能夠import像標(biāo)準(zhǔn)庫中的任何其他模塊一樣使用該模塊。您需要做一些額外的工作來構(gòu)建 Python 模塊。要使用CFFIPython 綁定,您需要執(zhí)行以下步驟:

    • 編寫一些描述綁定的 Python 代碼。
    • 運(yùn)行該代碼以生成可加載模塊。
    • 修改調(diào)用代碼以導(dǎo)入和使用新創(chuàng)建的模塊。

    這可能看起來需要做很多工作,但您將完成這些步驟中的每一個(gè),并了解它是如何工作的。

    編寫綁定

    CFFI提供讀取C 頭文件的方法,以在生成 Python 綁定時(shí)完成大部分工作。在 的文檔中CFFI,執(zhí)行此操作的代碼放置在單獨(dú)的 Python 文件中。對(duì)于此示例,您將直接將該代碼放入構(gòu)建工具中invoke,該工具使用 Python 文件作為輸入。要使用CFFI,您首先要?jiǎng)?chuàng)建一個(gè)cffi.FFI對(duì)象,該對(duì)象提供了您需要的三種方法:

    # tasks.py
    import cffi
    ...
    """ Build the CFFI Python bindings """
    print_banner("Building CFFI Module")
    ffi=cffi.FFI()

    擁有 FFI 后,您將使用.cdef()來自動(dòng)處理頭文件的內(nèi)容。這會(huì)為您創(chuàng)建包裝函數(shù)以從 Python 封送數(shù)據(jù):

    # tasks.py
    this_dir=pathlib.Path().absolute()
    h_file_name=this_dir / "cmult.h"
    with open(h_file_name) as h_file:
        ffi.cdef(h_file.read())

    讀取和處理頭文件是第一步。之后,您需要使用.set_source()來描述CFFI將生成的源文件:

    # tasks.py
    ffi.set_source(
        "cffi_example",
        # Since you're calling a fully-built library directly, no custom source
        # is necessary. You need to include the .h files, though, because behind
        # the scenes cffi generates a .c file that contains a Python-friendly
        # wrapper around each of the functions.
        '#include "cmult.h"',
        # The important thing is to include the pre-built lib in the list of
        # libraries you're linking against:
        libraries=["cmult"],
        library_dirs=[this_dir.as_posix()],
        extra_link_args=["-Wl,-rpath,."],
    )

    以下是您傳入的參數(shù)的細(xì)分:

    • "cffi_example"是將在您的文件系統(tǒng)上創(chuàng)建的源文件的基本名稱。CFFI將生成一個(gè).c文件,將其編譯為一個(gè).o文件,并將其鏈接到一個(gè).<system-description>.so或.<system-description>.dll文件。
    • '#include "cmult.h"'是自定義 C 源代碼,它將在編譯之前包含在生成的源代碼中。在這里,您只需包含.h要為其生成綁定的文件,但這可用于一些有趣的自定義。
    • libraries=["cmult"]告訴鏈接器您預(yù)先存在的 C 庫的名稱。這是一個(gè)列表,因此您可以根據(jù)需要指定多個(gè)庫。
    • library_dirs=[this_dir.as_posix(),] 是一個(gè)目錄列表,告訴鏈接器在何處查找上述庫列表。
    • extra_link_args=['-Wl,-rpath,.']是一組生成共享對(duì)象的選項(xiàng),它將在當(dāng)前路徑 ( .) 中查找它需要加載的其他庫。

    構(gòu)建 Python 綁定

    調(diào)用.set_source()不會(huì)構(gòu)建 Python 綁定。它只設(shè)置元數(shù)據(jù)來描述將生成的內(nèi)容。要構(gòu)建 Python 綁定,您需要調(diào)用.compile():

    # tasks.py
    ffi.compile()

    這通過生成.c文件、.o文件和共享庫來完成。在invoke你剛走通過任務(wù)可以在上運(yùn)行命令行構(gòu)建Python綁定:

    $ invoke build-cffi===================================================Building C Library
    * Complete===================================================Building CFFI Module
    * Complete

    你有你的CFFIPython 綁定,所以是時(shí)候運(yùn)行這段代碼了!

    調(diào)用你的函數(shù)

    在您為配置和運(yùn)行CFFI編譯器所做的所有工作之后,使用生成的 Python 綁定看起來就像使用任何其他 Python 模塊一樣:

    # cffi_test.py
    import cffi_example
    
    if __name__=="__main__":
        # Sample data for your call
        x, y=6, 2.3
    
        answer=cffi_example.lib.cmult(x, y)
        print(f"    In Python: int: {x} float {y:.1f} return val {answer:.1f}")

    你導(dǎo)入新模塊,然后就可以cmult()直接調(diào)用了。要對(duì)其進(jìn)行測試,請(qǐng)使用以下test-cffi任務(wù):

    $ invoke test-cffi===================================================Testing CFFI Module
        In cmult : int: 6 float 2.3 returning  13.8
        In Python: int: 6 float 2.3 return val 13.8

    這將運(yùn)行您的cffi_test.py程序,該程序會(huì)測試您使用CFFI. 關(guān)于編寫和使用CFFIPython 綁定的部分到此結(jié)束。

    長處和短處

    ctypes與CFFI您剛剛看到的示例相比,這似乎需要更少的工作。雖然這對(duì)于這個(gè)用例來說是正確的,CFFI但與ctypes由于大部分功能包裝的自動(dòng)化相比,它可以更好地?cái)U(kuò)展到更大的項(xiàng)目。

    CFFI也產(chǎn)生了完全不同的用戶體驗(yàn)。ctypes允許您將預(yù)先存在的 C 庫直接加載到您的 Python 程序中。CFFI,另一方面,創(chuàng)建一個(gè)可以像其他 Python 模塊一樣加載的新 Python 模塊。

    更重要的是,使用上面使用的外部 API方法,創(chuàng)建 Python 綁定的時(shí)間損失在您構(gòu)建它時(shí)完成一次,并且不會(huì)在每次運(yùn)行代碼時(shí)發(fā)生。對(duì)于小程序來說,這可能不是什么大問題,但也可以通過CFFI這種方式更好地?cái)U(kuò)展到更大的項(xiàng)目。

    就像ctypes, usingCFFI只允許您直接與 C 庫交互。C++ 庫需要大量的工作才能使用。在下一節(jié)中,您將看到一個(gè)專注于 C++ 的 Python 綁定工具。

    PyBind11

    PyBind11使用完全不同的方法來創(chuàng)建 Python 綁定。除了將重點(diǎn)從 C 轉(zhuǎn)移到 C++ 之外,它還使用 C++ 來指定和構(gòu)建模塊,使其能夠利用 C++ 中的元編程工具。像 一樣CFFI,生成的 Python 綁定PyBind11是一個(gè)完整的 Python 模塊,可以直接導(dǎo)入和使用。

    PyBind11以Boost::Python庫為藍(lán)本并具有類似的界面。但是,它將其使用限制為 C++11 和更新版本,與支持所有內(nèi)容的 Boost 相比,這使其能夠簡化和加快處理速度。

    它是如何安裝的

    文檔的“第一步”部分將PyBind11引導(dǎo)您了解如何下載和構(gòu)建PyBind11. 雖然這似乎不是嚴(yán)格要求,但完成這些步驟將確保您設(shè)置了正確的 C++ 和 Python 工具。

    注:大部分示例PyBind11使用cmake,是構(gòu)建 C 和 C++ 項(xiàng)目的好工具。但是,對(duì)于此演示,您將繼續(xù)使用該invoke工具,該工具遵循文檔的手動(dòng)構(gòu)建部分中的說明。

    您需要將此工具安裝到您的虛擬環(huán)境中:

    $ python3 -m pip install pybind11

    PyBind11是一個(gè)全頭庫,類似于 Boost 的大部分內(nèi)容。這允許pip將庫的實(shí)際 C++ 源代碼直接安裝到您的虛擬環(huán)境中。

    調(diào)用函數(shù)

    在您深入研究之前,請(qǐng)注意您使用的是不同的 C++ 源文件, cppmult.cpp,而不是您用于前面示例的 C 文件。兩種語言的功能基本相同。

    編寫綁定

    與 類似CFFI,您需要?jiǎng)?chuàng)建一些代碼來告訴該工具如何構(gòu)建您的 Python 綁定。與 不同CFFI,此代碼將使用 C++ 而不是 Python。幸運(yùn)的是,只需要很少的代碼:

    // pybind11_wrapper.cpp
    #include <pybind11/pybind11.h>
    #include <cppmult.hpp>
    
    PYBIND11_MODULE(pybind11_example, m) {
        m.doc()="pybind11 example plugin"; // Optional module docstring
        m.def("cpp_function", &cppmult, "A function that multiplies two numbers");
    }

    讓我們一次一個(gè)地看,因?yàn)镻yBind11將大量信息打包成幾行。

    前兩行包括pybind11.hC++ 庫的文件和頭文件cppmult.hpp. 之后,你就有了PYBIND11_MODULE宏。這將擴(kuò)展為PyBind11源代碼中詳細(xì)描述的 C++ 代碼塊:

    此宏創(chuàng)建入口點(diǎn),當(dāng) Python 解釋器導(dǎo)入擴(kuò)展模塊時(shí)將調(diào)用該入口點(diǎn)。模塊名稱作為第一個(gè)參數(shù)給出,不應(yīng)用引號(hào)引起來。第二個(gè)宏參數(shù)定義了一個(gè)py::module可用于初始化模塊的類型變量。(來源)

    這對(duì)您來說意味著,在本例中,您正在創(chuàng)建一個(gè)名為的模塊pybind11_example,其余代碼將m用作py::module對(duì)象的名稱。在下一行,在您定義的 C++ 函數(shù)中,您為模塊創(chuàng)建一個(gè)文檔字符串。雖然這是可選的,但讓您的模塊更加Pythonic是一個(gè)不錯(cuò)的選擇。

    最后,你有m.def()電話。這將定義一個(gè)由您的新 Python 綁定導(dǎo)出的函數(shù),這意味著它將在 Python 中可見。在此示例中,您將傳遞三個(gè)參數(shù):

    • cpp_function是您將在 Python 中使用的函數(shù)的導(dǎo)出名稱。如本例所示,它不需要匹配 C++ 函數(shù)的名稱。
    • &cppmult 獲取要導(dǎo)出的函數(shù)的地址。
    • "A function..." 是函數(shù)的可選文檔字符串。

    現(xiàn)在您已經(jīng)有了 Python 綁定的代碼,接下來看看如何將其構(gòu)建到 Python 模塊中。

    構(gòu)建 Python 綁定

    用于構(gòu)建 Python 綁定的工具PyBind11是 C++ 編譯器本身。您可能需要修改編譯器和操作系統(tǒng)的默認(rèn)值。

    首先,您必須構(gòu)建要為其創(chuàng)建綁定的 C++ 庫。對(duì)于這么小的示例,您可以將cppmult庫直接構(gòu)建到 Python 綁定庫中。但是,對(duì)于大多數(shù)實(shí)際示例,您將有一個(gè)要包裝的預(yù)先存在的庫,因此您將cppmult單獨(dú)構(gòu)建該庫。構(gòu)建是對(duì)編譯器的標(biāo)準(zhǔn)調(diào)用以構(gòu)建共享庫:

    # tasks.py
    invoke.run(
        "g++ -O3 -Wall -Werror -shared -std=c++11 -fPIC cppmult.cpp "
        "-o libcppmult.so "
    )

    運(yùn)行這個(gè)invoke build-cppmult產(chǎn)生libcppmult.so:

    $ invoke build-cppmult===================================================Building C++ Library
    * Complete

    另一方面,Python 綁定的構(gòu)建需要一些特殊的細(xì)節(jié):

     1# tasks.py
     2invoke.run(
     3    "g++ -O3 -Wall -Werror -shared -std=c++11 -fPIC "
     4    "`python3 -m pybind11 --includes` "
     5    "-I /usr/include/python3.7 -I .  "
     6    "{0} "
     7    "-o {1}`python3.7-config --extension-suffix` "
     8    "-L. -lcppmult -Wl,-rpath,.".format(cpp_name, extension_name)
     9)

    讓我們逐行瀏覽一下。第 3 行包含相當(dāng)標(biāo)準(zhǔn)的 C++ 編譯器標(biāo)志,指示幾個(gè)細(xì)節(jié),包括您希望捕獲所有警告并將其視為錯(cuò)誤、您需要共享庫以及您使用的是 C++11。

    第 4 行是魔法的第一步。它調(diào)用pybind11模塊使其include為PyBind11. 您可以直接在控制臺(tái)上運(yùn)行此命令以查看它的作用:

    $ python3 -m pybind11 --includes
    -I/home/jima/.virtualenvs/realpython/include/python3.7m
    -I/home/jima/.virtualenvs/realpython/include/site/python3.7

    您的輸出應(yīng)該相似但顯示不同的路徑。

    在編譯調(diào)用的第 5 行,您可以看到您還添加了 Python dev 的路徑includes。雖然建議您不要鏈接 Python 庫本身,但源代碼需要一些代碼Python.h才能發(fā)揮其魔力。幸運(yùn)的是,它使用的代碼在 Python 版本中相當(dāng)穩(wěn)定。

    第 5 行還用于-I .將當(dāng)前目錄添加到include路徑列表中。這允許#include <cppmult.hpp>解析包裝器代碼中的行。

    第 6 行指定源文件的名稱,即pybind11_wrapper.cpp. 然后,在第 7 行,您會(huì)看到更多的構(gòu)建魔法正在發(fā)生。此行指定輸出文件的名稱。Python 在模塊命名上有一些特別的想法,包括 Python 版本、機(jī)器架構(gòu)和其他細(xì)節(jié)。Python 還提供了一個(gè)工具來幫助解決這個(gè)問題python3.7-config:

    $ python3.7-config --extension-suffix
    .cpython-37m-x86_64-linux-gnu.so

    如果您使用的是不同版本的 Python,則可能需要修改該命令。如果您使用不同版本的 Python 或在不同的操作系統(tǒng)上,您的結(jié)果可能會(huì)發(fā)生變化。

    構(gòu)建命令的最后一行,第 8 行,將鏈接器指向libcppmult您之前構(gòu)建的庫。該rpath部分告訴鏈接器向共享庫添加信息以幫助操作系統(tǒng)libcppmult在運(yùn)行時(shí)查找。最后,您會(huì)注意到此字符串的格式為cpp_name和extension_name。Cython在下一節(jié)中構(gòu)建 Python 綁定模塊時(shí),您將再次使用此函數(shù)。

    運(yùn)行此命令以構(gòu)建綁定:

    $ invoke build-pybind11===================================================Building C++ Library
    * Complete===================================================Building PyBind11 Module
    * Complete

    就是這樣!您已經(jīng)使用PyBind11. 是時(shí)候測試一下了!

    調(diào)用你的函數(shù)

    與CFFI上面的示例類似,一旦您完成了創(chuàng)建 Python 綁定的繁重工作,調(diào)用您的函數(shù)看起來就像普通的 Python 代碼:

    # pybind11_test.py
    import pybind11_example
    
    if __name__=="__main__":
        # Sample data for your call
        x, y=6, 2.3
    
        answer=pybind11_example.cpp_function(x, y)
        print(f"    In Python: int: {x} float {y:.1f} return val {answer:.1f}")

    由于您pybind11_example在PYBIND11_MODULE宏中用作模塊的名稱,因此這就是您導(dǎo)入的名稱。在m.def()您告訴PyBind11將cppmult函數(shù)導(dǎo)出為 的調(diào)用中cpp_function,這就是您用來從 Python 調(diào)用它的方法。

    你也可以測試它invoke:

    $ invoke test-pybind11===================================================Testing PyBind11 Module
        In cppmul: int: 6 float 2.3 returning  13.8
        In Python: int: 6 float 2.3 return val 13.8

    這就是PyBind11看起來的樣子。接下來,您將了解何時(shí)以及為何PyBind11是適合該工作的工具。

    長處和短處

    PyBind11專注于 C++ 而不是 C,這使得它不同于ctypes和CFFI。它有幾個(gè)特性使其對(duì) C++ 庫非常有吸引力:

    • 它支持。
    • 它處理多態(tài)子類化
    • 它允許您從 Python 和許多其他工具向?qū)ο筇砑?/span>動(dòng)態(tài)屬性,而使用您檢查過的基于 C 的工具很難做到這一點(diǎn)。

    話雖如此,您需要進(jìn)行大量設(shè)置和配置才能PyBind11啟動(dòng)和運(yùn)行。正確安裝和構(gòu)建可能有點(diǎn)挑剔,但一旦完成,它似乎相當(dāng)可靠。此外,PyBind11要求您至少使用 C++11 或更高版本。對(duì)于大多數(shù)項(xiàng)目來說,這不太可能是一個(gè)很大的限制,但它可能是您的一個(gè)考慮因素。

    最后,創(chuàng)建 Python 綁定需要編寫的額外代碼是用 C++ 編寫的,而不是用 Python 編寫的。這可能是也可能不是你的問題,但它是比你在這里看到的其他工具不同。在下一節(jié)中,您將繼續(xù)討論Cython,它采用完全不同的方法來解決這個(gè)問題。

    Cython

    該方法Cython需要?jiǎng)?chuàng)建Python綁定使用類Python語言來定義綁定,然后生成的C或C ++代碼可被編譯成模塊。有幾種方法可以使用Cython. 最常見的一種是使用setupfrom distutils。對(duì)于此示例,您將堅(jiān)持使用該invoke工具,它允許您使用運(yùn)行的確切命令。

    它是如何安裝的

    Cython是一個(gè) Python 模塊,可以從PyPI安裝到您的虛擬環(huán)境中:

    $ python3 -m pip install cython

    同樣,如果您已將該requirements.txt文件安裝到虛擬環(huán)境中,則該文件已經(jīng)存在。您可以requirements.txt通過單擊以下鏈接獲取副本:

    獲取示例代碼: 單擊此處獲取您將用于在本教程中了解 Python 綁定的示例代碼。

    這應(yīng)該讓你準(zhǔn)備好與之合作Cython!

    調(diào)用函數(shù)

    要使用 構(gòu)建 Python 綁定Cython,您將遵循與用于CFFI和 的步驟類似的步驟PyBind11。您將編寫綁定、構(gòu)建它們,然后運(yùn)行 ?Python 代碼來調(diào)用它們。Cython可以同時(shí)支持 C 和 C++。對(duì)于本示例,您將使用cppmult您在PyBind11上面的示例中使用的庫。

    編寫綁定

    聲明模塊的最常見形式Cython是使用.pyx文件:

     1# cython_example.pyx
     2""" Example cython interface definition """
     3
     4cdef extern from "cppmult.hpp":
     5    float cppmult(int int_param, float float_param)
     6
     7def pymult( int_param, float_param ):
     8    return cppmult( int_param, float_param )

    這里有兩個(gè)部分:

    1. 線3和4告訴Cython您使用的是cppmult()從cppmult.hpp。
    2. 第 6 行和第 7 行創(chuàng)建了一個(gè)包裝函數(shù)pymult(),以調(diào)用cppmult()。

    這里使用的語言是 C、C++ 和 Python 的特殊組合。不過,對(duì)于 Python 開發(fā)人員來說,它看起來相當(dāng)熟悉,因?yàn)槠淠繕?biāo)是使過程更容易。

    第一部分 withcdef extern...告訴Cython下面的函數(shù)聲明也可以在cppmult.hpp文件中找到。這對(duì)于確保根據(jù)與 C++ 代碼相同的聲明構(gòu)建 Python 綁定非常有用。第二部分看起來像一個(gè)普通的 Python 函數(shù)——因?yàn)樗?!本?jié)創(chuàng)建一個(gè)可以訪問 C++ 函數(shù)的 Python 函數(shù)cppmult。

    現(xiàn)在您已經(jīng)定義了 Python 綁定,是時(shí)候構(gòu)建它們了!

    構(gòu)建 Python 綁定

    構(gòu)建過程Cython與您使用的構(gòu)建過程相似PyBind11。您首先Cython在.pyx文件上運(yùn)行以生成.cpp文件。完成此操作后,您可以使用用于以下內(nèi)容的相同函數(shù)對(duì)其進(jìn)行編譯PyBind11:

     1# tasks.py
     2def compile_python_module(cpp_name, extension_name):
     3    invoke.run(
     4        "g++ -O3 -Wall -Werror -shared -std=c++11 -fPIC "
     5        "`python3 -m pybind11 --includes` "
     6        "-I /usr/include/python3.7 -I .  "
     7        "{0} "
     8        "-o {1}`python3.7-config --extension-suffix` "
     9        "-L. -lcppmult -Wl,-rpath,.".format(cpp_name, extension_name)
    10    )
    11
    12def build_cython(c):
    13    """ Build the cython extension module """
    14    print_banner("Building Cython Module")
    15    # Run cython on the pyx file to create a .cpp file
    16    invoke.run("cython --cplus -3 cython_example.pyx -o cython_wrapper.cpp")
    17
    18    # Compile and link the cython wrapper library
    19    compile_python_module("cython_wrapper.cpp", "cython_example")
    20    print("* Complete")

    您首先運(yùn)行cython您的.pyx文件。您可以在此命令上使用幾個(gè)選項(xiàng):

    • --cplus 告訴編譯器生成 C++ 文件而不是 C 文件。
    • -3切換Cython到生成 Python 3 語法而不是 Python 2。
    • -o cython_wrapper.cpp 指定要生成的文件的名稱。

    生成 C++ 文件后,您可以使用 C++ 編譯器生成 Python 綁定,就像您為PyBind11. 請(qǐng)注意,include使用該pybind11工具生成額外路徑的調(diào)用仍在該函數(shù)中。在這里不會(huì)有任何傷害,因?yàn)槟膩碓床恍枰@些。

    在 中運(yùn)行此任務(wù)invoke會(huì)產(chǎn)生以下輸出:

    $ invoke build-cython===================================================Building C++ Library
    * Complete===================================================Building Cython Module
    * Complete

    可以看到它構(gòu)建了cppmult庫,然后構(gòu)建了cython模塊來包裝它?,F(xiàn)在你有了CythonPython 綁定。(試著說的是迅速...)它的時(shí)間來測試一下吧!

    調(diào)用你的函數(shù)

    調(diào)用新 Python 綁定的 Python 代碼與用于測試其他模塊的代碼非常相似:

     1# cython_test.py
     2import cython_example
     3
     4# Sample data for your call
     5x, y=6, 2.3
     6
     7answer=cython_example.pymult(x, y)
     8print(f"    In Python: int: {x} float {y:.1f} return val {answer:.1f}")

    第 2 行導(dǎo)入新的 Python 綁定模塊,并pymult()在第 7 行調(diào)用。請(qǐng)記住,該.pyx文件提供了一個(gè) Python 包裝器cppmult()并將其重命名為pymult. 使用 invoke 運(yùn)行您的測試會(huì)產(chǎn)生以下結(jié)果:

    $ invoke test-cython===================================================Testing Cython Module
        In cppmul: int: 6 float 2.3 returning  13.8
        In Python: int: 6 float 2.3 return val 13.8

    你得到和以前一樣的結(jié)果!

    長處和短處

    Cython是一個(gè)相對(duì)復(fù)雜的工具,可以在為 C 或 C++ 創(chuàng)建 Python 綁定時(shí)為您提供更深層次的控制。雖然您沒有在此處深入介紹它,但它提供了一種 Python 式的方法來編寫手動(dòng)控制GIL 的代碼,這可以顯著加快某些類型的問題的處理速度。

    然而,這種 Python 風(fēng)格的語言并不完全是 Python,因此當(dāng)您要快速確定 C 和 Python 的哪些部分適合何處時(shí),會(huì)有一個(gè)輕微的學(xué)習(xí)曲線。

    其他解決方案

    在研究本教程時(shí),我遇到了幾種用于創(chuàng)建 Python 綁定的不同工具和選項(xiàng)。雖然我將此概述限制為一些更常見的選項(xiàng),但我偶然發(fā)現(xiàn)了其他幾種工具。下面的列表并不全面。如果上述工具之一不適合您的項(xiàng)目,這只是其他可能性的一個(gè)示例。

    PyBindGen

    PyBindGen為 C 或 C++ 生成 Python 綁定并用 Python 編寫。它旨在生成可讀的 C 或 C++ 代碼,這應(yīng)該可以簡化調(diào)試問題。目前尚不清楚這是否最近已更新,因?yàn)槲臋n將 Python 3.4 列為最新的測試版本。然而,在過去的幾年里,每年都有發(fā)布。

    Boost.Python

    Boost.Python有一個(gè)類似于PyBind11您在上面看到的界面。這不是巧合,因?yàn)镻yBind11它基于這個(gè)庫!Boost.Python是用完整的 C++ 編寫的,并且在大多數(shù)平臺(tái)上支持大多數(shù)(如果不是全部)C++ 版本。相比之下,PyBind11僅限于現(xiàn)代 C++。

    SIP

    SIP是為PyQt項(xiàng)目開發(fā)的用于生成 Python 綁定的工具集。wxPython項(xiàng)目也使用它來生成它們的綁定。它有一個(gè)代碼生成工具和一個(gè)額外的 Python 模塊,為生成的代碼提供支持功能。

    Cppyy

    cppyy是一個(gè)有趣的工具,它的設(shè)計(jì)目標(biāo)與您目前所見略有不同。用包作者的話來說:

    “cppyy 背后的最初想法(追溯到 2001 年)是允許生活在 C++ 世界中的 Python 程序員訪問那些 C++ 包,而不必直接接觸 C++(或等待 C++ 開發(fā)人員過來并提供綁定) ?!?(來源)

    Shiboken

    Shiboken是為與 Qt 項(xiàng)目關(guān)聯(lián)的 PySide 項(xiàng)目開發(fā)的用于生成 Python 綁定的工具。雖然它被設(shè)計(jì)為該項(xiàng)目的工具,但文檔表明它既不是 Qt 也不是 PySide 特定的,可用于其他項(xiàng)目。

    SWIG

    SWIG是與此處列出的任何其他工具不同的工具。它是一個(gè)通用工具,用于為許多其他語言(而不僅僅是 Python)創(chuàng)建到 C 和 C++ 程序的綁定。這種為不同語言生成綁定的能力在某些項(xiàng)目中非常有用。當(dāng)然,就復(fù)雜性而言,它會(huì)帶來成本。

    結(jié)論

    恭喜!您現(xiàn)在已經(jīng)大致了解了用于創(chuàng)建Python 綁定的幾個(gè)不同選項(xiàng)。您已經(jīng)了解了編組數(shù)據(jù)以及創(chuàng)建綁定時(shí)需要考慮的問題。您已經(jīng)了解了如何使用以下工具從 Python 調(diào)用 C 或 C++ 函數(shù):

    • ctypes
    • CFFI
    • PyBind11
    • Cython

    您現(xiàn)在知道,雖然ctypes允許您直接加載 DLL 或共享庫,但其他三個(gè)工具需要額外的步驟,但仍會(huì)創(chuàng)建完整的 Python 模塊。作為獎(jiǎng)勵(lì),您還使用了invoke從 Python 運(yùn)行命令行任務(wù)的工具。


    點(diǎn)擊關(guān)注,第一時(shí)間了解華為云新鮮技術(shù)~華為云博客_大數(shù)據(jù)博客_AI博客_云計(jì)算博客_開發(fā)者中心-華為云

    由于C和C++代碼在編譯時(shí)生成的符號(hào)不同,而我們經(jīng)常會(huì)在C代碼里調(diào)用C++的代碼,

    或者在C++代碼里調(diào)用C的代碼,下面就簡單總結(jié)一下二者相互調(diào)用時(shí)的語法。

    • 1
    • 2
    • 3

    最主要的就是在C++代碼里添加 extern “C”

    1、首先C代碼調(diào)用C++

    main.c

    #include<stdio.h>

    int sum(int a,int b);//此時(shí)sum函數(shù)生成的符號(hào)是C規(guī)則下的符號(hào),

    int main()

    {

    int ret=0;

    ret=sum(1,2);

    printf("%d\n",ret);

    return 0;

    }

    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    test.cpp

    extern "C"

    {

    int sum(int a, int b)//此時(shí)CPP文件函數(shù)在extern “C”里,CPP文件不再按CPP規(guī)則生成符號(hào),

    { //而是按照C規(guī)則生成符號(hào),這樣和main.c里的sum符號(hào)名相同,在鏈接的時(shí)候

    return a+b; //能夠找到test.cpp里sum的定義。如果不加extern“C”,生成的符號(hào)為C++規(guī)則下 //的符號(hào),main.c里的sum和這里的sum符號(hào)不同,在連接時(shí), 就找不到符號(hào)的定義,就會(huì)報(bào)鏈接時(shí)的錯(cuò)誤

    }

    }

    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    2、C++調(diào)用C

    main.cpp

    #include<iostream>

    using namespace std;

    extern "C" int sum(int a,int b);//告訴編譯器,按C規(guī)則生成sum符號(hào),這樣才會(huì)和test.c里的sum一樣

    int main() //鏈接時(shí)才能找到符號(hào)的定義

    {

    int ret=0;

    ret=sum(1,2);

    cout<<ret<<endl;

    return 0;

    }

    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    test.c

    int sum(int a, int b)//C規(guī)則生成sum符號(hào)

    {

    return a+b;

    }

    • 1
    • 2
    • 3
    • 4

    由上我們可以看出extern “C”只有在C++文件才有的語法,而C文件不存在,所以處理的時(shí)候都是在C++的代碼里進(jìn)行處理。C++不僅能生成C++規(guī)則的符號(hào),還能生成C規(guī)則的符號(hào)。

    • 1
    • 2

    … 但是,但是,一般情況下,一個(gè)公司購買別的公司的軟件產(chǎn)品,別人不會(huì)把自己辛辛苦苦寫的源代碼給你的,給你的只是個(gè)接口,一般情況下是一個(gè)庫,那這個(gè)時(shí)候我們就看不到源代碼,更用不了源代碼。此時(shí)又該如何實(shí)現(xiàn)C和C++的相互調(diào)用呢,由以上知道源碼的情況下,我們知道,C和C++相互調(diào)用時(shí),C源碼并沒有發(fā)生任何改變,改變的只是C++的代碼,所以,在沒有源碼的情況下,C++調(diào)用C和上面的情況一樣。接下來我們就詳細(xì)討論一下如何在只有一個(gè)C++庫的情況下,用C調(diào)用C++庫

    假如sum.cpp如下,實(shí)際上用戶是看不見的。

    intsum(int a,int b)

    {

    return a+b;

    }

    • 1
    • 2
    • 3
    • 4
    • 5

    g++ -shared -fpic -o libsum.so sum.cpp 之后,我們得到一個(gè)libsum.so庫。我們知道這一個(gè)庫,它的源碼我們是不知道的,那此時(shí)我們?cè)撊绾斡肅調(diào)用它來實(shí)現(xiàn)自己的加法呢。此時(shí)我們就借助計(jì)算機(jī)世界里一句名言:計(jì)算機(jī)科學(xué)領(lǐng)域的任何問題都可以通過增加一個(gè)間接的中間層來解決。我們想辦法把libsum.so里的sum封裝起來。如下:

    mysum.cpp

    int sum(int a,int b);

    extern "C"

    {

    int mysum(int a,int b)

    {

    return sum(a,b);//此時(shí)調(diào)用mysum()就相當(dāng)于調(diào)用sum();

    }

    }

    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    那我在C里再次調(diào)用sum時(shí),我就調(diào)用封裝的mysum(),

    #include<stdio.h>

    int mysum(int a,int b);

    int main()

    {

    int ret=0;

    ret=mysum(10,20);

    printf("%d\n",ret);

    }

    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    gcc -o run main.c mysum.cpp ./libsum.so生成run 大功告成。

網(wǎng)站首頁   |    關(guān)于我們   |    公司新聞   |    產(chǎn)品方案   |    用戶案例   |    售后服務(wù)   |    合作伙伴   |    人才招聘   |   

友情鏈接: 餐飲加盟

地址:北京市海淀區(qū)    電話:010-     郵箱:@126.com

備案號(hào):冀ICP備2024067069號(hào)-3 北京科技有限公司版權(quán)所有