Compartilhar via


Criar uma extensão C++ para Python no Visual Studio

Neste artigo, você criará um módulo de extensão C++ para CPython para calcular uma tangente hiperbólica e chamá-la do código Python. A rotina é implementada primeiro no Python para demonstrar o ganho relativo de desempenho da implementação da mesma rotina no C++.

Módulos de código escritos em C++ (ou C) geralmente são usados para estender os recursos de um interpretador do Python. Há três tipos principais de módulos de extensão:

  • Módulos de acelerador: habilitar o desempenho acelerado. Como o Python é uma linguagem interpretada, você pode escrever um módulo de acelerador no C++ para obter um desempenho mais alto.
  • Módulos de wrapper: exponha interfaces C/C++ existentes ao código Python ou exponha uma API mais semelhante a Python que é fácil de usar do Python.
  • Módulos de acesso de sistema de baixo nível: crie módulos de acesso do sistema para alcançar recursos de nível inferior do CPython runtime, do sistema operacional ou do hardware subjacente.

Este artigo demonstra duas maneiras de disponibilizar um módulo de extensão C++ para Python:

  • Use as extensões padrão CPython , conforme descrito na documentação do Python.
  • Use pyBind11, que recomendamos para C++11 devido à sua simplicidade. Para garantir a compatibilidade, verifique se você está trabalhando com uma das versões mais recentes do Python.

O exemplo concluído para este passo a passo está disponível no GitHub em python-samples-vs-cpp-extension.

Pré-requisitos

  • Visual Studio 2017 ou posterior, com a carga de trabalho de desenvolvimento do Python instalada. A carga de trabalho inclui as ferramentas de desenvolvimento nativas do Python, que adicionam a carga de trabalho do C++ e os conjuntos de ferramentas necessários para extensões nativas.

    Captura de tela de uma lista de opções de desenvolvimento do Python, destacando a opção ferramentas de desenvolvimento nativas do Python.

    Para obter mais informações sobre as opções de instalação, consulte Instalar o suporte do Python para Visual Studio.

    Observação

    Quando você instala a carga de trabalho de ciência de dados e aplicativos analíticos , o Python e a opção de ferramentas de desenvolvimento nativas do Python são instalados por padrão.

  • Se você instalar o Python separadamente, selecione Baixar símbolos de depuração em Opções Avançadas no instalador do Python. Essa opção é necessária para que você use a depuração de modo misto entre o código Python e o código nativo.

Criar o aplicativo do Python

Siga estas etapas para criar o aplicativo Python.

  1. Crie um novo projeto python no Visual Studio selecionando Arquivo>Novo>Projeto.

  2. Na caixa de diálogo Criar um novo projeto , pesquise python. Selecione o modelo de Aplicativo Python e selecione Avançar.

  3. Insira um nome de projeto e local e selecione Criar.

    O Visual Studio cria o novo projeto. O projeto é aberto no Gerenciador de Soluções e o arquivo de projeto (.py) é aberto no editor de código.

  4. No arquivo .py , cole o código a seguir. Para experimentar alguns dos recursos de edição do Python, tente inserir o código manualmente.

    Esse código calcula uma tangente hiperbólica sem usar a biblioteca matemática e é o que você acelera mais tarde com extensões nativas do Python.

    Dica

    Escreva seu código em Python puro antes de reescrevê-lo no C++. Dessa forma, você pode verificar com mais facilidade para garantir que seu código Python nativo esteja correto.

    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. Execute o programa selecionando Depurar>Iniciar sem Depuração ou selecione o atalho de teclado Ctrl+F5.

    Uma janela de comando é aberta para mostrar a saída do programa.

  6. Na saída, observe a quantidade de tempo relatada para o processo de benchmark.

    Para este tutorial, o processo de benchmark deve levar aproximadamente 2 segundos.

  7. Conforme necessário, ajuste o valor da COUNT variável no código para permitir que o parâmetro de comparação seja concluído em cerca de 2 segundos em seu computador.

  8. Execute o programa novamente e confirme se o valor modificado COUNT produz o parâmetro de comparação em cerca de 2 segundos.

