Tworzenie rozszerzenia języka C++ dla języka Python w programie Visual Studio

W tym artykule utworzysz moduł rozszerzenia języka C++ dla języka CPython, aby obliczyć tangens hiperboliczny i wywołać go z kodu języka Python. Ta rutyna jest implementowana najpierw w języku Python, aby zademonstrować względny wzrost wydajności implementacji tej samej procedury w języku C++.

Moduły kodu napisane w języku C++ (lub C) są często używane do rozszerzania możliwości interpretera języka Python. Istnieją trzy podstawowe typy modułów rozszerzeń:

  • Moduły akceleratora: włącz przyspieszoną wydajność. Ponieważ język Python jest językiem interpretowanym, możesz napisać moduł akceleratora w języku C++, aby uzyskać większą wydajność.
  • Moduły otoki: uwidaczniaj istniejące interfejsy języka C/C++ w kodzie języka Python lub uwidacznia bardziej podobny interfejs API, który jest łatwy w użyciu z języka Python.
  • Moduły dostępu do systemu niskiego poziomu: utwórz moduły dostępu systemu, aby uzyskać dostęp do funkcji niższego CPython poziomu środowiska uruchomieniowego, systemu operacyjnego lub podstawowego sprzętu.

W tym artykule przedstawiono dwa sposoby udostępniania modułu rozszerzenia języka C++ dla języka Python:

  • Użyj standardowych CPython rozszerzeń zgodnie z opisem w dokumentacji języka Python.
  • Użyj narzędzia PyBind11, który zalecamy dla języka C++11 ze względu na jego prostotę. Aby zapewnić zgodność, upewnij się, że pracujesz z jedną z nowszych wersji języka Python.

Ukończony przykład dla tego przewodnika jest dostępny w witrynie GitHub w witrynie Python-samples-vs-cpp-extension.

Wymagania wstępne

  • Program Visual Studio 2017 lub nowszy z zainstalowanym pakietem roboczym Programowanie w języku Python. Obciążenie obejmuje natywne narzędzia programistyczne języka Python, które dodają obciążenie języka C++ i zestawy narzędzi niezbędne dla rozszerzeń natywnych.

    Screenshot of a list of Python development options, highlighting the Python native development tools option.

    Aby uzyskać więcej informacji na temat opcji instalacji, zobacz Instalowanie obsługi języka Python dla programu Visual Studio.

    Uwaga

    Po zainstalowaniu obciążenia Aplikacje do analizy i nauki o danych domyślnie instalowane są języki Python i natywne narzędzia programistyczne języka Python.

  • Jeśli zainstalujesz język Python oddzielnie, wybierz pozycję Pobierz symbole debugowania w obszarze Opcje zaawansowane w instalatorze języka Python. Ta opcja jest wymagana do korzystania z debugowania w trybie mieszanym między kodem języka Python i kodem natywnym.

Tworzenie aplikacji w języku Python

Wykonaj następujące kroki, aby utworzyć aplikację w języku Python.

  1. Utwórz nowy projekt języka Python w programie Visual Studio, wybierając pozycję Plik>nowy>projekt.

  2. W oknie dialogowym Tworzenie nowego projektu wyszukaj język Python. Wybierz szablon Aplikacja języka Python, a następnie wybierz pozycję Dalej.

  3. Wprowadź nazwę projektu i lokalizację, a następnie wybierz pozycję Utwórz.

    Program Visual Studio tworzy nowy projekt. Projekt zostanie otwarty w Eksplorator rozwiązań, a plik projektu (.py) zostanie otwarty w edytorze kodu.

  4. W pliku .py wklej następujący kod. Aby użyć niektórych funkcji edycji języka Python, spróbuj ręcznie wprowadzić kod.

    Ten kod oblicza tangens hiperboliczny bez używania biblioteki matematycznej i przyspiesza później przy użyciu rozszerzeń natywnych języka Python.

    Napiwek

    Napisz kod w czystym języku Python, zanim zapiszesz go ponownie w języku C++. Dzięki temu można łatwiej sprawdzić, czy natywny kod języka Python jest poprawny.

    from random import random
    from time import perf_counter
    
    # Change the value of COUNT according to the speed of your computer.
    # The value should enable the benchmark to complete in approximately 2 seconds.
    COUNT = 500000
    DATA = [(random() - 0.5) * 3 for _ in range(COUNT)]
    
    e = 2.7182818284590452353602874713527
    
    def sinh(x):
        return (1 - (e ** (-2 * x))) / (2 * (e ** -x))
    
    def cosh(x):
        return (1 + (e ** (-2 * x))) / (2 * (e ** -x))
    
    def tanh(x):
        tanh_x = sinh(x) / cosh(x)
        return tanh_x
    
    def test(fn, name):
        start = perf_counter()
        result = fn(DATA)
        duration = perf_counter() - start
        print('{} took {:.3f} seconds\n\n'.format(name, duration))
    
        for d in result:
            assert -1 <= d <= 1, " incorrect values"
    
    if __name__ == "__main__":
        print('Running benchmarks with COUNT = {}'.format(COUNT))
    
        test(lambda d: [tanh(x) for x in d], '[tanh(x) for x in d] (Python implementation)')
    
  5. Uruchom program, wybierając pozycję Debuguj>rozpocznij bez debugowania lub wybierz skrót klawiaturowy Ctrl+F5.

    Zostanie otwarte okno polecenia, aby wyświetlić dane wyjściowe programu.

  6. W danych wyjściowych zwróć uwagę na ilość czasu zgłoszonego dla procesu porównawczego.

    W tym przewodniku proces porównawczy powinien potrwać około 2 sekund.

  7. W razie potrzeby dostosuj wartość COUNT zmiennej w kodzie, aby umożliwić ukończenie testu porównawczego w ciągu około 2 sekund na komputerze.

  8. Uruchom ponownie program i potwierdź, że zmodyfikowana COUNT wartość generuje test porównawczy w ciągu około 2 sekund.

