如何用stringstream风格方法替代#define DEBUG()宏
问题描述
我想把下面的#define宏:
#define DEBUG(x) do { std::cout << x << std::endl; } while(0)
替换成符合RAII风格的错误追踪对象,目前的追踪类定义如下(已更新intro字段并加入栈转储逻辑):
class tracker() { // 注:此处存在语法错误,类定义不能带括号 public: tracker(std::string s) : intro(s) {} ~tracker() { if( this->stack.empty()) return; // dump_the_stack std::cout << "Messages from: " << intro << std::endl; for (const auto& item: stack) { std::cout << " * " << item; } std::cout << std::endl; // throw (abort) } void add( const std::string& message ) { stack.push_back( message ); } private: std::string intro; std::vector<const std::string> stack; // 注:此处类型错误,容器不能存储const std::string };
我希望使用该类时保留原有宏的语法风格,支持以下两种调用方式之一:
方式1:
tracker.add( "value exceeded: " << value );
方式2:
tracker << "value exceeded: " << value << std::endl;
我原本设想的解决方案类似:
tracker::add( ostringstream os ) { stack.push_back( os.str() + std::endl ); }
但第一种调用方式无法隐式转换为ostringstream,第二种方式的operator<<无法识别终止操作,想问有没有简单的解决办法?
补充:理想的使用场景是以下两种测试用例之一:
Case 1(首选):
int main() { tracker t("test case 1"); int trace = 0; t.add("hello world 1!" << " trace: " << ++trace); t.add("hello world 2!" << " trace: " << ++trace); return 0; }
Case 2(可接受):
int main() { tracker t("test case 1"); int trace = 0; t << "hello world 1!" << " trace: " << ++trace; t << "hello world 2!" << " trace: " << ++trace; return 0; }
期望输出:
Messages from: test case 1 * hello world 1! trace: 1 * hello world 2! trace: 2
解决方案
先修正原tracker类的基础错误:
- 类定义去掉多余括号,改为
class tracker { - 将
std::vector<const std::string>改为std::vector<std::string>
一、实现Case 2(operator<<调用方式)
核心思路是通过代理类追踪流操作的生命周期:每次<<调用创建临时代理对象,所有流操作写入代理内的ostringstream,语句结束时代理销毁,自动将流内容存入tracker栈中。
#include <iostream> #include <vector> #include <string> #include <sstream> class tracker { public: tracker(std::string s) : intro(std::move(s)) {} ~tracker() { if (stack.empty()) return; std::cout << "Messages from: " << intro << std::endl; for (const auto& item : stack) { std::cout << " * " << item << std::endl; } } // 代理类:负责接收流操作,销毁时提交内容 class proxy { public: proxy(tracker& t) : parent(t), oss() {} ~proxy() { parent.stack.push_back(oss.str()); } // 重载所有类型的operator<< template<typename T> proxy& operator<<(const T& val) { oss << val; return *this; } // 支持std::endl等流操纵符 proxy& operator<<(std::ostream& (*manip)(std::ostream&)) { oss << manip; return *this; } private: tracker& parent; std::ostringstream oss; }; // tracker的operator<<返回代理对象 template<typename T> proxy operator<<(const T& val) { proxy p(*this); p << val; return p; } private: std::string intro; std::vector<std::string> stack; }; // 测试Case 2 int main() { tracker t("test case 1"); int trace = 0; t << "hello world 1!" << " trace: " << ++trace; t << "hello world 2!" << " trace: " << ++trace; return 0; }
二、实现Case 1(add方法支持流语法)
要让add接收流表达式,需要将表达式转换为字符串,这里提供两种实现方式:
方式1:用宏包装流操作(最贴近需求语法)
通过宏将流操作封装为ostringstream,再传递给add:
#include <iostream> #include <vector> #include <string> #include <sstream> class tracker { public: tracker(std::string s) : intro(std::move(s)) {} ~tracker() { if (stack.empty()) return; std::cout << "Messages from: " << intro << std::endl; for (const auto& item : stack) { std::cout << " * " << item << std::endl; } } void add(std::string msg) { stack.push_back(std::move(msg)); } private: std::string intro; std::vector<std::string> stack; }; // 宏:将流操作转换为字符串后调用add #define TRACE_ADD(t, expr) do { \ std::ostringstream oss; \ oss << expr; \ t.add(oss.str()); \ } while(0) // 测试Case 1 int main() { tracker t("test case 1"); int trace = 0; TRACE_ADD(t, "hello world 1!" << " trace: " << ++trace); TRACE_ADD(t, "hello world 2!" << " trace: " << ++trace); return 0; }
方式2:用辅助类实现无宏版本
定义可隐式转换为字符串的辅助流类,避免宏的副作用:
#include <iostream> #include <vector> #include <string> #include <sstream> // 辅助流类:可隐式转换为std::string class trace_stream { public: template<typename T> trace_stream& operator<<(const T& val) { oss << val; return *this; } operator std::string() const { return oss.str(); } private: std::ostringstream oss; }; class tracker { public: tracker(std::string s) : intro(std::move(s)) {} ~tracker() { if (stack.empty()) return; std::cout << "Messages from: " << intro << std::endl; for (const auto& item : stack) { std::cout << " * " << item << std::endl; } } void add(std::string msg) { stack.push_back(std::move(msg)); } private: std::string intro; std::vector<std::string> stack; }; // 测试Case 1 int main() { tracker t("test case 1"); int trace = 0; t.add(trace_stream() << "hello world 1!" << " trace: " << ++trace); t.add(trace_stream() << "hello world 2!" << " trace: " << ++trace); return 0; }
内容的提问来源于stack exchange,提问作者rand'Chris
相关产品推荐
相关产品推荐

