如何让C++拼接多级别日志的循环逻辑更通用,有没有更优雅实现?
通用Concatenate实现方案
这里提供两种远优于枚举+switch的C++原生实现方案,均无冗余代码、符合开闭原则:
方案1:成员指针方案(兼容C++11及以上,零运行时开销)
因为MyItem中三类消息存储的成员类型完全相同,都是std::vector<std::string>,可以直接将指向类成员的指针作为入参传入通用函数,编译期就确定要读取的成员,完全不需要运行时分支判断:
// 通用拼接函数,入参为MyItem对应消息成员的指针 std::string Concatenate(std::vector<std::string> MyItem::* msg_member) { std::string str; for (const auto& item : items) { for (const auto& msg : item.*msg_member) { str += msg; str += " "; } } return str; }
调用方式极其简洁:
// 拼接所有错误 std::string all_errors = Concatenate(&MyItem::errors); // 拼接所有警告 std::string all_warnings = Concatenate(&MyItem::warnings); // 拼接所有信息 std::string all_infos = Concatenate(&MyItem::infos);
如果需要兼容原有接口,只要加一层薄封装即可,完全不用改上层调用逻辑:
std::string GetErrors() { return Concatenate(&MyItem::errors); } std::string GetWarnings() { return Concatenate(&MyItem::warnings); } std::string GetInfos() { return Concatenate(&MyItem::infos); }
方案2:C++17 提取器模板方案(扩展性更强)
如果后续可能有自定义过滤、拼接规则的需求,可以用泛型模板实现更高自由度的通用拼接:
template <typename MsgExtractor> std::string Concatenate(MsgExtractor&& extractor) { std::string str; for (const auto& item : items) { for (const auto& msg : extractor(item)) { str += msg; str += " "; } } return str; }
除了基础的全量拼接外,还支持自定义提取规则,不需要修改通用函数本身:
// 基础用法:全量拼接警告 std::string all_warnings = Concatenate([](const MyItem& item) { return item.warnings; }); // 扩展用法:只拼接长度大于5的错误信息 std::string long_errors = Concatenate([](const MyItem& item) { std::vector<std::string> filtered; for (const auto& e : item.errors) { if (e.size() > 5) filtered.push_back(e); } return filtered; });
和枚举+switch方案的对比优势
- 无运行时分支开销,成员选择逻辑完全在编译期完成
- 新增消息类型时不需要修改通用Concatenate的内部实现,只需要传入新的成员指针/提取器即可,符合开闭原则
- 没有冗余的switch分支代码,可维护性更高
内容的提问来源于stack exchange,提问作者LarsA
相关产品推荐
相关产品推荐

