You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何用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类的基础错误:

  1. 类定义去掉多余括号,改为class tracker {
  2. 将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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.27 15:02:47