从 Python 调用 C++ 中执行 Python 的函数会出现 free() 无效指针错误

问题描述 投票:0回答:1

我使用 Python C API 编写了一个在 C++ 中执行 Python 字符串的函数。 该功能运行完美。当我运行它时,它会执行 Python 字符串。

当我创建 .so 文件,然后使用 cpp 从 Python 端调用相同的函数时,就会出现问题。

它给出了一个磨损错误,

free() invalid pointer
Py_Initialize();
这一行。 当我注释掉该行时,就会发生分段错误。


我的runner.cpp代码是:

#include <vector>
#include <dlfcn.h>
#include <iostream>
#include <sstream>
#include <cstdlib>
#include <cstring>

typedef void (*Py_Initialize_t)();
typedef int (*PyRun_SimpleString_t)(const char *);
typedef void (*Py_Finalize_t)();


std::string execute_command(const char* command) {
    FILE* pipe = popen(command, "r");
    if (!pipe) {
        return "Error executing command";
    }

    std::ostringstream output;
    char buffer[128];
    while (!feof(pipe)) {
        if (fgets(buffer, 128, pipe) != nullptr) {
            output << buffer;
        }
    }

    // Close the pipe and return the output
    pclose(pipe);
    return output.str();
}

std::vector<std::string> split_string(const std::string& input, char delimiter) {
    std::vector<std::string> tokens;
    std::istringstream iss(input);
    std::string token;
    while (std::getline(iss, token, delimiter)) {
        tokens.push_back(token);
    }
    return tokens;
}

void execute_Prog730(std::string program) {
    std::cout << "Running Engine" << std::endl;
    std::string python_version_output = execute_command("python3 -c \"import sys; print(sys.version)\"");
    size_t start_pos = python_version_output.find_first_of("0123456789");
    size_t end_pos = python_version_output.find_first_not_of("0123456789.", start_pos);
    std::string python_version = python_version_output.substr(start_pos, end_pos - start_pos);
    std::vector<std::string> parts = split_string(python_version, '.');

    std::ostringstream lib_name_stream;
    lib_name_stream << "libpython" << parts[0] << '.' << parts[1] << ".so";
    std::string lib_name = lib_name_stream.str();
    std::cout << lib_name << std::endl;
    void* handle = dlopen(lib_name.c_str(), RTLD_LAZY);
    if(!handle && lib_name.back() == 'o') {
        std::string  lib_name_so1 = lib_name + ".1";
        handle = dlopen(lib_name_so1.c_str(), RTLD_LAZY);
    }

    if (!handle) {
        std::cerr << "Failed to load Python library" << std::endl;
        //return 1;
    }

    auto Py_Initialize = (Py_Initialize_t)dlsym(handle, "Py_Initialize");
    auto PyRun_SimpleString = (PyRun_SimpleString_t)dlsym(handle, "PyRun_SimpleString");
    auto Py_Finalize = (Py_Finalize_t)dlsym(handle, "Py_Finalize");

    if (!Py_Initialize || !PyRun_SimpleString || !Py_Finalize) {
        std::cerr << "Failed to resolve Python functions" << std::endl;
        dlclose(handle);
    }
    std::cout << "Debug" << std::endl;
    Py_Initialize();
    std::cout << "Debug" << std::endl;
    PyRun_SimpleString(program.c_str());
    Py_Finalize();
    dlclose(handle);
}

我的runner.h文件是:

#include <iostream>
#include "runner.cpp"

void execute_Prog730(std::string program);

我的main.cpp文件是:

#include <iostream>
#include <cstring>
#include <string.h>

#include <runner.h>

extern "C" void run(const char* program) {
    std::string pr(program);
    std::cout << pr << std::endl;
    execute_Prog730(pr);
}

我用来编译的命令:

g++ -o main.so --shared -fPIC main.cpp -I/home/lakshit/Desktop/temp/include

最后我的main.py文件是:

import ctypes

module = ctypes.CDLL('/home/lakshit/Desktop/temp/main.so')

func = module.run
func.argtypes = [ctypes.c_char_p]

