Créer une extension C++ pour Python dans Visual Studio

Dans cet article, vous créez un module d’extension C++ pour CPython pour calculer une tangente hyperbolique et l’appeler à partir du code Python. La routine est implémentée en premier dans Python pour illustrer le gain de performances relatif de l’implémentation de la même routine en C++.

Les modules de code écrits en C++ (ou en C) sont couramment utilisés pour étendre les capacités d’un interpréteur Python. Il existe trois types principaux de modules d’extension :

  • Modules d’accélération : Permettent des performances accélérées. Comme Python est un langage interprété, vous pouvez écrire un module d’accélération en C++ pour une meilleure performance.
  • Modules wrapper : Exposent des interfaces C/C++ existantes au code Python ou exposent une API plus proche de Python qui est facile à utiliser depuis Python.
  • Modules d’accès système de bas niveau : Créez des modules d’accès système pour accéder aux fonctionnalités de bas niveau de l’environnement d’exécution CPython, du système d’exploitation ou du matériel sous-jacent.

Cet article démontre deux façons de rendre un module d’extension C++ disponible pour Python :

  • Utilisez les extensions standard CPython, comme décrit dans la documentation Python.
  • Utilisez PyBind11, que nous recommandons pour C++11 en raison de sa simplicité. Pour garantir la compatibilité, assurez-vous de travailler avec l’une des versions les plus récentes de Python.

L’exemple terminé de cette démarche est disponible sur GitHub à l’adresse python-samples-vs-cpp-extension.

Prérequis

  • Visual Studio 2017 ou ultérieur, avec la charge de travail de développement Python installée. La charge de travail inclut les outils de développement natifs Python, qui ajoutent la charge de travail C++ et les jeux d’outils nécessaires pour les extensions natives.

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

    Pour plus d’informations sur les options d’installation, consultez Installer la prise en charge de Python pour Visual Studio.

    Remarque

    L’installation de la charge de travail Applications de science et analyse des données inclut par défaut Python et l’option Outils de développement natifs Python.

  • Si vous installez Python séparément, assurez-vous de sélectionner Télécharger les symboles de débogage sous Options avancées dans l’installateur Python. Cette option est nécessaire pour que vous utilisiez le débogage en mode mixte entre votre code Python et votre code natif.

Créer l’application Python

Suivez ces étapes pour créer l’application Python.

  1. Créez un projet Python dans Visual Studio en sélectionnant Fichier>Nouveau>Projet.

  2. Dans la boîte de dialogue Créer un nouveau projet, recherchez python. Sélectionnez le modèle Application Python et cliquez sur Suivant.

  3. Saisissez un Nom du projet et Emplacement, puis cliquez sur Créer.

    Visual Studio crée le nouveau projet. Le projet s’ouvre dans Explorateur de solutions et le fichier de projet (.py) s’ouvre dans l’éditeur de code.

  4. Dans le fichier .py, collez le code suivant. Pour découvrir certaines des fonctionnalités d’édition de Python, essayez d’entrer le code manuellement.

    Ce code calcule une tangente hyperbolique sans utiliser la bibliothèque mathématique, et c’est ce que vous allez accélérer plus tard avec les extensions natives Python.

    Conseil

    Écrivez votre code en Python pur avant de le réécrire en C++. De cette façon, vous pouvez plus facilement vérifier que votre code Python natif est correct.

    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. Exécutez le programme en sélectionnant Déboguer>Démarrer sans débogage ou sélectionnez le raccourci clavier Ctrl+F5.

    Une fenêtre de commande s’ouvre pour afficher la sortie du programme.

  6. Dans la sortie, notez la durée indiquée pour le processus de benchmark.

    Pour cette démarche, le processus de benchmark devrait prendre environ 2 secondes.

  7. Si nécessaire, ajustez la valeur de la variable COUNT dans le code pour que le benchmark se termine en environ 2 secondes sur votre ordinateur.

  8. Exécutez à nouveau le programme et confirmez que la valeur COUNT modifiée produit le benchmark en environ 2 secondes.

