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

在Pybind11中使用C风格函数指针实现回调遇类型转换错误求助

Pybind11中C风格函数指针回调的编译错误解决

问题背景

要在Pybind11中通过C风格函数指针实现回调功能,让C++代码调用Python函数,但编译时遇到类型转换相关错误。

错误信息

..../include/pybind11/detail/../detail/type_caster_base.h:1021:50: error: invalid conversion from 'char* (*)(const char*, const char*)' to 'const void*' [-fpermissive]
 1021 |         return type_caster_generic::src_and_type(src, cast_type, instance_type);
      |                                                  ^~~
      |                                                  |
      |                                                  char* (*)(const char*, const char*)
../include/pybind11/detail/../detail/type_caster_base.h:851:30: note:   initializing argument 1 of 'static std::pair<const void*, const pybind11::detail::type_info*> pybind11::detail::type_caster_generic::src_and_type(const void*, const std::type_info&, const std::type_info*)'
  851 |     src_and_type(const void *src,

相关代码示例

C++核心逻辑代码(cplusplus.cc)

typedef char* (*callback_type)(const char* name, const Server* server);
class Server{
    callback_type server_callback;
};

char *cplusplus_readfile(const char* name,Server* server) {
    char *buffer = server->server_callback(name,server);
    return buffer;
}

Python测试代码(server_test.py)

import server_wrapper as s
BUFFER_LENGTH = 1024 
class Test:
    def server_read(self,name,server):
        print("Python function called with name:", name)
        buffer = b''
        # 自定义逻辑
        return buffer
    def test_func(self):
        server= s.Server()
        server.register_server_callback(self.server_read)

Pybind11包装代码(server_wrapper.cc)

#include <pybind11/pybind11.h>
#include <pybind11/embed.h>
#include <pybind11/stl.h>
#include <pybind11/functional.h>
#include <cstring>
#include "Server.h" 
namespace py = pybind11;

PYBIND11_MODULE(server_wrapper, m) {

    py::class_<Server>(m, "Server")
        .def(py::init<>())
        .def_readwrite("server_callback", &Server::server_callback)
        .def("register_server_callback", [](py::object &obj,py::function py_callback){
                callback_type cb=[py_callback](const char * name , const Server* server) -> char*{
                py::object result = py_callback(name,server);
                std::string str_result = py::str(result);
                char * buffer = new char[str_result.length()+1];
                std::strcpy(buffer,str_result.c_str());
                return buffer;

            };
            Server &s = obj.cast<Server>();
            s.server_callback=cb;
        }, "function to set callback");

}

错误根源与修复方案

核心问题

C风格函数指针要求对应函数是无状态的(不能捕获外部变量),但原代码中用于转换为callback_type的lambda捕获了py_callback,违反了C风格函数指针的约束,导致编译器抛出类型转换错误。

修复步骤

  1. 修改Server类,添加Python回调存储
    给Server类新增成员变量,用于存储Python回调函数,避免依赖lambda捕获:

    #include <pybind11/pybind11.h>
    namespace py = pybind11;
    
    typedef char* (*callback_type)(const char* name, const Server* server);
    class Server{
    public:
        callback_type server_callback;
        py::function py_callback; // 存储Python回调
    };
    
  2. 实现静态C风格回调函数
    编写无状态的静态函数,通过Server实例获取存储的Python回调并执行:

    static char* static_callback(const char* name, const Server* server) {
        if (!server || !server->py_callback) {
            return nullptr;
        }
        try {
            // 将const Server*转换为Python可识别的对象
            py::object result = server->py_callback(name, py::cast(server));
            // 兼容bytes和字符串返回值
            std::string str_result;
            if (py::isinstance<py::bytes>(result)) {
                str_result = std::string(py::bytes(result));
            } else {
                str_result = py::str(result);
            }
            // 分配缓冲区并返回
            char* buffer = new char[str_result.size() + 1];
            std::strcpy(buffer, str_result.c_str());
            return buffer;
        } catch (const py::error_already_set& e) {
            PyErr_Print(); // 打印Python异常信息
            return nullptr;
        }
    }
    
  3. 修改Pybind11包装逻辑
    去掉带捕获的lambda,改为设置静态回调并存储Python函数:

    PYBIND11_MODULE(server_wrapper, m) {
        py::class_<Server>(m, "Server")
            .def(py::init<>())
            // 不再暴露server_callback给Python,避免误用
            .def("register_server_callback", [](Server& server, py::function callback) {
                server.py_callback = std::move(callback);
                server.server_callback = &static_callback;
            }, "Set the Python callback for server operations");
    }
    

额外注意事项

  • 不要将server_callback暴露给Python,防止非法修改导致程序崩溃;
  • 添加异常捕获逻辑,避免Python函数抛出异常时导致C++程序崩溃;
  • 注意new分配的char*缓冲区需要在合适时机手动释放,避免内存泄漏;
  • 兼容Python返回的bytes类型,更符合字节缓冲区的使用场景。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.29 04:33:10