C++ feat. Python Collegare, Incorporare, Installare facilmente

C++ feat. Python Collegare, Incorporare, Installare facilmente

Applicazione enterprise C++ per Windows che esegue un modulo Python. Ecco il mio percorso attraverso il processo di questa fusione tecnologica, dalla prima riga di codice alla consegna dell’applicazione.

Immagine generata dall'autore utilizzando strumenti di intelligenza artificiale

Introduzione

La semplicità di Python, l’ampio ecosistema di pacchetti e la comunità di supporto lo rendono una scelta interessante. Al contrario, quando si affrontano interfacce utente complesse, flussi di dati multithreaded e robustezza 24 ore su 24 nelle soluzioni enterprise, il C++ prende il centro della scena. A volte, sorge la necessità di amalgamare questi due mondi, cercando di ottenere il meglio di entrambi.

Spesso, il codice Python richiama metodi C++ per calcoli algoritmici complessi. Tuttavia, mi sono trovato di fronte a uno scenario opposto in cui la mia applicazione di Machine Learning richiedeva urgentemente l’invocazione di un modello personalizzato con codice di inferenza basato su Python. La prospettiva di riscrivere il codice in C++ o adottare un motore di inferenza corrispondente era irrealizzabile. Il mio pensiero iniziale era semplice: “Chiamare Python da C++ dovrebbe essere un gioco da ragazzi”. Ahimè, ho sottovalutato la complessità coinvolta!

Aspettative vs. Realta’

Esistono diversi bridge facilmente ricercabili che facilitano l’interazione tra C++ e Python:

  1. L’interfaccia C++ integrata di Python accessibile tramite gli header e le librerie C++ (Python.h, Python.lib) forniti con il pacchetto standard di Python.
  2. Boost.Python, per coloro che sono a proprio agio con la libreria Boost.
  3. Pybind11, la mia preferenza per la sua combinazione di potenza e facilità di integrazione nei progetti.

Ad una prima occhiata, la soluzione sembra essere a portata di mano e l’unica cosa che resta è selezionare il bridge più adatto tra questi tre. Ma diamo un’occhiata più da vicino!

I metodi sopra citati eccellono nel loro scopo principale di consentire la comunicazione Python-C++, sufficiente per prove ed esperimenti preliminari. Tuttavia, quando si mira a costruire una soluzione consegnabile, è necessario pensare al ciclo di vita completo del software. E una delle sfide da risolvere è la distribuzione. Per chiarire, il compito è distribuire i binari C++ insieme ai componenti Python. Anche la condivisione del codice Python stesso può essere difficile, se si tiene conto dell’installazione dell’interprete nel sistema operativo. Inoltre, possono coesistere più versioni di Python e ogni pacchetto aggiuntivo richiede un’installazione adeguata, compresa la risoluzione delle dipendenze. La domanda diventa: quali opzioni sono valide per il nostro caso?

  1. Istruire gli utenti a installare Python e i pacchetti necessari prima di utilizzare l’applicazione C++.
  2. Incorporare l’installer di Python all’interno dell’installer C++ per risolvere automaticamente i problemi correlati, inclusa un’altra installazione di Python sul sistema dell’utente.

Nessuna delle due sembra ottimale. Di conseguenza, mi sono imbarcato in una ricerca per scoprire un approccio più affidabile e stabile, che sono ansioso di condividere con voi.

Python Incorporato

Nella tua ultima visita alla homepage di Python, hai esplorato le opzioni di distribuzione? Tra queste c’è una scelta interessante: il pacchetto incorporabile di Windows. Questa opzione ha suscitato la mia curiosità, presentando una soluzione apparentemente magica al problema della distribuzione. Approfondendo la documentazione ufficiale, ho scoperto i dettagli su questa versione incorporata – sorprendentemente, Python offre una versione incorporata per un’integrazione senza soluzione di continuità in altre applicazioni.

Dopo aver esaminato il contenuto dell’archivio della versione incorporata, sorgono un paio di domande: come possono essere aggiunti moduli aggiuntivi a questa configurazione? E come si può indirizzare l’applicazione C++ per utilizzare questa variante incorporata?

