C++可变参数测试输出宏线程安全及实现问题咨询
C++可变参数调试输出宏相关问题
我编写了一组专用于测试输出场景的C++可变参数输出宏,输出格式为:函数名@文件名(首尾字符) 行号 变量名=值,示例如下:
L(i,b); // 输出示例:{evenRCpower@ih2943: i=36 b=1 @} L(hex,pd,dec,check.back(),even,"e.g."); // 输出示例:{way@ih3012: pd=babbbaba check.back()=15216868001 even=1 e.g. @}
目前该宏可在g++ C17实验版本(g1z)、C++17标准环境下正常运行,仅存在少量使用限制:逗号、输出格式指定符的使用受限,可通过增强文本分析逻辑放宽限制。现有以下技术疑问:
- 核心问题:如何实现该宏的完全线程安全?
- 是否存在无需文本分析的实现方案?例如可避免使用
#__VA_ARGS__、直接拆分获取每个传入参数名称的方案? - 如何准确区分传入的普通参数与输出流操作符(该功能为必要需求)?
- 若要适配其他更新版本的C标准与g编译器,需要对现有代码做哪些修改?
- 即便通过本地定义的输出流将单行完整内容先写入本地字符串,也无法完全解决并行场景下输出内容混杂的问题,该现象的成因是什么?哪个C++版本会从标准层面解决该问题?
下方给出基于std::cout实现的简化基础版本代码,该版本单线程运行正常,但多线程并行场景下会出现输出错乱问题:
#define WO_IS 1 #define L(...) locate(__FILE__,__LINE__,__func__,"\n{",": "," @}\n",#__VA_ARGS__,##__VA_ARGS__) string argTExcludes = " hex dec std::hex std::dec ", funcExcludes = " setprecision "; string argT(const string sarg,int &nextpos) { // 未实现精确文本分析!仅支持简单字符串、字符类型参数,传入带逗号的参数(如f(x,y,"A,",','))时会出现异常输出 int i = nextpos+1, pos = i, follow = 0; string nom; bool apo = false; for (; i < sarg.size(); i++) if(sarg.at(i) == ' ') { // 跳过空格 } else if ((sarg.at(i) == ',')||(i == (sarg.size()-1))) { nom = sarg.substr(pos,i-pos); if (argTExcludes.find(nom) != std::string::npos) { nextpos = i; return ""; }; break; } else { if ((sarg.at(i) != ' ') && (!follow++) && ((sarg.at(i) == '"')||(sarg.at(i) == '\'')) ) apo = true; if ((sarg.at(i) == '"') && ( (i==0)||((i > 0) && (sarg.at(i-1) != '\'')) )) { i++; while ((sarg.at(i) != '"') && (i < sarg.size())) i++; }; if (sarg.at(i) == '(') { nom = sarg.substr(pos,i-pos); if (funcExcludes.find(nom) != std::string::npos) apo = true; }; }; nextpos = i; return (apo)?"":sarg.substr(pos,i-pos)+"="; }; template <typename... Ts> inline void locate(string ort,long line,string funct,const string prefix, const string trenner, const string postfix, string sarg, Ts... args) { #if WO_IS > 0 // 已取消所有范围限制 int pos = -1; bool apo; sarg += ","; std::ios_base::fmtflags f( cout.flags() ); std::cout << prefix << funct << '@' << ort[0] << ort.back() << dec << line << trenner; cout.flags( f ); ((std::cout << argT(sarg,pos) << args << ' '), ...); // 禁止传入endl,即使参数末尾不加空格也会触发语法错误 // ((std::cout << argT(sarg,pos); std::cout << args; std::cout << ' '), ...); // 该写法下传入endl同样会触发语法错误 if (postfix == "\0") std::cout.flush(); else std::cout << postfix; #endif };
内容的提问来源于stack exchange,提问作者user19365174
相关产品推荐
相关产品推荐