Dica

Ao executar benchmarks, use sempre a opção Debug>Iniciar sem Depuração. Esse método ajuda a evitar a sobrecarga que pode incorrer quando você executa o código no depurador do Visual Studio.

Criar os principais projetos do C++

Siga estas etapas para criar dois projetos C++idênticos, superfastcode e superfastcode2. Posteriormente, você usará uma abordagem diferente em cada projeto para expor o código C++ ao Python.

  1. No Gerenciador de Soluções, clique com o botão direito do mouse no nome da solução e selecione Adicionar>Novo Projeto.

    Uma solução do Visual Studio pode conter projetos python e C++, que é uma das vantagens de usar o Visual Studio para desenvolvimento do Python.

  2. Na caixa de diálogo Adicionar um novo projeto , defina o filtro de idioma como C++e insira vazio na caixa Pesquisar .

  3. Na lista de resultados do modelo de projeto, selecione Projeto vazio e selecione Avançar.

  4. Na caixa de diálogo Configurar seu novo projeto , insira o nome do Projeto:

    • Para o primeiro projeto, insira o nome superfastcode.
    • Para o segundo projeto, insira o nome superfastcode2.
  5. Selecione Criar.

Repita essas etapas e crie dois projetos.

Dica

Uma abordagem alternativa está disponível quando você tem as ferramentas de desenvolvimento nativas do Python instaladas no Visual Studio. Você pode começar com o modelo do Módulo de Extensão do Python , que pré-conclui muitas das etapas descritas neste artigo.

Para o passo a passo neste artigo, começar com um projeto vazio ajuda a demonstrar como criar o módulo de extensão passo a passo. Depois de entender o processo, você pode usar o modelo alternativo para economizar tempo ao escrever suas próprias extensões.

Adicionar arquivo C++ ao projeto

Em seguida, adicione um arquivo C++ a cada projeto.

  1. No Gerenciador de Soluções, expanda o projeto, clique com o botão direito do mouse no nó Arquivos de Origem e selecione Adicionar>Novo Item.

  2. Na lista de modelos de arquivo, selecione Arquivo C++ (.cpp).

  3. Insira o Nome do arquivo como module.cpp e selecione Adicionar.

    Importante

    Verifique se o nome do arquivo inclui a extensão .cpp . O Visual Studio procura um arquivo com a extensão .cpp para habilitar a exibição das páginas de propriedades do projeto C++.

  4. Na barra de ferramentas, expanda o menu suspenso Configuração e selecione o tipo de configuração de destino:

    Captura de tela que mostra como definir o tipo de configuração de destino para o projeto C++ no Visual Studio.

    • Para um runtime do Python de 64 bits, ative a configuração x64 .
    • Para um runtime do Python de 32 bits, ative a configuração do Win32 .

Repita essas etapas para ambos os projetos.

Configurar propriedades do projeto

Antes de adicionar código aos novos arquivos C++, configure as propriedades para cada projeto de módulo C++ e teste as configurações para verificar se tudo está funcionando.