Conseil

Lorsque vous exécutez des benchmarks, utilisez toujours l’option Déboguer>Démarrer sans débogage. Cette méthode aide à éviter les frais généraux qui peuvent survenir lorsque vous exécutez le code dans le débogueur Visual Studio.

Créer les projets C++ principaux

Suivez ces étapes pour créer deux projets C++ identiques, superfastcode et superfastcode2. Plus tard, vous utilisez une approche différente dans chaque projet pour exposer le code C++ au code Python.

  1. Dans Explorateur de solutions, faites un clic droit sur le nom de la solution, puis sélectionnez Ajouter>Nouveau projet.

    Une solution Visual Studio peut contenir à la fois des projets Python et C++, ce qui est l’un des avantages de l’utilisation de Visual Studio pour le développement Python.

  2. Dans la boîte de dialogue Ajouter un nouveau projet, définissez le filtre de Langue sur C++, et saisissez empty dans la zone Recherche.

  3. Dans la liste des résultats du modèle de projet, sélectionnez Projet vide, puis cliquez sur Suivant.

  4. Dans la boîte de dialogue Configurer votre nouveau projet, saisissez le Nom du projet :

    • Pour le premier projet, saisissez le nom superfastcode.
    • Pour le deuxième projet, saisissez le nom superfastcode2.
  5. Sélectionnez Créer.

Veillez à répéter ces étapes et à créer deux projets.

Conseil

Une approche alternative est disponible lorsque vous avez installé les outils de développement natifs Python dans Visual Studio. Vous pouvez commencer par le modèle Module d’extension Python, qui préremplit bon nombre des étapes décrites dans cet article.

Pour la démarche de cet article, commencer par un projet vide permet de démontrer comment construire le module d’extension pas à pas. Après avoir compris le processus, vous pouvez utiliser le modèle alternatif pour gagner du temps lorsque vous écrivez vos propres extensions.

Ajouter un fichier C++ au projet

Ensuite, ajoutez un fichier C++ à chaque projet.

  1. Dans Explorateur de solutions, développez le projet, faites un clic droit sur le nœud Fichiers source, puis sélectionnez Ajouter>Nouvel élément.

  2. Dans la liste des modèles de fichier, sélectionnez Fichier C++ (.cpp).

  3. Saisissez le Nom du fichier comme module.cpp, puis cliquez sur Ajouter.

    Important

    Assurez-vous que le nom de fichier inclut l’extension .cpp. Visual Studio recherche un fichier avec l’extension .cpp pour permettre l’affichage des pages de propriétés du projet C++.

  4. Dans la barre d’outils, développez le menu déroulant Configuration et sélectionnez votre type de configuration cible :

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

    • Pour un runtime Python 64 bits, activez la configuration x64.
    • Pour un runtime Python 32 bits, activez la configuration Win32.

Assurez-vous de répéter ces étapes pour les deux projets.

Configurer les propriétés du projet

Avant d’ajouter du code aux nouveaux fichiers C++, configurez les propriétés pour chaque projet de module C++ et testez les configurations pour vous assurer que tout fonctionne correctement.