Napiwek

Podczas uruchamiania testów porównawczych zawsze użyj opcji Rozpocznij debugowanie>bez debugowania. Ta metoda pomaga uniknąć narzutów, które mogą być naliczane podczas uruchamiania kodu w debugerze programu Visual Studio.

Tworzenie podstawowych projektów języka C++

Wykonaj następujące kroki, aby utworzyć dwa identyczne projekty języka C++, superfastcode i superfastcode2. Później użyjesz innego podejścia w każdym projekcie, aby uwidocznić kod C++ w języku Python.

  1. W Eksplorator rozwiązań kliknij prawym przyciskiem myszy nazwę rozwiązania, a następnie wybierz pozycję Dodaj>nowy projekt.

    Rozwiązanie programu Visual Studio może zawierać zarówno projekty języka Python, jak i C++, co jest jedną z zalet korzystania z programu Visual Studio na potrzeby programowania w języku Python.

  2. W oknie dialogowym Dodawanie nowego projektu ustaw filtr Język na C++, a następnie wprowadź wartość pustąw polu Wyszukaj.

  3. Na liście wyników szablonu projektu wybierz pozycję Pusty projekt, a następnie wybierz pozycję Dalej.

  4. W oknie dialogowym Konfigurowanie nowego projektu wprowadź nazwę projektu:

    • W pierwszym projekcie wprowadź nazwę superfastcode.
    • W drugim projekcie wprowadź nazwę superfastcode2.
  5. Wybierz pozycję Utwórz.

Pamiętaj, aby powtórzyć te kroki i utworzyć dwa projekty.

Napiwek

Alternatywne podejście jest dostępne, gdy masz natywne narzędzia programistyczne języka Python zainstalowane w programie Visual Studio. Możesz rozpocząć od szablonu Moduł rozszerzenia języka Python, który wstępnie wykonuje wiele kroków opisanych w tym artykule.

Aby zapoznać się z przewodnikiem w tym artykule, rozpoczęcie od pustego projektu pomaga pokazać, jak utworzyć moduł rozszerzenia krok po kroku. Po zapoznaniu się z procesem możesz użyć alternatywnego szablonu, aby zaoszczędzić czas podczas pisania własnych rozszerzeń.

Dodawanie pliku C++ do projektu

Następnie dodaj plik C++ do każdego projektu.

  1. W Eksplorator rozwiązań rozwiń projekt, kliknij prawym przyciskiem myszy węzeł Pliki źródłowe i wybierz polecenie Dodaj>nowy element.

  2. Na liście szablonów plików wybierz pozycję Plik C++ (.cpp).

  3. Wprowadź nazwę pliku jako module.cpp, a następnie wybierz pozycję Dodaj.

    Ważne

    Upewnij się, że nazwa pliku zawiera rozszerzenie .cpp . Program Visual Studio wyszukuje plik z rozszerzeniem .cpp w celu włączenia wyświetlania stron właściwości projektu C++.

  4. Na pasku narzędzi rozwiń menu rozwijane Konfiguracja i wybierz docelowy typ konfiguracji:

    Screenshot that shows how to set the target configuration type for the C++ project in Visual Studio.

    • W przypadku 64-bitowego środowiska uruchomieniowego języka Python aktywuj konfigurację x64 .
    • W przypadku 32-bitowego środowiska uruchomieniowego języka Python aktywuj konfigurację win32 .

Pamiętaj, aby powtórzyć te kroki dla obu projektów.

Konfigurowanie właściwości projektu

Przed dodaniem kodu do nowych plików C++ skonfiguruj właściwości każdego projektu modułu C++ i przetestuj konfiguracje, aby upewnić się, że wszystko działa.