Você precisa definir as propriedades do projeto para as configurações de build de depuração e de lançamento de cada módulo.

  1. No Gerenciador de Soluções, clique com o botão direito do mouse no projeto do módulo C++ (superfastcode ou superfastcode2) e selecione Propriedades.

  2. Configure as propriedades para o build de debug do módulo e configure as mesmas propriedades para o build de release:

    Na parte superior da caixa de diálogo Páginas de Propriedades do projeto, configure as seguintes opções de configuração de arquivo:

    Captura de tela que mostra como definir as opções de build e plataforma do projeto para o arquivo C++ no Visual Studio.

    1. Para a Configuração, selecione Depurar ou Liberar. (Você pode ver essas opções com o prefixo Ativo .)

    2. Para a Plataforma, selecione Ativo (x64) ou Ativo (Win32), dependendo da seleção na etapa anterior.

      Observação

      Ao criar seus próprios projetos, você desejará configurar as definições de depuração e liberação separadamente, de acordo com seus requisitos de cenário específicos. Neste exercício, você define as configurações para usar uma compilação de lançamento do CPython. Essa configuração desabilita alguns recursos de depuração do runtime do C++, incluindo declarações. O uso de binários de depuração CPython (python_d.exe) requer configurações diferentes.

    3. Defina outras propriedades do projeto, conforme descrito na tabela a seguir.

      Para alterar um valor de propriedade, insira um valor no campo de propriedade. Para alguns campos, você pode selecionar o valor atual para expandir um menu suspenso de opções ou abrir uma caixa de diálogo para ajudar a definir o valor.

      Depois de atualizar os valores em uma guia, selecione Aplicar antes de alternar para uma guia diferente. Essa ação ajuda a garantir que suas alterações permaneçam.

      Aba e seção Propriedade Valor
      Propriedades de Configuração>Geral nome de destino Especifique o nome do módulo a ser referenciado por Python em from...import instruções, como superfastcode. Use esse mesmo nome no código C++ ao definir o módulo para Python. Para usar o nome do projeto como o nome do módulo, deixe o valor padrão de $<ProjectName>. Para python_d.exe, adicione _d ao final do nome.
      Tipo de Configuração Biblioteca Dinâmica (.dll)
      Propriedades de Configuração>Avançadas Extensão de arquivo de destino .pyd (Módulo de Extensão do Python)
      C/C++>Geral Diretórios de inclusão adicionais Adicione a pasta de inclusão do Python conforme apropriado para sua instalação (por exemplo, c:\Python36\include).
      C/C++>Pré-processador Definições de pré-processador Se estiver presente, altere o valor _DEBUG para NDEBUG para corresponder à versão sem depuração do CPython. Quando você usar python_d.exe, deixe esse valor inalterado.
      C/C++>Geração de código Biblioteca de runtime DLL de vários threads (/MD) para corresponder à versão sem depuração do CPython. Quando você usar python_d.exe, deixe esse valor como DLL de depuração multi-threaded (/MDd).
      Verificações básicas de runtime Padrão
      Linker>Geral Diretórios de biblioteca adicionais Adicione a pasta libs do Python que contém arquivos .lib , conforme apropriado para sua instalação (por exemplo, c:\Python36\libs). Certifique-se de apontar para a pasta libs que contém arquivos .lib e não a pasta Lib que contém arquivos .py .

      Importante

      Se a guia C/C++ não for exibida como uma opção para as propriedades do projeto, o projeto não conterá arquivos de código que o Visual Studio identifique como arquivos de origem C/C++. Essa condição poderá ocorrer se você criar um arquivo de origem sem uma extensão de arquivo .c ou .cpp .

      Se você inseriu acidentalmente module.coo em vez de module.cpp quando criou o arquivo C++, o Visual Studio criará o arquivo, mas não definirá o tipo de arquivo como compilador C/C+. Esse tipo de arquivo é necessário para ativar a presença da guia de propriedades C/C++ na caixa de diálogo propriedades do projeto. A identificação incorreta permanece mesmo se você renomear o arquivo de código com uma extensão de arquivo .cpp .

      Para definir o tipo de arquivo de código corretamente, no Gerenciador de Soluções, clique com o botão direito do mouse no arquivo de código e selecione Propriedades. Para o Tipo de Item, selecione o compilador C/C++.

    4. Depois de atualizar todas as propriedades, selecione OK.

    Repita as etapas para a outra configuração de build.

  3. Teste sua configuração atual. Repita as etapas a seguir para as compilações de depuração e de liberação de ambos os projetos C++.

    1. Na barra de ferramentas do Visual Studio, defina a configuração de build como Depuração ou Release:

      Captura de tela que mostra como definir a configuração de build para o projeto C++ no Visual Studio.

    2. No Gerenciador de Soluções, clique com o botão direito do mouse no projeto C++ e selecione Compilar.

      Os arquivos .pyd estão na pasta da solução , em Depuração e Versão, e não na própria pasta de projeto do C++.

