Call Python Function From C++ While Using Virtual Environment

Call Python Function From C++ While Using Virtual Environment

Integrating Python functionality into C++ applications can be a powerful way to leverage the strengths of both languages. A Python virtual environment, often abbreviated as venv, is a self-contained directory tree that contains installation for a specific version of Python, as well as additional packages. The purpose of creating a virtual environment is to isolate the dependencies of a project, ensuring that it can run independently of the system-wide Python installation and the packages installed there. This tutorial explains how to call Python function from C++ while using virtual environment.

The following code snippet begins by getting the path of the current C++ script and setting up a Python configuration for an isolated environment. The virtual environment's Python executable path (<app_path>/venv/bin/python) is then determined and configured. After initializing the Python interpreter with the specified settings, the script's directory is added to the Python sys.path.

The Python module test (test.py script) is imported, and the sum function is retrieved. A Python list with two float values is created, and the Python function is called with this list as an argument. The result is then converted to a double, and the output is displayed.

project/main.cpp

#include <Python.h>
#include <filesystem>
#include <iostream>

int main(int argc, char *argv[])
{
    std::filesystem::path script(argv[0]);

    PyConfig config;
    PyConfig_InitIsolatedConfig(&config);

    auto fullPath = std::filesystem::current_path() / script.parent_path();
    auto venv_executable = (fullPath / "venv/bin/python").wstring();
    PyConfig_SetString(&config, &config.executable, venv_executable.c_str());

    Py_InitializeFromConfig(&config);
    PyConfig_Clear(&config);

    PyObject * sysPath = PySys_GetObject("path");
    PyList_Insert(sysPath, 0, PyUnicode_FromString(script.parent_path().c_str()));

    PyObject * pModule = PyImport_ImportModule("test");
    PyObject * pFunc = PyObject_GetAttrString(pModule, "sum");

    PyObject * list = PyList_New(2);
    PyList_SetItem(list, 0, PyFloat_FromDouble(2.0));
    PyList_SetItem(list, 1, PyFloat_FromDouble(5.0));

    double result = PyFloat_AsDouble(PyObject_CallFunction(pFunc, "O", list));
    std::cout << result << std::endl;

    Py_Finalize();

    return 0;
}

Within the project directory, create a CMakeLists.txt file:

project/CMakeLists.txt

cmake_minimum_required(VERSION 3.27)
project(myapp)

set(CMAKE_CXX_STANDARD 17)

find_package(Python3 COMPONENTS Development REQUIRED)

add_executable(${PROJECT_NAME} main.cpp)

target_link_libraries(${PROJECT_NAME} Python3::Python)

In the project directory, run the following CMake command to generate the build scripts and configure the project:

cmake -S . -B build

Execute the given CMake command to build the project:

cmake --build build

Go to build directory and create Python virtual environment:

cd build && python -m venv venv

Activate virtual environment by using the following command:

venv\Scripts\activate.bat
source venv/bin/activate

When a virtual environment has been activated, install NumPy package:

pip install numpy

Inside the build directory, create a Python script named test.py for calculating the sum of the elements in the input array.

build/test.py

import numpy as np


def sum(arr):
    return np.sum(arr)

Finally, you can run the program as follows:

./build/myapp

Output:

7

Leave a Comment

Cancel reply

Your email address will not be published.