Należy ustawić właściwości projektu zarówno dla konfiguracji kompilacji debugowania , jak i wydania poszczególnych modułów.

  1. W Eksplorator rozwiązań kliknij prawym przyciskiem myszy projekt modułu C++ (superfastcode lub superfastcode2), a następnie wybierz polecenie Właściwości.

  2. Skonfiguruj właściwości kompilacji debugowania modułu, a następnie skonfiguruj te same właściwości kompilacji wydania :

    W górnej części okna dialogowego Strony właściwości projektu skonfiguruj następujące opcje konfiguracji pliku:

    Screenshot that shows how to set the project build and platform options for the C++ file in Visual Studio.

    1. W obszarze Konfiguracja wybierz pozycję Debugujlub Zwolnij. (Te opcje mogą być widoczne za pomocą polecenia Aktywny prefiks).

    2. W polu Platforma wybierz pozycję Aktywne (x64) lub Aktywne (Win32) w zależności od wybranego elementu w poprzednim kroku.

      Uwaga

      Podczas tworzenia własnych projektów należy oddzielnie skonfigurować konfiguracje debugowania i wydania zgodnie z określonymi wymaganiami scenariusza. W tym ćwiczeniu ustawisz konfiguracje tak, aby korzystały z kompilacji wydania środowiska CPython. Ta konfiguracja wyłącza niektóre funkcje debugowania środowiska uruchomieniowego języka C++, w tym asercji. Korzystanie z plików binarnych debugowania CPython (python_d.exe) wymaga różnych ustawień.

    3. Ustaw inne właściwości projektu zgodnie z opisem w poniższej tabeli.

      Aby zmienić wartość właściwości, wprowadź wartość w polu właściwości. W przypadku niektórych pól możesz wybrać bieżącą wartość, aby rozwinąć menu rozwijane opcji lub otworzyć okno dialogowe, aby ułatwić zdefiniowanie wartości.

      Po zaktualizowaniu wartości na karcie wybierz pozycję Zastosuj przed przełączeniem na inną kartę. Ta akcja pomaga zapewnić, że zmiany pozostaną.

      Karta i sekcja Właściwości Wartość
      Właściwości>konfiguracji — ogólne Nazwa docelowa Określ nazwę modułu, aby odwoływać się do niego z języka Python w from...import instrukcjach, takich jak superfastcode. Ta sama nazwa jest używana w kodzie języka C++ podczas definiowania modułu dla języka Python. Aby użyć nazwy projektu jako nazwy modułu, pozostaw wartość domyślną $<ProjectName>. W polu python_d.exedodaj _d ciąg na końcu nazwy.
      Typ konfiguracji Biblioteka dynamiczna (.dll)
      Zaawansowane właściwości>konfiguracji Rozszerzenie pliku docelowego .pyd (moduł rozszerzenia języka Python)
      C/C++>General Dodatkowe katalogi dołączania Dodaj folder dołączania języka Python odpowiednio do instalacji (na przykład c:\Python36\include).
      Preprocesor języka C/C++> Definicje preprocesora Jeśli jest obecny, zmień wartość _DEBUG na NDEBUG , aby była zgodna z wersją inną niż CPython. Jeśli używasz python_d.exe, pozostaw tę wartość bez zmian.
      Generowanie kodu C/C++> Biblioteka środowiska uruchomieniowego Wielowątkowa biblioteka DLL (/MD) zgodna z wersją wydania (niedebug) języka CPython. Jeśli używasz python_d.exe, pozostaw tę wartość jako bibliotekę DLL debugowania wielowątkowego (/MDd).
      Podstawowe testy środowiska uruchomieniowego Wartość domyślna
      Ogólne konsolidatora> Dodatkowe katalogi bibliotek Dodaj folder libs języka Python zawierający pliki .lib odpowiednio do instalacji (na przykład c:\Python36\libs). Pamiętaj, aby wskazać folder libs zawierający pliki .lib, a nie folder Lib zawierający pliki .py.

      Ważne

      Jeśli karta C/C++ nie jest wyświetlana jako opcja właściwości projektu, projekt nie zawiera plików kodu, które program Visual Studio identyfikuje jako pliki źródłowe C/C++. Ten warunek może wystąpić, jeśli tworzysz plik źródłowy bez rozszerzenia .c lub .cpp pliku.

      Jeśli przypadkowo wprowadzono plik module.coo zamiast module.cpp podczas tworzenia pliku C++, program Visual Studio tworzy plik, ale nie ustawia typu pliku na kompilator C/C+. Ten typ pliku jest niezbędny do aktywowania obecności karty właściwości C/C++ w oknie dialogowym właściwości projektu. Nieprawidłowadentyfikacja pozostaje nawet w przypadku zmiany nazwy pliku kodu przy użyciu rozszerzenia pliku .cpp .

      Aby poprawnie ustawić typ pliku kodu, w Eksplorator rozwiązań kliknij prawym przyciskiem myszy plik kodu i wybierz polecenie Właściwości. W polu Typ elementu wybierz pozycję Kompilator C/C++.

    4. Po zaktualizowaniu wszystkich właściwości wybierz przycisk OK.

    Powtórz kroki dla innego konfiguracji kompilacji.

  3. Przetestuj bieżącą konfigurację. Powtórz poniższe kroki zarówno dla kompilacji debugowania , jak i wydania obu projektów języka C++.

    1. Na pasku narzędzi programu Visual Studio ustaw konfigurację kompilacji na Debugowanie lub Wydanie:

      Screenshot that shows how to set the build configuration for the C++ project in Visual Studio.

    2. W Eksplorator rozwiązań kliknij prawym przyciskiem myszy projekt C++, a następnie wybierz polecenie Kompiluj.

      Pliki pyd znajdują się w folderze rozwiązania w obszarze Debugowanie i wydawanie, a nie w samym folderze projektu C++.

Dodawanie kodu i konfiguracji testu

