我正在尝试替换
#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
您可以在临时的析构函数中打印输出,如下所示。
#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);
}
感谢所有回复的人。这就是我最终得到的结果。尽管我更喜欢使用类似函数的接口来与现有代码更加一致,但这确实很有效并且可以满足我的直接需求。最大的麻烦是需要一个结束令牌来终止消息。
#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;
}
这是一个活生生的例子,也许...