Rispondere alla prima domanda è relativamente semplice: i pacchetti possono essere semplicemente copiati da un’installazione standard di Python.

La seconda domanda ha richiesto una ricerca approfondita, come accennato in precedenza i bridge Python funzionano con l’installazione predefinita. Anche se cambiare le variabili d’ambiente di Python (PYTHONPATH, PYTHONHOME) può essere occasionalmente utile, non è una soluzione definitiva per passare alla configurazione incorporata, in quanto Python potrebbe mettere i suoi file in posizioni diverse del sistema operativo.

Flag Python per C++

Il programma Python più semplice può contenere solo una riga:

print(‘Ciao, mondo!’)

Python protegge i programmatori da numerose routine interne per raggiungere questa apparente semplicità. Quando viene lanciata silenziosamente questa riga, l’interprete gestisce i parametri di installazione, l’inizializzazione, l’importazione dei moduli predefiniti, la compilazione del bytecode, la ricerca delle funzioni e altro ancora. Tuttavia, esiste un modo per intervenire in questi meccanismi segreti. Citando la documentazione ufficiale, “Python ha variabili per la configurazione globale per controllare diverse funzionalità e opzioni”. Esistono come argomenti della riga di comando o come flag e metodi per il codice C++, accessibili attraverso il già citato Python.h. Nel caso presente, è necessario sovrascrivere il percorso di ricerca di Python utilizzando il metodo Py_SetPath(). Altri flag entrano in gioco in alcuni casi insoliti: Py_NoSiteFlag, Py_NoUserSiteDirectory, Py_IgnoreEnvironmentFlag. Questi flag si riferiscono a saltare il caricamento del modulo predefinito site, o a ignorare una directory separata con moduli nella directory home dell’utente. Verificali per gestire alcuni casi specifici!

Guida di riepilogo

  1. Prepara il tuo progetto C++ per iniziare l’integrazione con Python.
  2. Decidi la versione di Python e installala su Windows come installazione predefinita.
  3. Scarica la stessa versione di Python come pacchetto integrato ed estrailo nella directory del tuo progetto.
  4. Scrivi il codice di test Python che richiama tutti i metodi Python necessari per la copia dei moduli successivi e i test.
  5. Avvia il codice di test Python con l’installazione integrata (utilizza i flag della riga di comando per sovrascrivere il percorso di ricerca). Otterrai errori di moduli mancanti se vengono utilizzati moduli non predefiniti. Copia uno per uno i moduli mancanti dall’installazione predefinita all’installazione integrata in modo che il codice di test Python venga completato con successo.
  6. Vai al tuo progetto C++. Ottieni pybind11. Aggiungi progetti che includano un percorso di ricerca ad esso.
  7. Aggiungi percorsi di ricerca agli header e alle librerie dall’installazione predefinita di Python al tuo progetto.
  8. Nel codice C++ utilizza Py_SetPath() per impostare il percorso relativo all’installazione integrata di Python. Utilizza eventuali flag aggiuntivi se necessario.
  9. Se uno dei pacchetti Python si trova nella directory del sito dell’utente, copialo nella cartella del pacchetto integrato e aggiungi un percorso separato ad esso in Py_SetPath().
  10. Nel codice C++, utilizza l’API pybind11 per accedere ai metodi Python. Se il programma C++ si blocca durante la chiamata del metodo Python, molto probabilmente alcuni moduli Python non possono essere importati – utilizza il codice di test Python dal passaggio [5] per risolvere il problema.
  11. Distribuisci i binari C++ con la cartella Python integrata come componente integrale. Potrebbe essere necessario copiare alcune DLL nella posizione binaria, come python3.dll e redistribuibili di Windows.

Parte finale

Le applicazioni C++ a scopo di produzione possono includere Python integrato come parte di esse in modo che gli utenti nemmeno ne siano a conoscenza. E questo è un grande risultato, poiché a tutti piace svolgere i nostri compiti con l’aiuto di applicazioni semplici ma potenti anziché impazzire per la loro installazione!

Condivido il mio progetto demo, che incorpora tutte le informazioni dell’articolo per un avvio veloce.