Teraz możesz dodać kod do plików języka C++ i przetestować kompilację wydania .

  1. W przypadku projektu superfastcode C++ otwórz plik module.cpp w edytorze kodu.

  2. W pliku module.cpp wklej następujący kod:

    #include <Windows.h>
    #include <cmath>
    
    const double e = 2.7182818284590452353602874713527;
    
    double sinh_impl(double x) {
        return (1 - pow(e, (-2 * x))) / (2 * pow(e, -x));
    }
    
    double cosh_impl(double x) {
        return (1 + pow(e, (-2 * x))) / (2 * pow(e, -x));
    }
    
    double tanh_impl(double x) {
        return sinh_impl(x) / cosh_impl(x);
    }
    
  3. Zapisz zmiany.

  4. Skompiluj konfigurację wydania dla projektu C++, aby potwierdzić poprawność kodu.

Powtórz kroki dodawania kodu do pliku C++ dla projektu superfastcode2 i przetestuj kompilację wydania .

Konwertowanie projektów C++ na rozszerzenia języka Python

Aby uczynić bibliotekę DLL języka C++ rozszerzeniem dla języka Python, najpierw zmodyfikujesz wyeksportowane metody w celu interakcji z typami języka Python. Następnie dodaj funkcję w celu wyeksportowania modułu wraz z definicjami metod modułu.

W poniższych sekcjach pokazano, jak utworzyć rozszerzenia przy użyciu rozszerzeń CPython i PyBind11. Projekt superfasctcode używa rozszerzeń CPython i projektu superfasctcode2 implementuje PyBind11.

Korzystanie z rozszerzeń CPython

Aby uzyskać więcej informacji na temat kodu przedstawionego w tej sekcji, zobacz Podręcznik dokumentacji interfejsu API języka Python/C, szczególnie na stronie Obiekty modułu . Podczas przeglądania zawartości referencyjnej pamiętaj, aby wybrać swoją wersję języka Python na liście rozwijanej w prawym górnym rogu.

  1. W przypadku projektu superfastcode C++ otwórz plik module.cpp w edytorze kodu.

  2. Dodaj instrukcję w górnej części pliku module.cpp , aby dołączyć plik nagłówka Python.h :

    #include <Python.h>
    
  3. Zastąp tanh_impl kod metody, aby akceptował i zwracał typy języka Python (czyli ):PyObject*

    PyObject* tanh_impl(PyObject* /* unused module reference */, PyObject* o) {
        double x = PyFloat_AsDouble(o);
        double tanh_x = sinh_impl(x) / cosh_impl(x);
        return PyFloat_FromDouble(tanh_x);
    }
    
  4. Na końcu pliku dodaj strukturę, aby zdefiniować sposób prezentowania funkcji języka C++ tanh_impl w języku Python:

    static PyMethodDef superfastcode_methods[] = {
        // The first property is the name exposed to Python, fast_tanh
        // The second is the C++ function with the implementation
        // METH_O means it takes a single PyObject argument
        { "fast_tanh", (PyCFunction)tanh_impl, METH_O, nullptr },
    
        // Terminate the array with an object containing nulls
        { nullptr, nullptr, 0, nullptr }
    };
    
  5. Dodaj inną strukturę, aby zdefiniować sposób odwoływania się do modułu w kodzie języka Python, w szczególności podczas korzystania z instrukcji from...import .

    Nazwa zaimportowana w tym kodzie powinna być zgodna z wartością we właściwościach projektu w obszarze Właściwości>konfiguracji Ogólna>nazwa docelowa.

    W poniższym przykładzie nazwa oznacza, że można użyć instrukcji from superfastcode import fast_tanh w języku Python, "superfastcode" ponieważ fast_tanh jest zdefiniowana w pliku superfastcode_methods. Nazwy plików, które są wewnętrzne dla projektu C++, takie jak module.cpp, są niekonsekwencyjne.

    static PyModuleDef superfastcode_module = {
        PyModuleDef_HEAD_INIT,
        "superfastcode",                        // Module name to use with Python import statements
        "Provides some functions, but faster",  // Module description
        0,
        superfastcode_methods                   // Structure that defines the methods of the module
    };
    
  6. Dodaj metodę wywoływaną przez język Python podczas ładowania modułu. Nazwa metody musi mieć PyInit_<module-name>wartość , gdzie <nazwa modułu> dokładnie odpowiada właściwości właściwości>ogólnej>nazwy docelowej projektu C++. Oznacza to, że nazwa metody jest zgodna z nazwą pliku pyd skompilowanego przez projekt.

    PyMODINIT_FUNC PyInit_superfastcode() {
        return PyModule_Create(&superfastcode_module);
    }
    
  7. Skompiluj projekt C++ i zweryfikuj kod. Jeśli wystąpią błędy, zobacz sekcję "Rozwiązywanie problemów z błędami kompilacji".

Korzystanie z narzędzia PyBind11

Jeśli wykonasz kroki opisane w poprzedniej sekcji dla projektu superfastcode , możesz zauważyć, że ćwiczenie wymaga standardowego kodu w celu utworzenia struktur modułów dla rozszerzeń CPython języka C++. W tym ćwiczeniu dowiesz się, że narzędzie PyBind11 upraszcza proces kodowania. Makra są używane w pliku nagłówka języka C++, aby osiągnąć ten sam wynik, ale z znacznie mniejszym kodem. Jednak dodatkowe kroki są wymagane, aby upewnić się, że program Visual Studio może zlokalizować biblioteki PyBind11 i dołączyć pliki. Aby uzyskać więcej informacji na temat kodu w tej sekcji, zobacz PyBind11 basics (Podstawy PyBind11).

