如何从C代码遍历C++前向迭代器?求实现方案
我正在为一个C库编写C后端,想要让C代码能遍历C前向迭代器的单个元素。原本C++里的遍历代码是:
auto rng = wks.range(XLCellReference("A1"), XLCellReference("Q1")); for (auto& cell : rng) { // 对"cell"进行操作 }
现在我要把这个逻辑导出成C可调用的接口,计划用下面一组函数实现C端遍历:
void *startiteration(void *cpp_obj); void *getnextitem(void *cpp_obj, void *iter); void finishiteration(void *cpp_obj, void *iter);
预期startiteration()返回迭代器指针给C代码,后续调用getnextitem()和finishiteration()时,把这个指针和C对象指针一起传入。但问题在于:用new创建的C对象指针可以在C和C++之间传递,但迭代器是由类方法返回的,我不知道怎么把它转成能传给C的指针,也不知道该怎么释放这类迭代器。
编辑
基于建议实现的代码:
struct myiter { XLCellIterator begin; XLCellIterator end; }; void *xlsx_startiteration(void *handle, int idx) { XLDocument *doc = (XLDocument *) handle; XLWorkbook wb = doc->workbook(); auto wks = wb.worksheet(wb.sheetNames()[idx-1]); XLCellRange cr = wks.range(XLCellReference("A1"), XLCellReference("Q1")); myiter *it = new myiter(); it->begin = std::begin(static_cast<XLCellRange*>(cr)); it->end = std::end(static_cast<XLCellRange*>(cr)); return it; }
解决方案
核心思路是把C迭代器(以及遍历所需的终止迭代器)打包到一个自定义结构体里,通过new分配内存后将结构体指针返回给C代码——因为void*可以在C和C之间安全传递,C代码不需要知道结构体内部细节,只需要把这个指针原封不动传回后续接口即可。
完整实现步骤
定义迭代器包装结构体
在C++代码中定义一个仅内部可见的结构体,用来保存遍历需要的当前迭代器和终止迭代器:// 仅C++代码可见,C端无需知晓内部结构 struct IterWrapper { XLCellIterator current; XLCellIterator end; };这里保存当前迭代器而非起始迭代器,方便每次调用
getnextitem时直接移动并返回元素。修正并实现
startiteration函数
创建包装结构体并初始化迭代器范围,注意原代码里的static_cast<XLCellRange*>(cr)是错误的——cr是对象而非指针,直接调用成员方法即可:extern "C" void *xlsx_startiteration(void *handle, int idx) { XLDocument *doc = static_cast<XLDocument*>(handle); XLWorkbook wb = doc->workbook(); auto wks = wb.worksheet(wb.sheetNames()[idx-1]); XLCellRange cr = wks.range(XLCellReference("A1"), XLCellReference("Q1")); IterWrapper *wrapper = new IterWrapper(); wrapper->current = cr.begin(); wrapper->end = cr.end(); return wrapper; }实现
getnextitem函数
负责移动迭代器并返回当前元素指针,遍历结束时返回NULL:extern "C" void *xlsx_getnextitem(void *cpp_obj, void *iter) { (void)cpp_obj; // 若无需用到原C++对象可忽略 IterWrapper *wrapper = static_cast<IterWrapper*>(iter); if (wrapper->current == wrapper->end) { return NULL; // 遍历完成 } // 返回当前元素的指针,假设迭代器解引用为XLCell& XLCell *cell = &(*wrapper->current); ++wrapper->current; // 移动到下一个元素 return cell; }实现
finishiteration函数
负责释放包装结构体的内存,避免泄漏:extern "C" void xlsx_finishiteration(void *cpp_obj, void *iter) { (void)cpp_obj; IterWrapper *wrapper = static_cast<IterWrapper*>(iter); delete wrapper; }C端调用示例
C代码可以按如下方式使用这组接口:void traverse_cells(void *xl_doc, int sheet_idx) { void *iter = xlsx_startiteration(xl_doc, sheet_idx); if (!iter) return; void *cell; while ((cell = xlsx_getnextitem(xl_doc, iter)) != NULL) { // 调用其他C接口操作单元格,比如获取值 // const char* value = xlsx_get_cell_value(cell); } xlsx_finishiteration(xl_doc, iter); }
关键注意事项
- 迭代器包装结构体必须在C++代码中分配和释放,C代码仅负责传递指针,不能直接修改内部结构。
- 接口函数必须用
extern "C"声明,避免C++名称修饰导致C端无法链接。 - 如果迭代器持有额外资源,需要在
finishiteration中补充清理逻辑;标准风格的前向迭代器直接删除包装结构体即可。
内容的提问来源于stack exchange,提问作者Andreas

