如何通过SWIG实现C与Ruby间char*指针的双向传递?
问题解决:SWIG实现char*指针在C→Ruby→C的引用传递
核心结论
可以实现,关键是避免SWIG默认将char*转换为Ruby字符串,而是让指针以原生内存引用的形式在Ruby端存储,传回C时再恢复为指针。以下是三种可行方案,按推荐程度排序:
方案1:封装结构体传递(推荐,同时兼容缓冲区大小)
直接将指针和大小封装为C结构体,让SWIG自动包装为Ruby对象,既解决指针传递,也覆盖后续需要传递大小的需求:
步骤1:定义C结构体
typedef struct { unsigned char* ptr; size_t size; } LinkBuffer;
步骤2:修改C端生成和接收函数
// 生成链接缓冲区并返回结构体 LinkBuffer link_rb() { LinkBuffer buf = {NULL, 0}; link_create(NULL, 0, &buf.size); if (buf.size != 0) { buf.ptr = (unsigned char*)malloc(buf.size); if (buf.ptr != NULL) buf.size = link_create(buf.ptr, buf.size, NULL); } return buf; } // 接收结构体并创建追踪器 tracer_handle_t tracer_create_rb(LinkBuffer buf) { return inprocesslinktracer_create(buf.ptr, buf.size); }
步骤3:SWIG接口文件声明结构体
typedef struct { unsigned char* ptr; size_t size; } LinkBuffer; // 导出上述两个函数 %include "your_c_file.c"
Ruby端使用
# 第一个线程生成缓冲区 buf = YourModule.link_rb # 将buf传递到第二个线程 # 第二个线程创建追踪器 tracer = YourModule.tracer_create_rb(buf) # 使用完后记得释放内存(需新增C函数free(buf.ptr)) YourModule free_link_buffer(buf.ptr)
方案2:使用SWIG%pointer_class自动封装指针
让SWIG为unsigned char*生成一个指针包装类,Ruby端直接持有该类实例传递:
步骤1:SWIG接口添加指针类声明
%pointer_class(unsigned char, UnsignedCharPtr); %include "your_c_file.c"
步骤2:修改C端函数返回unsigned char*并输出大小
unsigned char* link_rb(size_t *out_size) { size_t in_process_link_size = 0; link_create(NULL, 0, &in_process_link_size); unsigned char* in_process_link = NULL; if (in_process_link_size != 0) { in_process_link = (unsigned char*)malloc(in_process_link_size); if (in_process_link != NULL) in_process_link_size = link_create(in_process_link, in_process_link_size, NULL); } *out_size = in_process_link_size; return in_process_link; } tracer_handle_t tracer_create_rb(unsigned char const* link, size_t link_size) { return inprocesslinktracer_create(link, link_size); }
Ruby端使用
# 第一个线程获取指针和大小 size_ptr = YourModule::SizeT.new ptr = YourModule.link_rb(size_ptr) link_size = size_ptr.value # 传递ptr和link_size到第二个线程 # 第二个线程调用 tracer = YourModule.tracer_create_rb(ptr, link_size)
方案3:自定义SWIG类型映射(手动管理指针地址)
通过类型映射将指针地址转为Ruby数值存储,传回时再恢复为指针,适合极简场景:
步骤1:SWIG接口添加类型映射
// 输出:将unsigned char*转为Ruby整数(内存地址) %typemap(out) unsigned char* { $result = LONG2NUM((long)$1); } // 输入:将Ruby整数转为unsigned char*指针 %typemap(in) unsigned char const* { $1 = (unsigned char const*)NUM2LONG($input); } %include "your_c_file.c"
Ruby端使用
# 第一个线程获取指针地址 ptr_addr = YourModule.link_rb # 传递ptr_addr到第二个线程 # 第二个线程调用 tracer = YourModule.tracer_create_rb(ptr_addr)
关键注意事项
- 内存管理:C端
malloc分配的内存Ruby不会自动释放,必须新增C函数void free_link(unsigned char* ptr) { free(ptr); },在Ruby端使用完指针后主动调用释放。 - 线程安全:确保指针在传递过程中不会被提前释放,第二个线程使用时指针必须有效。
内容的提问来源于stack exchange,提问作者skrystos
相关产品推荐
相关产品推荐