Instalowanie narzędzia PyBind11

Pierwszym krokiem jest zainstalowanie narzędzia PyBind11 w konfiguracji projektu. W tym ćwiczeniu użyjesz okna Program PowerShell dla deweloperów.

  1. Otwórz okno Narzędzia>wiersza polecenia>dla deweloperów programu PowerShell.

  2. W oknie Program PowerShell dla deweloperów zainstaluj narzędzie PyBind11 przy użyciu polecenia pip install pybind11 pip lub py -m pip install pybind11.

    Program Visual Studio instaluje oprogramowanie PyBind11 i jego pakiety zależne.

Dodawanie ścieżek PyBind11 do projektu

Po zainstalowaniu narzędzia PyBind11 należy dodać ścieżki PyBind11 do właściwości Dodatkowe katalogi dołączania dla projektu.

  1. W programie Developer PowerShell w oknie uruchom polecenie python -m pybind11 --includes lub py -m pybind11 --includes.

    Ta akcja powoduje wyświetlenie listy ścieżek PyBind11, które należy dodać do właściwości projektu.

  2. Wyróżnij listę ścieżek w oknie i wybierz pozycję Kopiuj (podwójna strona) na pasku narzędzi okna.

    Screenshot that shows how to highlight and copy the list of paths from the Developer PowerShell window in Visual Studio.

    Lista połączonych ścieżek jest dodawana do Schowka.

  3. W Eksplorator rozwiązań kliknij prawym przyciskiem myszy projekt superfastcode2, a następnie wybierz polecenie Właściwości.

  4. W górnej części okna dialogowego Strony właściwości w polu Konfiguracja wybierz pozycję Wydanie. (Ta opcja może być widoczna z opcją Aktywny prefiks).

  5. W oknie dialogowym na karcie C/C++>General (Ogólne) rozwiń menu rozwijane właściwości Dodatkowe katalogi dołączania i wybierz pozycję Edytuj.

  6. W oknie podręcznym dodaj listę skopiowanych ścieżek:

    Powtórz następujące kroki dla każdej ścieżki na liście łączonej skopiowanej z okna programu PowerShell dla deweloperów:

    1. Wybierz pozycję Nowy wiersz (folder z symbolem plus) na pasku narzędzi okna podręcznego okna dialogowego.

      Screenshot that shows how to add a PyBind11 path to the Additional Include Directories property.

      Program Visual Studio dodaje pusty wiersz w górnej części listy ścieżek i umieszcza kursor wstaw na początku.

    2. Wklej ścieżkę PyBind11 do pustego wiersza.

      Możesz również wybrać pozycję Więcej opcji (...) i użyć okna dialogowego eksploratora plików wyskakujących, aby przejść do lokalizacji ścieżki.

      Ważne

      • Jeśli ścieżka zawiera -I prefiks, usuń prefiks ze ścieżki.
      • Aby program Visual Studio rozpoznawał ścieżkę, ścieżka musi znajdować się w osobnym wierszu.

      Po dodaniu nowej ścieżki program Visual Studio wyświetli potwierdzoną ścieżkę w polu Wartość obliczona .

  7. Wybierz przycisk OK , aby zamknąć okno dialogowe wyskakujące.

  8. W górnej części okna dialogowego Strony właściwości umieść kursor na wartości właściwości Dodatkowe katalogi dołączania i upewnij się, że ścieżki PyBind11 są obecne.

  9. Wybierz przycisk OK , aby zastosować zmiany właściwości.

Aktualizowanie pliku module.cpp

Ostatnim krokiem jest dodanie pliku nagłówka PyBind11 i kodu makra do pliku C++ projektu.

  1. W przypadku projektu superfastcode2 C++ otwórz plik module.cpp w edytorze kodu.

  2. Dodaj instrukcję w górnej części pliku module.cpp , aby dołączyć plik nagłówka pybind11.h :

    #include <pybind11/pybind11.h>
    
  3. Na końcu pliku module.cpp dodaj kod makra PYBIND11_MODULE , aby zdefiniować punkt wejścia do funkcji C++:

    namespace py = pybind11;
    
    PYBIND11_MODULE(superfastcode2, m) {
        m.def("fast_tanh2", &tanh_impl, R"pbdoc(
            Compute a hyperbolic tangent of a single argument expressed in radians.
        )pbdoc");
    
    #ifdef VERSION_INFO
        m.attr("__version__") = VERSION_INFO;
    #else
        m.attr("__version__") = "dev";
    #endif
    }
    
  4. Skompiluj projekt C++ i zweryfikuj kod. Jeśli wystąpią błędy, zobacz następną sekcję Rozwiązywanie problemów z błędami kompilacji.

Rozwiązywanie problemów z błędami kompilacji

Zapoznaj się z poniższymi sekcjami, aby zapoznać się z możliwymi problemami, które mogą spowodować niepowodzenie kompilacji modułu C++.