Vous devez définir les propriétés du projet pour les configurations de build debug et release de chaque module.

  1. Dans Explorateur de solutions, faites un clic droit sur le projet de module C++ (superfastcode ou superfastcode2), puis sélectionnez Propriétés.

  2. Configurez les propriétés pour la build debug du module, puis configurez les mêmes propriétés pour la build release :

    En haut de la boîte de dialogue Pages de propriétés du projet, configurez les options de configuration de fichier suivantes :

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

    1. Pour la Configuration, sélectionnez Debug ou Release. Vous pouvez voir ces options avec le préfixe Active).

    2. Pour la Plateforme, sélectionnez Active (x64) ou Active (Win32), selon votre sélection dans l’étape précédente.

      Remarque

      Lorsque vous créez vos propres projets, vous voudrez configurer les configurations debug et release séparément, selon les besoins spécifiques de votre scénario. Dans cet exercice, vous définissez les configurations pour utiliser une version de production de CPython. Cette configuration désactive certaines fonctionnalités de débogage du runtime C++, y compris les assertions. L’utilisation de fichiers binaires de débogage CPython (python_d.exe) nécessite des paramètres différents.

    3. Définissez les autres propriétés du projet comme décrit dans le tableau suivant.

      Pour modifier une valeur de propriété, saisissez une valeur dans le champ de propriété. Pour certains champs, vous pouvez sélectionner la valeur actuelle pour développer un menu déroulant de choix ou ouvrir une boîte de dialogue pour aider à définir la valeur.

      Après avoir mis à jour les valeurs sur un onglet, sélectionnez Appliquer avant de passer à un onglet différent. Cette action permet de garantir que vos modifications restent en place.

      Onglet et section Propriété Valeur
      Propriétés de configuration>Général. Nom de la cible Spécifiez le nom du module pour y faire référence depuis Python dans les instructions from...import, telles que superfastcode. Vous utilisez ce même nom dans le code C++ lorsque vous définissez le module pour Python. Pour utiliser le nom du projet comme nom de module, laissez la valeur par défaut $<ProjectName>. Pour python_d.exe, ajoutez _d à la fin du nom.
      Type de configuration Bibliothèque dynamique (.dll)
      Propriétés de configuration>Avancé Extension du fichier cible .pyd (Module d’extension Python)
      C/C++>Général Autres répertoires Include Ajoutez le dossier include Python comme approprié pour votre installation (par exemple, c:\Python36\include).
      C/C++>Préprocesseur Définitions de préprocesseur Si elle est présente, changez la valeur _DEBUG en NDEBUG pour correspondre à la version non déboguée de CPython. Lorsque vous utilisez python_d.exe, laissez cette valeur inchangée.
      C/C++>Génération de code Bibliothèque Runtime Multi-threaded DLL (/MD) pour correspondre à la version de production (non déboguée) de CPython. Lorsque vous utilisez python_d.exe, laissez cette valeur comme Multi-threaded Debug DLL (/MDd).
      Vérifications de base à l’exécution Par défaut
      Éditeur de liens>Général Répertoires de bibliothèques supplémentaires Ajoutez le dossier libs Python contenant des fichiers .lib en fonction de votre installation, par exemple c:\Python36\libs). Veillez à pointer vers le dossier libs qui contient des fichiers .lib, et non vers le dossier Lib qui contient des fichiers .py.

      Important

      Si l’onglet C/C++ n’est pas affiché comme option pour les propriétés du projet, alors le projet ne contient aucun fichier de code que Visual Studio identifie comme des fichiers source C/C++. Cette situation peut se produire si vous créez un fichier source sans extension de fichier .c ou .cpp.

      Si vous avez accidentellement saisi module.coo au lieu de module.cpp lorsque vous avez créé le fichier C++, Visual Studio crée le fichier mais ne définit pas le type de fichier en compilateur C/C++. Ce type de fichier est nécessaire pour activer la présence de l’onglet de propriétés C/C++ dans la boîte de dialogue des propriétés du projet. L’identification incorrecte reste même si vous renommez le fichier de code avec une extension de fichier .cpp.

      Pour définir correctement le type de fichier de code, dans Explorateur de solutions, faites un clic droit sur le fichier de code, puis sélectionnez Propriétés. Pour le Type d’élément, sélectionnez Compilateur C/C++.

    4. Après avoir mis à jour toutes les propriétés, sélectionnez OK.

    Répétez les étapes pour l’autre configuration de build.

  3. Testez votre configuration actuelle. Répétez les étapes suivantes pour les configurations debug et release des deux projets C++.

    1. Sur la barre d’outils de Visual Studio, définissez la configuration Build sur Debug ou Release :

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

    2. Dans Explorateur de solutions, faites un clic droit sur le projet C++, puis sélectionnez Build.

      Les fichiers .pyd se trouvent dans le dossier solution, sous Debug et Release, et non dans le dossier du projet C++ lui-même.

