如何用 stringstream 类型方法替换 #define DEBUG()

问题描述 投票:0回答:2

我正在尝试替换

#define
函数,如下所示:

#define DEBUG(x) do { std::cout << x << std::endl; } while(0)

具有更多RAII类型的错误跟踪对象。此跟踪对象当前类似于此(*更新为包括“介绍”,以确保清晰;并包含来自 Ahmed AEK 的堆栈转储逻辑):

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;
};

我想在保留现有

#define
语法的同时使用它。例如,我想要一种方法,允许类用户维护类似于以下之一的语法:

tracker.add( "value exceeded: " << value );

甚至:

tracker << "value exceeded: " << value << std::endl;

理想情况下,我相信我想要的解决方案如下所示:

tracker::add( ostringstream os ) {
  stack.push_back( os.str() + std::endl );
}

但对于第一种情况,似乎没有隐式转换为 ostringstream,并且无法确定

<<
情况下的终结符
operator<<()
。这里有一个我忽略的简单解决方案吗?


编辑:为了在下面进行澄清,并在上面的示例类中添加了“简介”:

我的理想场景是能够在这两个测试用例中使用语法:

案例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;
}

情况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
c++ c++17
2个回答
2
投票

您可以在临时的析构函数中打印输出,如下所示。

#include <iostream>
#include <vector>
#include <string>

class tracker {
public:
  tracker() = default;
  ~tracker() {
    for (const auto& item: stack)
    {
        std::cout << item;
    }
    std::cout << std::endl;
  }
  void add(std::string message ) {
    stack.push_back(std::move(message));
  }
private:
  std::vector<std::string> stack;
};

tracker&&  operator<<(tracker&& tr, std::string item)
{
    tr.add(std::move(item));
    return std::move(tr);
}

#define DEBUG(X) tracker{} << X

int main()
{
    DEBUG("hello" << " " << "world!");
}

一个不幸的副作用是需要执行

return std::move(tr);
并且由于该对象的生命周期一直延伸到结束分号,因此这里没有 UB 或泄漏。

为了能够使用任何用户定义的 ostream 重载,您可以创建一个使用字符串流的自由函数,如下所示。

template <typename T>
tracker&& operator<<(tracker&& tr, T&& item)
{
    if constexpr (std::is_convertible_v<T,std::string>)
    {
        tr.add(std::forward<T>(item));
    }
    else
    {
        std::ostringstream s;
        s << std::forward<T>(item);
        tr.add(s.str());
    }
    return std::move(tr);
}

0
投票

感谢所有回复的人。这就是我最终得到的结果。尽管我更喜欢使用类似函数的接口来与现有代码更加一致,但这确实很有效并且可以满足我的直接需求。最大的麻烦是需要一个结束令牌来终止消息。

#include <iostream>
#include <sstream>
#include <vector>
#include <string>

class tracker {
public:
  tracker(std::string s) : intro(s) {};
  ~tracker() {
    std::cout << "Messages from: " << intro << std::endl;
    for (const auto& item: stack) {
        std::cout << " * " << item << std::endl;
    }
    std::cout << std::endl;
  }
  void add(std::string message ) {
    stack.push_back(std::move(message));
  }
  void operator<<(std::ostream& (*)(std::ostream&)) { // catch std::endl
    this->add(linebuf.str());
    linebuf.str(std::string());
  }
  template <typename T>
  tracker& operator<<(T&& item) {
    linebuf << std::forward<T>(item);
    return *this;
  }

private:
  std::string intro;
  std::ostringstream linebuf;
  std::vector<std::string> stack;
};

int main()
{
    tracker t("test1");
    int trace = 0;

    t << "hello world 1!" << " trace: " << ++trace << std::endl;
    t << "hello world 2!" << " trace: " << ++trace << std::endl;
    return 0;
}

这是一个活生生的例子,也许...

© www.soinside.com 2019 - 2024. All rights reserved.