如何通过pybind11获取Python调用C++函数的代码行号
获取Python调用C++函数的代码行号解决方案
要在pybind11实现的C++日志类中获取Python调用处的文件名和行号,核心是利用Python的栈帧API提取调用上下文信息,具体实现步骤如下:
1. 补充必要头文件
首先添加pybind11相关头文件以及日志处理所需的标准库头:
#include <pybind11/pybind11.h> #include <pybind11/eval.h> #include <string> #include <memory> #include <chrono> #include <iomanip> #include <fstream> namespace py = pybind11;
2. 修改日志类的log方法
在log方法中通过Python的sys._getframe()获取调用栈帧,从中提取文件名和行号,同时处理非Python调用的异常情况:
class PythonLogger { public: PythonLogger(const std::string& filename) : log_file(filename, std::ios::app) { /* 打开日志文件 */ } ~PythonLogger() { if (log_file.is_open()) log_file.close(); } void log(const std::string& msg) { std::string filename = "unknown"; int line_number = 0; try { // 获取上一级栈帧(参数1跳过当前C++函数对应的Python包装帧) py::object frame = py::module_::import("sys").attr("_getframe")(1); filename = frame.attr("f_code").attr("co_filename").cast<std::string>(); line_number = frame.attr("f_lineno").cast<int>(); } catch (const py::error_already_set&) { // 无法获取帧信息时,清除Python错误状态并使用默认值 PyErr_Clear(); } // 生成格式化时间字符串 auto now = std::chrono::system_clock::now(); std::time_t now_time = std::chrono::system_clock::to_time_t(now); std::string time_str = std::ctime(&now_time); if (!time_str.empty() && time_str.back() == '\n') { time_str.pop_back(); } // 格式化日志内容并写入文件 std::string log_entry = "[" + time_str + "] [" + filename + ":" + std::to_string(line_number) + "]: " + msg + "\n"; if (log_file.is_open()) { log_file << log_entry; log_file.flush(); } } private: std::ofstream log_file; // 替换为你的文件类,此处用标准库ofstream示例 }; typedef std::shared_ptr<PythonLogger> PythonLoggerPtr;
3. 修正pybind11模块绑定
确保Python的debug方法正确映射到C++的log方法,同时添加构造函数绑定:
PYBIND11_EMBEDDED_MODULE(PythonLogger, m) { py::class_<PythonLogger, PythonLoggerPtr>(m, "Logger") .def(py::init<const std::string&>()) .def("debug", &PythonLogger::log); }
4. 验证效果
运行嵌入式Python程序后,script.py中第2行调用logger.debug("debug message")会生成符合预期的日志:
[Wed Oct 11 14:30:00 2024] [script.py:2]: debug message
关键说明
sys._getframe(1):获取调用当前函数的上一级栈帧,参数1跳过C++函数对应的Python包装层,直接指向Python代码的调用位置。- 异常处理:如果
log被C++代码直接调用,sys._getframe()会抛出错误,此时捕获异常并清除Python错误状态,避免影响后续执行。
内容的提问来源于stack exchange,提问作者Eliv_
相关产品推荐
相关产品推荐