Ajouter du code et tester la configuration

Maintenant, vous êtes prêt à ajouter du code à vos fichiers C++ et à tester la build release.

  1. Pour le projet C++ superfastcode, ouvrez le fichier module.cpp dans l’éditeur de code.

  2. Dans le fichier module.cpp, collez le code suivant :

    #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. Enregistrez vos modifications.

  4. Générez la configuration release pour le projet C++ pour confirmer que votre code est correct.

Répétez les étapes pour ajouter du code au fichier C++ pour le projet superfastcode2 et tester la build release.

Convertir les projets C++ en extensions Python

Pour faire du DLL C++ une extension pour Python, vous modifiez d’abord les méthodes exportées pour interagir avec les types Python. Ensuite, ajoutez une fonction pour exporter le module, ainsi que des définitions pour les méthodes du module.

Les sections suivantes démontrent comment créer les extensions en utilisant les extensions CPython et PyBind11. Le projet superfasctcode utilise les extensions CPython et le projet superfasctcode2 implémente PyBind11.

Utiliser les extensions CPython

Pour plus d’informations sur le code présenté dans cette section, veuillez consulter la section Python/C API Reference Manual, et en particulier la page Objets de module. Lorsque vous examinez le contenu de référence, assurez-vous de sélectionner votre version de Python dans la liste déroulante en haut à droite.

  1. Pour le projet C++ superfastcode, ouvrez le fichier module.cpp dans l’éditeur de code.

  2. Ajoutez une instruction en haut du fichier module.cpp pour inclure le fichier d’en-tête Python.h :

    #include <Python.h>
    
  3. Remplacez le code de la méthode tanh_impl pour accepter et renvoyer des types Python (c’est-à-dire un 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. À la fin du fichier, ajoutez une structure pour définir comment présenter la tanh_impl fonction C++ à 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. Ajoutez une autre structure pour définir comment faire référence au module dans votre code Python, spécifiquement lorsque vous utilisez l’instruction from...import.

    Le nom importé dans ce code doit correspondre à la valeur dans les propriétés du projet sous Propriétés de configuration>Général>Nom de la cible.

    Dans l’exemple suivant, le nom "superfastcode" signifie que vous pouvez utiliser l’instruction from superfastcode import fast_tanh dans Python car fast_tanh est défini dans superfastcode_methods. Les noms de fichiers internes au projet C++, tels que module.cpp, ne sont pas essentiels.

    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. Ajoutez une méthode que Python appelle lorsqu’il charge le module. Le nom de la méthode doit être PyInit_<module-name>, où <nom-du-module> correspond exactement à la propriété Propriétés de configurations>Général>Nom de la cible du projet C++. Autrement dit, le nom de la méthode correspond au nom de fichier du fichier .pyd généré par le projet.

    PyMODINIT_FUNC PyInit_superfastcode() {
        return PyModule_Create(&superfastcode_module);
    }
    
  7. Construisez le projet C++ et vérifiez votre code. Si vous rencontrez des erreurs, consultez la section « Dépanner les erreurs de compilation ».

Utiliser PyBind11

Si vous avez terminé les étapes de la section précédente pour le projet superfastcode, vous remarquerez peut-être que l’exercice nécessite du code de base pour créer les structures de module pour les extensions C++ CPython. Dans cet exercice, vous découvrez que PyBind11 simplifie le processus de codage. Vous utilisez des macros dans un fichier d’en-tête C++ pour obtenir le même résultat, mais avec beaucoup moins de code. Cependant, des étapes supplémentaires sont nécessaires pour que Visual Studio puisse localiser les bibliothèques et les fichiers d’en-tête de PyBind11. Pour plus d’informations sur le code de cette section, consultez Principes de base de PyBind11.

Installer PyBind11

La première étape consiste à installer PyBind11 dans votre configuration de projet. Dans cet exercice, vous utilisez la fenêtre Developer PowerShell.

  1. Ouvrez la fenêtre Outils>Ligne de commande>Developer PowerShell.

  2. Dans la fenêtre Developer PowerShell, installez PyBind11 en utilisant la commande pip pip install pybind11 ou py -m pip install pybind11.

    Visual Studio installe PyBind11 et ses packages dépendants.

Ajouter les chemins PyBind11 au projet

Après l’installation de PyBind11, vous devez ajouter les chemins PyBind11 à la propriété Répertoires d’inclusion supplémentaires pour le projet.

  1. Dans la fenêtre Developer PowerShell, exécutez la commande python -m pybind11 --includes ou py -m pybind11 --includes.

    Cette action affiche une liste de chemins PyBind11 que vous devez ajouter à vos propriétés de projet.

  2. Surlignez la liste des chemins dans la fenêtre et sélectionnez Copier (double page) dans la barre d’outils de la fenêtre.

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

    La liste de chemins concaténés est ajoutée à votre presse-papiers.

  3. Dans Explorateur de solutions, faits un clic droit sur le projet superfastcode2, puis sélectionnez Propriétés.

  4. En haut de la boîte de dialogue Pages de propriétés, pour le champ Configuration, sélectionnez Release. (Vous pouvez voir cette option avec le préfixe Actif.)

  5. Dans la boîte de dialogue, dans l’onglet C/C++>Général, développez le menu déroulant pour la propriété Répertoires d’inclusion supplémentaires, et sélectionnez Modifier.

  6. Dans la boîte de dialogue contextuelle, ajoutez la liste des chemins copiés :

    Répétez ces étapes pour chaque chemin dans la liste concaténée copiée depuis la fenêtre Developer PowerShell :

    1. Sélectionnez Nouvelle ligne (dossier avec un symbole plus) dans la barre d’outils de la boîte de dialogue contextuelle.

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

      Visual Studio ajoute une ligne vide en haut de la liste des chemins et positionne le curseur d’insertion au début.

    2. Collez le chemin PyBind11 dans la ligne vide.

      Vous pouvez également sélectionner Plus d’options (...) et utiliser une boîte de dialogue explorateur de fichiers contextuelle pour parcourir l’emplacement du chemin.

      Important

      • Si le chemin contient le préfixe -I, supprimez le préfixe du chemin.
      • Pour que Visual Studio reconnaisse un chemin, le chemin doit être sur une ligne séparée.

      Après avoir ajouté un nouveau chemin, Visual Studio affiche le chemin confirmé dans le champ Valeur évaluée.

  7. Sélectionnez >OK pour fermer la boîte de dialogue contextuelle.

  8. En haut de la boîte de dialogue Pages de propriétés, survolez la valeur de la propriété Répertoires d’inclusion supplémentaires et confirmez que les chemins PyBind11 sont présents.

  9. Sélectionnez OK pour appliquer les modifications de propriété.

Mettre à jour le fichier module.cpp

La dernière étape consiste à ajouter le fichier d’en-tête et le code macro de PyBind11 au fichier C++ du projet.

  1. Pour le projet C++ superfastcode2, ouvrez le fichier module.cpp dans l’éditeur de code.

  2. Ajoutez une instruction en haut du fichier module.cpp pour inclure le fichier d’en-tête pybind11.h :

    #include <pybind11/pybind11.h>
    
  3. À la fin du fichier module.cpp, ajoutez le code de la macro PYBIND11_MODULE pour définir le point d’entrée vers la fonction 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. Construisez le projet C++ et vérifiez votre code. Si vous rencontrez des erreurs, consultez la section suivante, Dépannage des erreurs de compilation.

Dépannage des erreurs de compilation

Examinez les sections suivantes pour identifier les problèmes potentiels pouvant entraîner l’échec de la construction du module C++.

Erreur : Impossible de localiser le fichier d’en-tête

Visual Studio renvoie un message d’erreur tel que E1696: Impossible d’ouvrir le fichier source « Python.h » ou C1083: Impossible d’ouvrir le fichier Include: « Python.h »: Aucun fichier ou dossier de ce type.

Cette erreur indique que le compilateur ne parvient pas à localiser un fichier d’en-tête (.h) requis pour votre projet.

  • Pour le projet superfastcode, vérifiez que la propriété du projet C/C++>Général>Répertoires d’inclusion supplémentaires contient le chemin du dossier include de votre installation Python. Revoir les étapes de Configurer les propriétés du projet.

  • Pour le projet superfastcode2, vérifiez que la même propriété de projet contient le chemin du dossier include pour votre installation PyBind11. Revoir les étapes Ajouter les chemins PyBind au projet.

Pour plus d’informations sur l’accès aux informations de configuration de votre installation Python, veuillez consulter la documentation Python.

Erreur : Impossible de localiser les bibliothèques Python

Visual Studio renvoie une erreur indiquant que le compilateur ne peut pas localiser les fichiers de bibliothèque (DLL) requis pour votre projet.

  • Pour le projet C++ (superfastcode ou superfastcode2), vérifiez que la propriété Linker>Général>Répertoires de bibliothèque supplémentaires contient le chemin du dossier libs pour votre installation Python. Revoir les étapes de Configurer les propriétés du projet.

Pour plus d’informations sur l’accès aux informations de configuration de votre installation Python, veuillez consulter la documentation Python.

Visual Studio signale des erreurs de liaison liées à la configuration de l’architecture cible pour votre projet, telle que x64 ou Win32.

  • Pour le projet C++ (superfastcode ou superfastcode2), modifiez la configuration cible pour correspondre à votre installation Python. Par exemple, si la configuration cible de votre projet C++ est Win32, mais votre installation Python est 64 bits, changez la configuration cible du projet C++ en x64.

Tester le code et comparer les résultats

Maintenant que la DLL est structurée en extensions Python, vous pouvez les référencer à partir du projet Python, importer les modules et utiliser leurs méthodes.

Rendez votre DLL disponible pour Python

Vous pouvez rendre votre DLL disponible pour Python de plusieurs manières. Voici deux options à considérer :

Si votre projet Python et votre projet C++ sont dans la même solution, vous pouvez utiliser l’approche suivante :

  1. Dans Explorateur de solutions, faites un clic droit sur le nœud Références de votre projet Python, puis sélectionnez Ajouter une référence.

    Assurez-vous d’effectuer cette action pour votre projet Python et non pour votre projet C++.

  2. Dans la boîte de dialogue Ajouter une référence, développez l’onglet Projets.

  3. Sélectionnez les cases à cocher des projets superfastcode et superfastcode2, puis sélectionnez OK.

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

Une autre approche consiste à installer le module d’extension C++ dans votre environnement Python. Cette méthode rend le module disponible pour d’autres projets Python. Pour plus d’informations, veuillez consulter la documentation du projet setuptools.

Effectuez les étapes suivantes pour installer le module d’extension C++ dans votre environnement Python :

  1. Dans Explorateur de solutions, faites un clic droit sur votre projet C++, puis sélectionnez Ajouter>Nouvel élément.

  2. Dans la liste des modèles de fichier, sélectionnez Fichier C++ (.cpp).

  3. Saisissez le Nom du fichier sous la forme setup.py, puis sélectionnez Ajouter.

    Assurez-vous d’entrer le nom de fichier avec l’extension Python (.py). Visual Studio reconnaît le fichier comme du code Python malgré l’utilisation du modèle de fichier C++.

    Visual Studio ouvre le nouveau fichier dans l’éditeur de code.

  4. Collez le code suivant dans le nouveau fichier. Choisissez la version de code correspondant à votre méthode d’extension :

    • Extensions CPython (projet 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 (projet 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. Dans le projet C++, créez un deuxième fichier nommé pyproject.toml, et collez le code suivant :

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

    Le fichier TOML (.toml) utilise le format Tom’s Obvious, Minimal Language pour les fichiers de configuration.

  6. Pour construire l’extension, faites un clic droit sur le nom de fichier pyproject.toml dans l’onglet de la fenêtre de code, puis sélectionnez Copier le chemin complet.

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

    Supprimez le nom pyproject.toml du chemin avant de l’utiliser.

  7. Dans Explorateur de solutions, développez le nœud Environnements Python pour la solution.

  8. Faites un clic droit sur l’environnement Python actif (affiché en gras), puis sélectionnez Gérer les packages Python.

    Le volet Environnements Python s’ouvre.

    Si le package nécessaire est déjà installé, vous le verrez répertorié dans ce volet.

    • Avant de continuer, sélectionnez le X à côté du nom du package pour le désinstaller.

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

  9. Dans la zone de recherche du volet Environnements Python, collez le chemin copié et supprimez le nom de fichier pyproject.toml de la fin du chemin.

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

  10. Sélectionnez Entrée pour installer le module à partir de l’emplacement du chemin copié.

    Conseil

    Si l’installation échoue en raison d’une erreur de permission, ajoutez l’argument --user à la fin de la commande, puis essayez à nouveau l’installation.

Appeler la DLL à partir de Python

Après avoir rendu le DLL disponible pour Python, comme décrit dans la section précédente, vous êtes prêt à appeler les fonctions superfastcode.fast_tanh et superfastcode2.fast_tanh2 depuis Python. Vous pouvez ensuite comparer les performances de la fonction à l’implémentation Python.

Suivez cette procédure pour appeler le module d’extension DLL depuis Python :

  1. Ouvrez le fichier .py pour votre projet Python dans l’éditeur de code.

  2. À la fin du fichier, ajoutez le code suivant pour appeler les méthodes exportées à partir des DLLs et afficher leur sortie :

    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. Exécutez le programme Python en sélectionnant Déboguer>Démarrer sans débogage ou utilisez le raccourci clavier Ctrl+F5.

    Remarque

    Si la commande Démarrer sans débogage n’est pas disponible, dans Explorateur de solutions, faites un clic droit sur le projet Python, puis sélectionnez Définir comme projet de démarrage.

    Lorsque le programme s’exécute, remarquez que les routines C++ s’exécutent environ 5 à 20 fois plus rapidement que l’implémentation Python.

    Voici un exemple de sortie de programme typique :

    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. Essayez d’augmenter la variable COUNT pour que les différences de temps soient plus prononcées.

    Une build debug du module C++ s’exécute également plus lentement qu’une construction release car la version de débogage est moins optimisée et contient divers contrôles d’erreur. Essayez de passer entre les configurations de construction pour comparer, mais n’oubliez pas de mettre à jour les propriétés que vous avez définies précédemment pour la configuration de sortie.

Adressez la vitesse de traitement et les frais généraux

Dans la sortie, vous remarquerez peut-être que l’extension PyBind11 n’est pas aussi rapide que l’extension CPython, bien qu’elle devrait être plus rapide que l’implémentation Python pure. La principale raison de la différence est l’utilisation du drapeau METH_O. Ce drapeau ne prend pas en charge plusieurs paramètres, noms de paramètres ou arguments de mots-clés. PyBind11 génère du code légèrement plus complexe pour fournir une interface plus semblable à Python aux appelants. Comme le code de test appelle la fonction 500 000 fois, les résultats peuvent grandement amplifier les frais généraux.

Vous pouvez réduire les frais généraux en déplaçant la boucle for dans le code Python natif. Cette approche implique l’utilisation du protocole d’itérateur (ou du type PyBind11 py::iterable pour le paramètre de fonction) pour traiter chaque élément. La suppression des transitions répétées entre Python et C++ est un moyen efficace de réduire le temps nécessaire au traitement de la séquence.

Résoudre les erreurs d’importation

Si vous recevez un message ImportError lorsque vous essayez d’importer votre module, vous pouvez le résoudre de l’une des manières suivantes :

  • Lorsque vous construisez via une référence de projet, assurez-vous que les propriétés du projet C++ correspondent à l’environnement Python activé pour votre projet Python. Confirmez que les mêmes emplacements de dossiers sont utilisés pour les fichiers Include (.h) et Library (DLL).

  • Assurez-vous que votre fichier de sortie est correctement nommé, tel que superfastcode.pyd. Un nom ou une extension incorrecte empêche l’importation du fichier nécessaire.

  • Si vous installez votre module en utilisant le fichier setup.py, assurez-vous d’exécuter la commande pip dans l’environnement Python activé pour votre projet Python. Lorsque vous développez l’environnement Python actif pour votre projet dans Explorateur de solutions, vous devriez voir une entrée pour le projet C++, telle que superfastcode.

Déboguer le code C++

Visual Studio prend en charge le débogage simultané du code Python et du code C++. Les étapes suivantes montrent le processus de débogage pour le projet C++ superfastcode, mais le processus est le même pour le projet superfastcode2.

  1. Dans l’Explorateur de solutions, faites un clic droit sur le projet Python, et sélectionnez Propriétés.

  2. Dans le volet Propriétés, sélectionnez l’onglet Débogage, puis sélectionnez l’option Débogage>Activer le débogage du code natif.

    Conseil

    Lorsque vous activez le débogage du code natif, la fenêtre de sortie Python peut se fermer immédiatement après la fin du programme sans mettre en pause et afficher l’invite Appuyez sur une touche pour continuer. Pour forcer la pause et l’invite après avoir activé le débogage du code natif, ajoutez l’argument -i au champ Exécuter>Arguments de l’interpréteur de l’onglet Débogage. Cet argument place l’interprète Python en mode interactif après l’exécution du code. Le programme attend que vous sélectionniez Ctrl+Z+Entrée pour fermer la fenêtre. Une approche alternative est d’ajouter des déclarations import os et os.system("pause") à la fin de votre programme Python. Ce code double l’invite de pause d’origine.

  3. Sélectionnez Fichier>Enregistrer (ou Ctrl+S) pour enregistrer les modifications de propriété.

  4. Sur la barre d’outils de Visual Studio, définissez la configuration de Build sur Déboguer.

  5. Parce que le code prend généralement plus de temps à s’exécuter dans le débogueur, vous voudrez peut-être changer la variable COUNT dans le fichier .py de votre projet Python pour une valeur d’environ cinq fois plus petite que la valeur par défaut. Par exemple, changez la valeur de 500000 à 100000.

  6. Dans votre code C++, définissez un point d’arrêt sur la première ligne de la méthode tanh_impl.

  7. Démarrez le débogueur en sélectionnant Déboguer>Démarrer le débogage ou utilisez le raccourci clavier F5.

    Le débogueur s’arrête quand ce code de point d’arrêt est appelé. Si le point d’arrêt n’est pas atteint, vérifiez que la configuration est définie sur Déboguer et que vous avez enregistré le projet, ce qui ne se fait pas automatiquement lorsque vous démarrez le débogueur.

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

  8. À ce point d’arrêt, vous pouvez exécuter pas à pas le code C++, examiner les variables, etc. Pour plus d’informations sur ces fonctionnalités, consultez Déboguer Python et C++ ensemble.

Autres approches

Vous pouvez créer des extensions Python de différentes façons, comme décrit dans le tableau suivant. Les deux premières lignes, CPython et PyBind11, sont décrites dans cet article.

Approche Vintage Utilisateurs représentatifs
Modules d’extension C/C++ pour CPython 1991 bibliothèque standard
PyBind11 (recommandé pour C++) 2015
Cython (recommandé pour C) 2007 gevent, kivy
HPy 2019
mypyc 2017
ctypes 2003 oscrypto
cffi 2013 cryptography, pypy
SWIG 1996 crfsuite
Boost.Python 2002
cppyy 2017