Błąd: Nie można zlokalizować pliku nagłówka

Program Visual Studio zwraca komunikat o błędzie, taki jak E1696: Nie można otworzyć pliku open source "Python.h" lub C1083: Nie można otworzyć pliku dołączania: "Python.h": brak takiego pliku lub katalogu.

Ten błąd wskazuje, że zgodny nie może zlokalizować wymaganego pliku nagłówka (h) dla projektu.

  • W przypadku projektu superfastcode sprawdź, czy właściwość projektu C/C++>General>Additional Include Directories zawiera ścieżkę do folderu include dla instalacji języka Python. Przejrzyj kroki opisane w temacie Konfigurowanie właściwości projektu.

  • W przypadku projektu superfastcode2 sprawdź, czy ta sama właściwość projektu zawiera ścieżkę do folderu include dla instalacji PyBind11. Zapoznaj się z krokami Ścieżki usługi Ad PyBind do projektu.

Aby uzyskać więcej informacji na temat uzyskiwania dostępu do informacji o konfiguracji instalacji języka Python, zobacz dokumentację języka Python.

Błąd: Nie można zlokalizować bibliotek języka Python

Program Visual Studio zwraca błąd wskazujący, że zgodny program nie może zlokalizować wymaganych plików biblioteki (DLL) dla projektu.

  • W przypadku projektu C++ (superfastcode lub superfastcode2) sprawdź, czy właściwość Ogólne>katalogi dodatkowych bibliotek konsolidatora>zawiera ścieżkę do folderu libs dla instalacji języka Python. Przejrzyj kroki opisane w temacie Konfigurowanie właściwości projektu.

Aby uzyskać więcej informacji na temat uzyskiwania dostępu do informacji o konfiguracji instalacji języka Python, zobacz dokumentację języka Python.

Program Visual Studio zgłasza błędy konsolidatora związane z konfiguracją architektury docelowej dla projektu, takie jak x64 lub Win32.

  • W przypadku projektu C++ (superfastcode lub superfastcode2) zmień konfigurację docelową tak, aby odpowiadała instalacji języka Python. Jeśli na przykład konfiguracja docelowa projektu C++ to Win32, ale instalacja języka Python jest 64-bitowa, zmień konfigurację docelową projektu C++ na x64.

Testowanie kodu i porównywanie wyników

Teraz, gdy masz biblioteki DLL ustrukturyzowane jako rozszerzenia języka Python, możesz odwoływać się do nich z projektu języka Python, zaimportować moduły i użyć ich metod.

Udostępnianie biblioteki DLL w języku Python

Bibliotekę DLL można udostępnić w języku Python na kilka sposobów. Poniżej przedstawiono dwie opcje, które należy wziąć pod uwagę:

Jeśli projekt języka Python i projekt C++ znajdują się w tym samym rozwiązaniu, możesz użyć następującego podejścia:

  1. W Eksplorator rozwiązań kliknij prawym przyciskiem myszy węzeł Odwołania w projekcie języka Python, a następnie wybierz polecenie Dodaj odwołanie.

    Pamiętaj, aby wykonać tę akcję dla projektu w języku Python, a nie dla projektu języka C++.

  2. W oknie dialogowym Dodawanie odwołania rozwiń kartę Projekty.

  3. Zaznacz pola wyboru dla projektów superfastcode i superfastcode2 , a następnie wybierz przycisk OK.

    Screenshot that shows how to add a reference to the super fast code project in Visual Studio.

Alternatywnym podejściem jest zainstalowanie modułu rozszerzenia języka C++ w środowisku języka Python. Ta metoda udostępnia moduł innym projektom języka Python. Aby uzyskać więcej informacji, zobacz dokumentację projektu setuptools.