Adicionar configuração de código e de teste

Agora você está preparado para adicionar código aos seus arquivos C++ e testar a compilação de release.

  1. Para o projeto C++ de superfastcode , abra o arquivo module.cpp no editor de código.

  2. No arquivo module.cpp , cole o seguinte código:

    #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. Salve suas alterações.

  4. Crie a configuração de versão do projeto C++ para confirmar se o código está correto.

Repita as etapas para adicionar código ao arquivo C++ para o projeto superfastcode2 e teste o build de release.

Converter projetos do C++ em extensões do Python

Para tornar a DLL do C++ uma extensão para Python, primeiro modifique os métodos exportados para interagir com os tipos do Python. Em seguida, adicione uma função para exportar o módulo, juntamente com definições para os métodos do módulo.

As seções a seguir demonstram como criar as extensões usando as extensões CPython e PyBind11. O projeto superfasctcode usa as extensões CPython e o projeto superfasctcode2 implementa PyBind11.

Usar extensões do CPython

Para obter mais informações sobre o código apresentado nesta seção, consulte o Manual de Referência da API do Python/C, especialmente a página Objetos do Módulo . Ao examinar o conteúdo de referência, certifique-se de selecionar sua versão do Python na lista suspensa no canto superior direito.

  1. Para o projeto C++ de superfastcode , abra o arquivo module.cpp no editor de código.

  2. Adicione uma instrução na parte superior do arquivo module.cpp para incluir o arquivo de cabeçalho Python.h :

    #include <Python.h>
    
  3. Substitua o código do tanh_impl método para aceitar e retornar tipos python (ou seja, um 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. No final do arquivo, adicione uma estrutura para definir como apresentar a função C++ tanh_impl ao 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. Adicione outra estrutura para definir como fazer referência ao módulo em seu código Python, especificamente quando você usa a from...import instrução.

    O nome importado neste código deve corresponder ao valor nas Propriedades de Configuração>Geral>Nome de Destino do projeto.

    No exemplo a seguir, o "superfastcode" nome significa que você pode usar a from superfastcode import fast_tanh instrução no Python porque fast_tanh está definida dentro superfastcode_methods. Os nomes de arquivo internos do projeto C++, como module.cpp, são inconseqüentes.

    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. Adicione um método que o Python chama quando ele carrega o módulo. O nome do método deve serPyInit_<module-name>, em que <o nome> do módulo corresponde exatamente à propriedadeNome> das Propriedades > de Configuraçãodo projeto C++. Ou seja, o nome do método corresponde ao nome do arquivo .pyd criado pelo projeto.

    PyMODINIT_FUNC PyInit_superfastcode() {
        return PyModule_Create(&superfastcode_module);
    }
    
  7. Crie o projeto C++ e verifique seu código. Se você encontrar erros, consulte a seção "Solucionar problemas de erros de compilação ".

Usar PyBind11

Se você concluir as etapas na seção anterior do projeto superfastcode, poderá observar que o exercício requer código padrão para criar as estruturas de módulo para extensões C++ CPython. Neste exercício, você descobrirá que pyBind11 simplifica o processo de codificação. Você usa macros em um arquivo de cabeçalho C++ para obter o mesmo resultado, mas com muito menos código. No entanto, etapas extras são necessárias para garantir que o Visual Studio possa localizar as bibliotecas PyBind11 e incluir arquivos. Para obter mais informações sobre o código nesta seção, consulte as noções básicas do PyBind11.

Instalar o PyBind11

A primeira etapa é instalar o PyBind11 na configuração do projeto. Neste exercício, você usará a janela do PowerShell do Desenvolvedor .

  1. Abra a janela Ferramentas>Linha de Comando>PowerShell do Desenvolvedor.

  2. Na janela PowerShell do Developer, instale o PyBind11 usando o comando pip install pybind11 pip ou py -m pip install pybind11.

    O Visual Studio instala o PyBind11 e seus pacotes dependentes.

Adicionar caminhos PyBind11 ao projeto

Após a instalação do PyBind11, você precisa adicionar os caminhos PyBind11 à propriedade Diretórios de Inclusão Adicional para o projeto.

  1. No PowerShell do Desenvolvedor, execute o comando python -m pybind11 --includes ou py -m pybind11 --includes.

    Essa ação imprime uma lista de caminhos PyBind11 que você precisa adicionar às propriedades do projeto.

  2. Realce a lista de caminhos na janela e selecione Copiar (página dupla) na barra de ferramentas da janela.

    Captura de tela que mostra como realçar e copiar a lista de caminhos da janela do PowerShell do Desenvolvedor no Visual Studio.

    A lista dos caminhos concatenados é adicionada à sua área de transferência.

  3. No Gerenciador de Soluções, clique com o botão direito do mouse no projeto superfastcode2 e selecione Propriedades.

  4. Na parte superior da caixa de diálogo Páginas de Propriedades, para o campo Configuração, selecione Release. (Você pode ver essa opção com o prefixo Ativo .)

  5. Na caixa de diálogo, na guia C/C++>Geral , expanda o menu suspenso da propriedade Diretórios de Inclusão Adicional e selecione Editar.

  6. Na caixa de diálogo pop-up, adicione a lista de caminhos copiados:

    Repita estas etapas para cada caminho na lista concatenada copiada da janela do PowerShell do Desenvolvedor :

    1. Selecione Nova Linha (pasta com símbolo de adição) na barra de ferramentas de diálogo pop-up.

      Captura de tela que mostra como adicionar um caminho do PyBind11 à propriedade Diretórios de Inclusão Adicional.

      O Visual Studio adiciona uma linha vazia na parte superior da lista de caminhos e posiciona o cursor de inserção no início.

    2. Cole o caminho PyBind11 na linha vazia.

      Você também pode selecionar Mais opções (...) e usar uma caixa de diálogo do explorador de arquivos pop-up para navegar até o local do caminho.

      Importante

      • Se o caminho contiver o -I prefixo, remova o prefixo do caminho.
      • Para que o Visual Studio reconheça um caminho, o caminho precisa estar em uma linha separada.

      Depois de adicionar um novo caminho, o Visual Studio mostrará o caminho confirmado no campo Valor Avaliado .

  7. Selecione OK para sair da caixa de diálogo pop-up.

  8. Na parte superior da caixa de diálogo Páginas de Propriedades , passe o mouse sobre o valor da propriedade Diretórios de Inclusão Adicional e confirme se os caminhos PyBind11 estão presentes.

  9. Selecione OK para aplicar as alterações de propriedade.

Atualizar o arquivo de module.cpp

A última etapa é adicionar o arquivo de cabeçalho PyBind11 e o código de macro ao arquivo C++ do projeto.

  1. Para o projeto do C++ superfastcode2 , abra o arquivo module.cpp no editor de código.

  2. Adicione uma instrução na parte superior do arquivo module.cpp para incluir o arquivo de cabeçalho pybind11.h :

    #include <pybind11/pybind11.h>
    
  3. No final do arquivo module.cpp , adicione código para a PYBIND11_MODULE macro para definir o ponto de entrada para a função 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. Crie o projeto C++ e verifique seu código. Se você encontrar erros, consulte a próxima seção, solucionar problemas de erros de compilação.

Solucionar problemas de erros de compilação

Revise as seções a seguir em busca de possíveis problemas que possam causar falha na compilação do módulo C++.

Erro: não é possível localizar o arquivo de cabeçalho

O Visual Studio retorna uma mensagem de erro como E1696: não é possível abrir o arquivo de software livre "Python.h" ou C1083: não é possível abrir o arquivo de inclusão: "Python.h": nenhum arquivo ou diretório.

Esse erro indica que o compilador não pode localizar um arquivo de cabeçalho necessário (.h) para seu projeto.

  • Para o projeto superfastcode, verifique se a propriedade do projeto C/C++>General>Additional Include Directories contém o caminho para a pasta include da sua instalação do Python. Examine as etapas em Configurar propriedades do projeto.

  • Para o projeto superfastcode2 , verifique se a mesma propriedade de projeto contém o caminho para a pasta de inclusão para a instalação do PyBind11. Revise as etapas Adicione caminhos PyBind ao projeto.

Para obter mais informações sobre como acessar suas informações de configuração de instalação do Python, consulte a documentação do Python.

Erro: não é possível localizar bibliotecas do Python

O Visual Studio retorna um erro que indica que o compilador não pode localizar os arquivos de biblioteca (DLL) necessários para seu projeto.

  • Para o projeto C++ (superfastcode ou superfastcode2), verifique se a propriedadeDiretórios adicionais de biblioteca do>> contém o caminho para a pasta libs para sua instalação do Python. Examine as etapas em Configurar propriedades do projeto.

Para obter mais informações sobre como acessar suas informações de configuração de instalação do Python, consulte a documentação do Python.

O Visual Studio relata erros de vinculador relacionados à configuração de arquitetura de destino para seu projeto, como x64 ou Win32.

  • Para o projeto C++ (superfastcode ou superfastcode2), altere a configuração de destino para corresponder à instalação do Python. Por exemplo, se a configuração de destino do projeto C++ for Win32, mas a instalação do Python for de 64 bits, altere a configuração de destino do projeto C++ para x64.

Testar o código e comparar os resultados

Agora que você tem as DLLs estruturadas como extensões do Python, você pode fazer referência a elas do projeto Python, importar os módulos e usar seus métodos.

Disponibilizar sua DLL para Python

Você pode disponibilizar sua DLL para Python de várias maneiras. Aqui estão duas opções a serem consideradas:

Se o projeto python e o projeto C++ estiverem na mesma solução, você poderá usar a seguinte abordagem:

  1. No Gerenciador de Soluções, clique com o botão direito do mouse no nó Referências em seu projeto python e selecione Adicionar Referência.

    Certifique-se de fazer essa ação para seu projeto python e não para seu projeto C++.

  2. Na caixa de diálogo Adicionar Referência , expanda a guia Projetos .

  3. Selecione as caixas de seleção para os projetos superfastcode e superfastcode2 e selecione OK.

    Captura de tela que mostra como adicionar uma referência ao projeto de código super rápido no Visual Studio.

Uma abordagem alternativa é instalar o módulo de extensão C++ em seu ambiente python. Esse método disponibiliza o módulo para outros projetos do Python. Para obter mais informações, consulte a documentação do projeto setuptools.

Conclua as seguintes etapas para instalar o módulo de extensão C++ em seu ambiente python:

  1. No Gerenciador de Soluções, clique com o botão direito do mouse no projeto do C++ e selecione Adicionar>Novo Item.

  2. Na lista de modelos de arquivo, selecione Arquivo C++ (.cpp).

  3. Insira o Nome do arquivo como setup.py e selecione Adicionar.

    Insira o nome do arquivo com a extensão Python (.py). O Visual Studio reconhece o arquivo como código Python, apesar do uso do modelo de arquivo C++.

    O Visual Studio abre o novo arquivo no editor de código.

  4. Cole o código a seguir no novo arquivo. Escolha a versão de código que corresponde ao método de extensão:

    • Extensões CPython (projeto 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 (projeto 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. No projeto C++, crie um segundo arquivo chamado pyproject.toml e cole o seguinte código:

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

    O arquivo TOML (.toml) usa o formato de Linguagem Óbvia e Mínima do Tom para arquivos de configuração.

  6. Para criar a extensão, clique com o botão direito do mouse no nome do arquivo pyproject.toml na guia janela de código e selecione Copiar Caminho Completo.

    Captura de tela que mostra como copiar o caminho completo para o arquivo toml do projeto py no Visual Studio.

    Exclua o nome pyproject.toml do caminho antes de usá-lo.

  7. No Gerenciador de Soluções, expanda o nó Ambientes do Python da solução.

  8. Clique com o botão direito do mouse no ambiente ativo do Python (mostrado em negrito) e selecione Gerenciar Pacotes do Python.

    O painel Ambientes do Python é aberto.

    Se o pacote necessário já estiver instalado, você o verá listado neste painel.

    • Antes de continuar, selecione o X ao lado do nome do pacote para desinstalá-lo.

    Captura de tela que mostra como desinstalar um pacote no painel Ambientes do Python.

  9. Na caixa de pesquisa do painel Ambientes do Python , cole o caminho copiado e exclua o nome de arquivo pyproject.toml do final do caminho.

    Captura de tela que mostra como inserir o caminho no painel Ambientes do Python para instalar o módulo de extensão.

  10. Selecione Enter para instalar o módulo no local do caminho copiado.

    Dica

    Se a instalação falhar devido a um erro de permissão, adicione o --user argumento ao final do comando e tente a instalação novamente.

Chame a DLL a partir do Python

Depois de disponibilizar a DLL para o Python, conforme descrito na seção anterior, você estará pronto para chamar as funções superfastcode.fast_tanh e superfastcode2.fast_tanh2 a partir do Python. Em seguida, você pode comparar o desempenho da função com a implementação do Python.

Siga estas etapas para chamar a DLL do módulo de extensão a partir do Python:

  1. Abra o arquivo .py para seu projeto python no editor de código.

  2. No final do arquivo, adicione o seguinte código para chamar os métodos exportados das DLLs e exibir sua saída:

    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. Execute o programa Python selecionando Depurar>Iniciar sem Depuração ou usar o atalho de teclado Ctrl+F5.

    Observação

    Se o comando Iniciar sem Depuração não estiver disponível, no Gerenciador de Soluções, clique com o botão direito do mouse no projeto Python e selecione Definir como Projeto de Inicialização.

    Quando o programa é executado, observe que as rotinas do C++ são executadas aproximadamente 5 a 20 vezes mais rápido do que a implementação do Python.

    Aqui está um exemplo de saída típica do programa:

    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. Tente aumentar a COUNT variável para que as diferenças de tempo sejam mais acentuadas.

    Um build de depuração do módulo C++ também é executado mais lentamente do que um build de release porque o build de depuração é menos otimizado e contém várias verificações de erro. Tente alternar entre as configurações de build para comparação, mas lembre-se de atualizar as propriedades definidas anteriormente para a configuração de versão.

Velocidade e sobrecarga do processo de endereço

Na saída, você pode observar que a extensão PyBind11 não é tão rápida quanto a extensão CPython, embora deva ser mais rápida do que a implementação pura do Python. O principal motivo para a diferença é devido ao uso do sinalizador de METH_O. Esse sinalizador não dá suporte a vários parâmetros, nomes de parâmetros ou argumentos de palavras-chave. O PyBind11 gera um código um pouco mais complexo para fornecer uma interface mais semelhante ao Python aos chamadores. Como o código de teste chama a função 500.000 vezes, os resultados podem ampliar muito a sobrecarga.

Você pode reduzir ainda mais a sobrecarga movendo o for loop para o código python nativo. Essa abordagem envolve o uso do protocolo iterador (ou o tipo PyBind11 py::iterable para o parâmetro de função) para processar cada elemento. Remover as transições repetidas entre Python e C++ é uma maneira eficaz de reduzir o tempo necessário para processar a sequência.

Solucionar problemas de erros de importação

Se você receber uma ImportError mensagem ao tentar importar seu módulo, poderá resolvê-la de uma das seguintes maneiras:

  • Ao criar por meio de uma referência de projeto, verifique se as propriedades do projeto C++ correspondem ao ambiente Python ativado para seu projeto Python. Confirme se os mesmos locais de pasta estão em uso para os arquivos Include (.h) e Library (DLL).

  • Verifique se o arquivo de saída está corretamente nomeado, como superfastcode.pyd. Um nome ou extensão incorreto impede a importação do arquivo necessário.

  • Se você instalar o módulo usando o arquivo setup.py , execute o pip comando no ambiente python ativado para seu projeto python. Ao expandir o ambiente ativo do Python para seu projeto no Gerenciador de Soluções, você deverá ver uma entrada para o projeto C++, como o superfastcode.

Depurar código C++

O Visual Studio dá suporte à depuração de código Python e C++ juntos. As etapas a seguir demonstram o processo de depuração para o projeto C++ de superfastcode , mas o processo é o mesmo para o projeto superfastcode2 .

  1. No Gerenciador de Soluções, clique com o botão direito do mouse no projeto Python e selecione Propriedades.

  2. No painel Propriedades , selecione a guia Depurar e, em seguida, selecione a opção Habilitar>depuração de código nativo .

    Dica

    Quando você habilita a depuração de código nativo, a janela de saída do Python pode ser fechada imediatamente após a conclusão do programa, sem pausar e mostrar a mensagem Pressione qualquer tecla para continuar. Para forçar a pausa e o prompt depois de habilitar a depuração de código nativo, adicione o argumento -i ao campo Executar>Argumentos do Interpretador na guia Depurar. Esse argumento coloca o interpretador Python no modo interativo após a execução do código. O programa aguarda que você selecione Ctrl+Z+Enter para fechar a janela. Uma abordagem alternativa é adicionar as instruções import os e os.system("pause") ao final do seu programa Python. Esse código duplica o prompt de pausa original.

  3. Selecione Salvar Arquivo> (ou Ctrl+) para salvar as alterações de propriedade.

  4. Na barra de ferramentas do Visual Studio, defina a configuração de build como Depurar.

  5. Como o código geralmente leva mais tempo para ser executado no depurador, talvez você queira alterar a COUNT variável em seu projeto python .py arquivo para um valor cerca de cinco vezes menor que o valor padrão. Por exemplo, altere-o de 5000000 para 100000.

  6. No código C++, defina um ponto de interrupção na primeira linha do tanh_impl método.

  7. Inicie o depurador selecionando Depurar>Iniciar Depuração ou use o atalho de teclado F5.

    O depurador é interrompido quando o código do ponto de interrupção é chamado. Se o ponto de interrupção não for atingido, verifique se a configuração está definida como Depurar e se você salvou o projeto, o que não acontece automaticamente quando você inicia o depurador.

    Captura de tela do código C++ que contém um ponto de interrupção no Visual Studio.

  8. No ponto de interrupção, você pode avançar pelo código C++, examinar as variáveis e assim por diante. Para obter mais informações sobre esses recursos, consulte Depurar Python e C++ juntos.

Abordagens alternativas

Você pode criar extensões do Python de várias maneiras, conforme descrito na tabela a seguir. As duas primeiras linhas, CPython e PyBind11, são discutidas neste artigo.

Abordagem Vindima Usuários representativos
Módulos de extensão C/C++ para CPython 1991 Biblioteca Padrão
PyBind11 (recomendado para C++) 2015
Cython (recomendado para C) 2007 gevent, kivy
HPy 2019
mypyc 2017
ctypes 2003 oscrypto
cffi 2013 criptografia, pypy
GOLE 1996 crfsuite
Boost.Python 2002
cppyy 2017