You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

C++14编写Python扩展:如何将wstring传递给Python回调函数?

好的,咱们来一步步拆解这个问题——你的思路方向是对的,但有几个关键细节需要调整和明确,确保跨语言的字符串传递不出问题:

一、回调函数的合理定义

首先,Python端的回调函数应该接收**str类型**(也就是Python 3的原生Unicode字符串),而不是原始字节串。这样用户在Python里处理非英文字符时不需要额外解码,体验更顺畅。比如Python端的回调可以写成:

def my_callback(text: str):
    print(f"Received text: {text}")

在C++层,你需要把转换后的UTF-8字符串包装成Python的str对象再传递给回调,而不是直接传字节串。

二、wstring转UTF-8的关键细节

C++的wstring底层是wchar_t,而wchar_t的宽度是平台相关的:

  • Windows下是16位(对应UTF-16编码)
  • Unix-like系统(Linux/macOS)下是32位(对应UTF-32编码)

所以转换逻辑需要区分平台,才能正确把wstring转成标准的UTF-8字符串。这里用C++14支持的std::wstring_convert来实现:

#include <codecvt>
#include <string>
#include <stdexcept>

#ifdef _WIN32
// Windows下:UTF-16(wstring)转UTF-8
std::string wstring_to_utf8(const std::wstring& wstr) {
    std::wstring_convert<std::codecvt_utf8_utf16<wchar_t>> converter;
    try {
        return converter.to_bytes(wstr);
    } catch (const std::range_error& e) {
        throw std::runtime_error("Invalid UTF-16 sequence in wstring");
    }
}
#else
// Unix-like下:UTF-32(wstring)转UTF-8
std::string wstring_to_utf8(const std::wstring& wstr) {
    std::wstring_convert<std::codecvt_utf8<wchar_t>> converter;
    try {
        return converter.to_bytes(wstr);
    } catch (const std::range_error& e) {
        throw std::runtime_error("Invalid UTF-32 sequence in wstring");
    }
}
#endif

三、C++层调用回调的完整流程

  1. 保存用户传入的回调:模块初始化时要妥善保存Python回调对象,注意管理引用计数:
#include <Python.h>

static PyObject* g_user_callback = nullptr;

// 模块提供的设置回调的API
static PyObject* set_callback(PyObject* self, PyObject* args) {
    PyObject* callback;
    if (!PyArg_ParseTuple(args, "O", &callback)) {
        return nullptr;
    }
    if (!PyCallable_Check(callback)) {
        PyErr_SetString(PyExc_TypeError, "Argument must be a callable function");
        return nullptr;
    }
    // 释放旧回调(如果存在),增加新回调的引用计数
    Py_XDECREF(g_user_callback);
    Py_INCREF(callback);
    g_user_callback = callback;
    Py_RETURN_NONE;
}
  1. 读取文件并调用回调:读取得到wstring后,转成UTF-8再包装成Pythonstr,然后调用回调:
// 读取文本文件为wstring的示例实现(你可以替换成自己的读取逻辑)
std::wstring read_file_as_wstring(const char* filename) {
    std::wifstream file(filename);
    if (!file.is_open()) {
        throw std::runtime_error("Failed to open file");
    }
    return std::wstring(std::istreambuf_iterator<wchar_t>(file), {});
}

// 模块提供的处理文件并触发回调的API
static PyObject* process_text_file(PyObject* self, PyObject* args) {
    const char* filename;
    if (!PyArg_ParseTuple(args, "s", &filename)) {
        return nullptr;
    }

    try {
        std::wstring file_content = read_file_as_wstring(filename);
        std::string utf8_content = wstring_to_utf8(file_content);

        // 检查回调是否已设置
        if (!g_user_callback) {
            PyErr_SetString(PyExc_RuntimeError, "Callback not set. Call set_callback first.");
            return nullptr;
        }

        // 将UTF-8字符串转为Python str对象
        PyObject* py_text = PyUnicode_FromString(utf8_content.c_str());
        if (!py_text) {
            return nullptr;
        }

        // 调用回调函数
        PyObject* result = PyObject_CallObject(g_user_callback, PyTuple_Pack(1, py_text));
        Py_DECREF(py_text);

        if (!result) {
            // 回调执行出错,Python已自动设置异常
            return nullptr;
        }
        Py_DECREF(result);
    } catch (const std::runtime_error& e) {
        PyErr_SetString(PyExc_RuntimeError, e.what());
        return nullptr;
    }

    Py_RETURN_NONE;
}
  1. 模块注册:最后把方法注册到扩展模块中:
static PyMethodDef ExtensionMethods[] = {
    {"set_callback", set_callback, METH_VARARGS, "Set the callback function to receive text"},
    {"process_file", process_text_file, METH_VARARGS, "Read a text file and pass content to the callback"},
    {nullptr, nullptr, 0, nullptr} // 哨兵元素
};

static struct PyModuleDef my_extension_module = {
    PyModuleDef_HEAD_INIT,
    "my_extension", // 模块名称
    "C++ extension for processing text files with Python callback", // 模块文档
    -1,
    ExtensionMethods
};

PyMODINIT_FUNC PyInit_my_extension(void) {
    return PyModule_Create(&my_extension_module);
}

几个核心注意事项

  • 引用计数必须严格管理:操作Python对象时,Py_INCREF/Py_DECREF/Py_XDECREF一个都不能少,否则会导致内存泄漏或Python解释器崩溃。
  • 异常处理要到位:不管是文件读取失败还是字符串转换出错,都要把C++异常转为Python异常,让Python端能捕获和处理。
  • 平台兼容性:一定要区分wchar_t的平台差异,否则在跨平台部署时会出现乱码。

内容的提问来源于stack exchange,提问作者NonStatic

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.20 09:21:11