Wykonaj następujące kroki, aby zainstalować moduł rozszerzenia C++ w środowisku języka Python:

  1. W Eksplorator rozwiązań kliknij prawym przyciskiem myszy projekt C++, a następnie wybierz polecenie Dodaj>nowy element.

  2. Na liście szablonów plików wybierz pozycję Plik C++ (.cpp).

  3. Wprowadź nazwę pliku jako setup.py, a następnie wybierz pozycję Dodaj.

    Pamiętaj, aby wprowadzić nazwę pliku z rozszerzeniem języka Python (.py). Program Visual Studio rozpoznaje plik jako kod w języku Python pomimo użycia szablonu pliku C++.

    Program Visual Studio otwiera nowy plik w edytorze kodu.

  4. Wklej następujący kod do nowego pliku. Wybierz wersję kodu odpowiadającą metodzie rozszerzenia:

    • Rozszerzenia CPython (projekt superfastcode ):

      from setuptools import setup, Extension
      
      sfc_module = Extension('superfastcode', sources = ['module.cpp'])
      
      setup(
          name='superfastcode',
          version='1.0',
          description='Python Package with superfastcode C++ extension',
          ext_modules=[sfc_module]
      )
      
    • PyBind11 (projekt superfastcode2 ):

      from setuptools import setup, Extension
      import pybind11
      
      cpp_args = ['-std=c++11', '-stdlib=libc++', '-mmacosx-version-min=10.7']
      
      sfc_module = Extension(
          'superfastcode2',
          sources=['module.cpp'],
          include_dirs=[pybind11.get_include()],
          language='c++',
          extra_compile_args=cpp_args,
      )
      
      setup(
          name='superfastcode2',
          version='1.0',
          description='Python package with superfastcode2 C++ extension (PyBind11)',
          ext_modules=[sfc_module],
      )
      
  5. W projekcie języka C++ utwórz drugi plik o nazwie pyproject.toml i wklej następujący kod:

    [build-system]
    requires = ["setuptools", "wheel", "pybind11"]
    build-backend = "setuptools.build_meta"
    

    Plik TOML (.toml) używa formatu Tom's Obvious, Minimal Language dla plików konfiguracji.

  6. Aby skompilować rozszerzenie, kliknij prawym przyciskiem myszy nazwę pliku pyproject.toml na karcie okna kodu, a następnie wybierz polecenie Kopiuj pełną ścieżkę.

    Screenshot that shows how to copy the full path to the py project toml file in Visual Studio.

    Przed jego użyciem należy usunąć nazwę pyproject.toml ze ścieżki.

  7. W Eksplorator rozwiązań rozwiń węzeł Środowiska języka Python dla rozwiązania.

  8. Kliknij prawym przyciskiem myszy aktywne środowisko języka Python (pokazane pogrubioną) i wybierz pozycję Zarządzaj pakietami języka Python.

    Zostanie otwarte okienko Środowiska języka Python.

    Jeśli wymagany pakiet jest już zainstalowany, zostanie wyświetlony w tym okienku.

    • Przed kontynuowaniem wybierz znak X obok nazwy pakietu, aby go odinstalować.

    Screenshot that shows how to uninstall a package in the Python Environments pane.

  9. W polu wyszukiwania okienka Środowiska języka Python wklej skopiowaną ścieżkę i usuń nazwę pliku pyproject.toml z końca ścieżki.

    Screenshot that shows how to enter the path in the Python Environments pane to install the extension module.

  10. Wybierz klawisz Enter , aby zainstalować moduł z lokalizacji skopiowanej ścieżki.

    Napiwek

    Jeśli instalacja zakończy się niepowodzeniem z powodu błędu uprawnień, dodaj --user argument na końcu polecenia i spróbuj ponownie zainstalować.

Wywoływanie biblioteki DLL z języka Python

Po udostępnieniu biblioteki DLL dla języka Python zgodnie z opisem w poprzedniej sekcji możesz wywołać superfastcode.fast_tanh funkcje i superfastcode2.fast_tanh2 z języka Python. Następnie możesz porównać wydajność funkcji z implementacją języka Python.

Wykonaj następujące kroki, aby wywołać bibliotekę DLL modułu rozszerzenia z poziomu języka Python:

  1. Otwórz plik .py dla projektu języka Python w edytorze kodu.

  2. Na końcu pliku dodaj następujący kod, aby wywołać metody wyeksportowane z bibliotek DLL i wyświetlić ich dane wyjściowe:

    from superfastcode import fast_tanh
    test(lambda d: [fast_tanh(x) for x in d], '[fast_tanh(x) for x in d] (CPython C++ extension)')
    
    from superfastcode2 import fast_tanh2
    test(lambda d: [fast_tanh2(x) for x in d], '[fast_tanh2(x) for x in d] (PyBind11 C++ extension)')
    
  3. Uruchom program w języku Python, wybierając pozycję Rozpocznij>bez debugowania lub używając skrótu klawiaturowego Ctrl+F5.

    Uwaga

    Jeśli polecenie Uruchom bez debugowania nie jest dostępne, w Eksplorator rozwiązań kliknij prawym przyciskiem myszy projekt języka Python, a następnie wybierz polecenie Ustaw jako projekt startowy.

    Po wykonaniu programu zwróć uwagę, że procedury języka C++ działają około 5 do 20 razy szybciej niż implementacja języka Python.

    Oto przykład typowych danych wyjściowych programu:

    Running benchmarks with COUNT = 500000
    [tanh(x) for x in d] (Python implementation) took 0.758 seconds
    
    [fast_tanh(x) for x in d] (CPython C++ extension) took 0.076 seconds
    
    [fast_tanh2(x) for x in d] (PyBind11 C++ extension) took 0.204 seconds
    
  4. Spróbuj zwiększyć zmienną, COUNT aby różnice czasu są bardziej wyraźne.

    Kompilacja debugowania modułu C++ działa również wolniej niż kompilacja wydania, ponieważ kompilacja debugowania jest mniej zoptymalizowana i zawiera różne kontrole błędów. Spróbuj przełączyć się między konfiguracjami kompilacji w celu porównania, ale pamiętaj, aby zaktualizować właściwości ustawione wcześniej dla konfiguracji wydania.

Rozwiązywanie problemów z szybkością i obciążeniem procesów

W danych wyjściowych można zauważyć, że rozszerzenie PyBind11 nie jest tak szybkie, jak rozszerzenie CPython, chociaż powinno być szybsze niż czysta implementacja języka Python. Główną przyczyną różnicy jest użycie flagi METH_O. Ta flaga nie obsługuje wielu parametrów, nazw parametrów ani argumentów słów kluczowych. Narzędzie PyBind11 generuje nieco bardziej złożony kod, aby zapewnić bardziej podobny interfejs języka Python do wywołań. Ponieważ kod testowy wywołuje funkcję 500 000 razy, wyniki mogą znacznie zwiększyć obciążenie.