func(b"print('hello')")

注意: 我使用的是ubuntu 22.04。部分版本需要添加-ldl命令才能成功编译.so文件。

python c++ python-c-api
1个回答
1
投票

总结评论讨论:

  1. 由于 Python 已经加载,所以你不应该调用
    Py_Initialize
    Py_Finalize
  2. 由于Python DLL文件已经加载,您需要使用
    RTLD_NOLOAD
  3. 加载它
  4. 在C++端调用Python之前,需要获取GIL

以下代码解决了我系统上的问题。

#include <vector>
#include <dlfcn.h>
#include <iostream>
#include <sstream>
#include <cstdlib>
#include <cstring>

namespace {

    enum PyGILState_STATE
    {
        locked, unlocked
    };

    using PyRun_SimpleString_t = int (*)(const char *);
    using PyGILState_Ensure_t = PyGILState_STATE (*)(void);
    using PyGILState_Release_t = void (*)(PyGILState_STATE);
}

std::string execute_command(const char* command) {
    FILE* pipe = popen(command, "r");
    if (!pipe) {
        return "Error executing command";
    }

    std::ostringstream output;
    char buffer[128];
    while (!feof(pipe)) {
        if (fgets(buffer, 128, pipe) != nullptr) {
            output << buffer;
        }
    }

    // Close the pipe and return the output
    pclose(pipe);
    return output.str();
}

std::vector<std::string> split_string(const std::string& input, char delimiter) {
    std::vector<std::string> tokens;
    std::istringstream iss(input);
    std::string token;
    while (std::getline(iss, token, delimiter)) {
        tokens.push_back(token);
    }
    return tokens;
}

void execute_Prog730(std::string program) {
    std::cout << "Running Engine" << std::endl;
    std::string python_version_output = execute_command("python3 -c \"import sys; print(sys.version)\"");
    size_t start_pos = python_version_output.find_first_of("0123456789");
    size_t end_pos = python_version_output.find_first_not_of("0123456789.", start_pos);
    std::string python_version = python_version_output.substr(start_pos, end_pos - start_pos);
    std::vector<std::string> parts = split_string(python_version, '.');

    std::ostringstream lib_name_stream;
    lib_name_stream << "libpython" << parts[0] << '.' << parts[1] << ".so";
    std::string lib_name = lib_name_stream.str();
    std::cout << lib_name << std::endl;
    void* handle = dlopen(lib_name.c_str(), RTLD_NOLOAD); // Use the already-loaded Python interpreter
    if(!handle && lib_name.back() == 'o') {
        std::string  lib_name_so1 = lib_name + ".1";
        handle = dlopen(lib_name_so1.c_str(), RTLD_NOLOAD);
    }

    if (!handle) {
        std::cerr << "Failed to load Python library" << std::endl;
        //return 1;
    }

    auto PyRun_SimpleString = (PyRun_SimpleString_t)dlsym(handle, "PyRun_SimpleString");
    auto PyPyGILState_Ensure = (PyGILState_Ensure_t)dlsym(handle, "PyGILState_Ensure");
    auto PyGILState_Release = (PyGILState_Release_t)dlsym(handle, "PyGILState_Release");

    if (!PyRun_SimpleString || !PyPyGILState_Ensure || !PyGILState_Release) {
        std::cerr << "Failed to resolve Python functions" << std::endl;
        if (handle)
        {
            dlclose(handle);
        }
    }
    std::cout << "Debug" << std::endl;
    auto state = PyPyGILState_Ensure(); // Acquire GIL
    PyRun_SimpleString(program.c_str());
    PyGILState_Release(state); // Release GIL
    if (handle)
    { dlclose(handle); }
}
print('hello')
Running Engine
libpython3.10.so
Failed to load Python library
Debug
hello

发生

Failed to load Python library
是因为主Python可执行文件不加载
libpython3
,而是静态链接它,因此这些函数是从可执行文件加载的,而不是共享库。如果另一个版本的Python动态链接
libpython3
那么它就会成功。

© www.soinside.com 2019 - 2024. All rights reserved.