我编写了一个在 c++ 中使用 python c api 执行 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 文件
总结评论讨论
Py_Initialize
和 Py_Finalize
RTLD_NOLOAD
以下代码解决了我系统上的问题。
#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 already loaded python
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;
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可执行文件不加载libpython
,而是静态链接它,所以这些函数是从可执行文件加载的,而不是共享库,如果另一个版本的Python动态链接libpython
那么一定会成功的。