Możesz dodatkowo zmniejszyć obciążenie, przenosząc pętlę for do natywnego kodu w języku Python. Takie podejście polega na użyciu protokołu iteratora (lub typu PyBind11 py::iterable dla parametru funkcji) w celu przetworzenia każdego elementu. Usunięcie powtarzających się przejść między językiem Python a językiem C++ jest skutecznym sposobem skrócenia czasu przetwarzania sekwencji.

Rozwiązywanie problemów z błędami importowania

Jeśli podczas próby zaimportowania modułu ImportError zostanie wyświetlony komunikat, możesz rozwiązać go w jeden z następujących sposobów:

  • Podczas kompilowania za pomocą odwołania do projektu upewnij się, że właściwości projektu języka C++ są zgodne ze środowiskiem języka Python aktywowanym dla projektu języka Python. Upewnij się, że te same lokalizacje folderów są używane dla plików dołączania (h) i biblioteki (DLL).

  • Upewnij się, że plik wyjściowy jest poprawnie nazwany, na przykład superfastcode.pyd. Nieprawidłowa nazwa lub rozszerzenie uniemożliwia importowanie niezbędnego pliku.

  • Jeśli moduł zostanie zainstalowany przy użyciu pliku setup.py , pamiętaj, aby uruchomić pip polecenie w środowisku języka Python aktywowanym dla projektu języka Python. Po rozwinięciu aktywnego środowiska języka Python dla projektu w Eksplorator rozwiązań powinien zostać wyświetlony wpis dla projektu języka C++, taki jak superfastcode.

Debugowanie kodu C++

Program Visual Studio obsługuje debugowanie kodu w języku Python i C++. W poniższych krokach przedstawiono proces debugowania dla projektu C++ superfastcode , ale proces jest taki sam dla projektu superfastcode2 .

  1. W Eksplorator rozwiązań kliknij prawym przyciskiem myszy projekt języka Python i wybierz polecenie Właściwości.

  2. W okienku Właściwości wybierz kartę Debugowanie, a następnie wybierz opcję Debuguj> Włącz debugowanie kodu natywnego.

    Napiwek

    Po włączeniu debugowania kodu natywnego okno danych wyjściowych języka Python może zostać zamknięte natychmiast po zakończeniu działania programu bez wstrzymywania i wyświetlania klawisza Naciśnij dowolny klawisz, aby kontynuować monit. Aby wymusić wstrzymanie i monit po włączeniu debugowania kodu natywnego, dodaj -i argument do pola Uruchom>argumenty interpretera na karcie Debugowanie . Ten argument umieszcza interpreter języka Python w tryb interaktywny po uruchomieniu kodu. Program czeka na wybranie klawiszy Ctrl+Z+Enter, aby zamknąć okno. Alternatywnym podejściem jest dodanie import os instrukcji i os.system("pause") na końcu programu w języku Python. Ten kod duplikuje oryginalny monit o wstrzymanie.

  3. Wybierz pozycję Zapisz plik>(lub Ctrl+S), aby zapisać zmiany właściwości.

  4. Na pasku narzędzi programu Visual Studio ustaw konfigurację kompilacji na Debugowanie.

  5. Ponieważ kod zwykle trwa dłużej w debugerze, możesz zmienić COUNT zmienną w projekcie języka Python .py pliku na wartość, która jest około pięć razy mniejsza niż wartość domyślna. Na przykład zmień ją z 5000000 na 100000.

  6. W kodzie języka C++ ustaw punkt przerwania w pierwszym wierszu tanh_impl metody.

  7. Uruchom debuger, wybierając pozycję Debuguj>rozpocznij debugowanie lub użyj skrótu klawiaturowego F5.

    Debuger zatrzymuje się po wywołaniu kodu punktu przerwania. Jeśli punkt przerwania nie zostanie trafiony, upewnij się, że konfiguracja jest ustawiona na Debugowanie i zapisanie projektu, co nie odbywa się automatycznie po uruchomieniu debugera.

    Screenshot of C++ code that contains a breakpoint in Visual Studio.

  8. W punkcie przerwania możesz przejść przez kod języka C++, zbadać zmienne itd. Aby uzyskać więcej informacji na temat tych funkcji, zobacz Debugowanie języka Python i języka C++ razem.

Podejścia alternatywne

Rozszerzenia języka Python można tworzyć na różne sposoby, zgodnie z opisem w poniższej tabeli. Pierwsze dwa wiersze CPython i PyBind11, zostały omówione w tym artykule.

Metoda Zabytkowe Reprezentatywni użytkownicy
Moduły rozszerzeń C/C++ dla CPython 1991 Standardowa biblioteka
PyBind11 (zalecane dla języka C++) 2015
Cython (zalecane dla C) 2007 gevent, kivy
HPy 2019
mypyc 2017
ctypes 2003 oscrypto
cffi 2013 kryptografia, pypy
SWIG 1996 crfsuite
Boost.Python 2002
cppyy 2017