C++ 自动换行ostream
为这个糟糕的标题提前道歉,不知道该怎么称呼我正在尝试做的事情 一些背景知识,如果你不想读,跳到下一段。我有一个单元测试类,在这个类中,我使用一些条件调用assert,如果它失败,我将输出传入的一些字符串。我发现,如果我想说索引失败:“+I”,那么构建一个字符串来发送给它是非常烦人的。我的想法是返回一个C++ 自动换行ostream,c++,std,ostream,C++,Std,Ostream,为这个糟糕的标题提前道歉,不知道该怎么称呼我正在尝试做的事情 一些背景知识,如果你不想读,跳到下一段。我有一个单元测试类,在这个类中,我使用一些条件调用assert,如果它失败,我将输出传入的一些字符串。我发现,如果我想说索引失败:“+I”,那么构建一个字符串来发送给它是非常烦人的。我的想法是返回一个std::ostream,而不是使用std::string。如果断言失败,我将返回std::cerr,如果断言通过,我将返回std::stringstream。我想我可以把这一切都做好。我必须在单元
std::ostream
,而不是使用std::string
。如果断言失败,我将返回std::cerr
,如果断言通过,我将返回std::stringstream
。我想我可以把这一切都做好。我必须在单元测试类中存储一个std::stringstream
,以便返回一个引用
我想做的是,不是返回一个标准的std::ostream
而是返回一个扩展的std::ostream
,它在执行时输出std::endl
,这样我就不必为每个断言记住它。具体而言,其想法如下:
UnitTest("My test");
ut.assert(false) << "Hello world";
ut.assert(1 == 0) << "On the next line";
UnitTest(“我的测试”);
ut.assert(false)无法复制std::cerr
(已删除std::basic_ostream
的复制构造函数)。因此,创建实现复制构造函数的派生类实际上不是一个选项
我建议您将您的newline\u ostream
创建为一个类,该类包含对std::ostream
的引用(而不是从中派生):
#include <iostream>
class newline_ostream
{
std::ostream &_strm;
public:
explicit newline_ostream(std::ostream &strm)
:_strm(strm)
{}
/* In the destructor, we submit a final 'endl'
before we die, as desired. */
virtual ~newline_ostream() {
_strm << std::endl;
}
template <typename T>
newline_ostream &operator<<(const T& t) {
_strm << t;
return *this;
}
};
int main()
{
newline_ostream s(std::cerr);
s << "This is a number " << 3 << '\n';
/* Here we make a copy (using the default copy
constructor of the new class), just to show
that it works. */
newline_ostream s2(s);
s2 << "This is another number: " << 12;
return 0;
}
#包括
类换行符_ostream
{
标准::奥斯特拉姆和斯特姆;
公众:
显式换行符_ostream(std::ostream&strm)
:_strm(strm)
{}
/*在析构函数中,我们提交一个最终的“endl”
在我们死之前,如所愿*/
虚拟~newline_ostream(){
_strm您也可以使用预处理器执行此操作:
#define U_ASSERT(ut, cond, stream) \
do { ut.assert(cond) << stream << std::endl; } while (0)
U_ASSERT(ut, 1 == 0, "The result is " << (1 == 0));
#定义U#U断言(ut、cond、stream)\
do{ut.assert(cond)我找到了一种适合我的方法(特别是我返回一个副本而不是一个引用):
可能是异端邪说,但为了导出流以产生另一种流类型,更通用的方法可以是定义操纵器:
// compile with g++ -std=c++11 -Wall -pedantic
#include <iostream>
class sassert
{
public:
sassert(bool b) :ps(), good(b)
{}
friend std::ostream& operator<<(std::ostream& s, sassert&& a)
{ a.ps = &s; if(a.good) s.setstate(s.failbit); return s; }
~sassert()
{
if(good && ps) ps->clear();
if(!good && ps) *ps << std::endl;
}
//move semantics allow sassert to be a result of a calculation
sassert(sassert&& s) :ps(s.ps), good(s.good) { s.ps=nullptr; }
sassert& operator=(sassert s){ ps=s.ps; good=s.good; s.ps=0; return *this; }
private:
std::ostream* ps;
bool good;
};
int main()
{
std::cout << sassert(false) << "this is a failed assertion";
std::cout << sassert(true) << "this is a good assertion";
std::cout << sassert(false) << "this is another failed assertion";
std::cout << sassert(true) << "this is another good assertion";
return 0;
}
这实际上取决于你想要实现的具体目标
例如,如果你从未听说过,现在可能是阅读的好时机。有一种方法可以使用垫片来做一些疯狂的事情
否则,下面是一个简单的版本:
class AssertMessage {
public:
AssertMessage(): _out(nullptr) {}
AssertMessage(std::ostream& out): _out(&out) {}
AssertMessage(AssertMessage&& other): _out(other._out) { other._out = nullptr; }
AssertMessage& operator=(AssertMessage&& other) {
if (_out) { _out << "\n"; }
_out = other._out;
other._out = nullptr;
return *this;
}
~AssertMessage() { if (_out) { _out << "\n"; } }
template <typename T>
AssertMessage& operator<<(T const& t) {
if (_out) { *_out << t; }
}
private:
std::ostream* _out;
}; // class AssertMessage
但是,如果我是你,我会认真考虑使用宏,因为你得到额外津贴:
#define UT_ASSERT(Cond_) \
assert(Cond_, #Cond_, __func__, __FILE__, __LINE__)
AssertMessage assert(bool test,
char const* condition,
char const* func,
char const* file,
int line)
{
if (test) { return AssertMessage(); }
return AssertMessage(std::cerr <<
"Failed assert at " << file << "#" << line <<
" in " << func << ": '" << condition << "', ");
}
然后,如果条件的计算结果为true
,的主体,而则根本不执行。你说我不能复制构造函数,这让我思考,我想出了我的答案。你介意评论一下它是否有什么问题(比如不可移植)?仅供参考,我想出了一个适合我的解决方案。但是,如果您认为它有什么问题,请告诉我。(我假设用户不会存储新行。)您应该详细说明您所做的(不再返回引用)复制流缓冲区是可能的,而且基本上是一个好主意,我认为。据我所知,当您从std::cerr
创建newline\u ostream
时,复制流缓冲区应该是可行的。但是,当您从assert
fu的原始版本中的临时std::stringstream
创建流缓冲区时,您会遇到麻烦nction.现在您有了nullStream
,我认为它是一个全局对象。我有点担心如果您重置该全局字符串流(例如,通过调用nullStream.str(“”;
)会发生什么情况。这可能会使现有的newline_ostream
对象持有的流缓冲区指针无效。诚然,我的原始答案根本没有解决这一方面。它只讨论了如何使复制构造函数成为可能;因此,问题是如何确保字符串流(如果使用字符串流而不是cerr
)继续生存是一个单独的问题。nullStream是我的UnitTest类的受保护成员。因此,只要我不清除字符串(这是一个很好的观点,因为我正考虑这样做以减少内存使用),我应该没问题。另一种可能性是,我可能会考虑扩展newline_ostream,以提供一个空的_ostream类,该类将包含自己的stringstream。如果它是异端,它肯定是非常聪明的。+1。@jogojapan:不过,和所有聪明的想法一样,它们与既定的实践相冲突。没有理由“流化”对象也不能检查流的状态,例如,当流失败时抛出异常。@MatthieuM。我怀疑所有聪明的想法都会这样做……但无论如何,关于failbit的使用有哪些既定的做法?failbit(不是badbit)失败时是否抛出异常设置是否合适?@jogojapan:我真的不知道,但我可以想象一些人可能会这样使用它。我指的不是规范的既定做法,而是“今天存在的做法”。与其说是测试坏位,不如说是测试流是否准备好接受输入。@MatthieuM.:根据标准,当流由于其性质而无法读取或写入某个内容时,就会设置failbit(当您无法读取某个数字时,因为输入提供文本,这是典型的情况)。始终按照标准,当设置故障位时,所有操作都可能不进行I/O。关于“已建立的实践”…我根本不知道你在说什么…我为什么要用一段时间。似乎那会把它塞进一个无限循环。@CrazyCasta:没错,我错过了中断
。宏的问题是通用的。带有多个语句的宏需要嵌入到一个块中,否则它们不能很好地处理if
或for
:if(…)宏
变为if(…)stmt1;stmt2;stmt3;
,解析为if(…){stmt1;}stmt2;stmt3;
by
Test 0
Test 1
Test 2
Test 3
Test 5
Test 7
Test 9
Test 10
// compile with g++ -std=c++11 -Wall -pedantic
#include <iostream>
class sassert
{
public:
sassert(bool b) :ps(), good(b)
{}
friend std::ostream& operator<<(std::ostream& s, sassert&& a)
{ a.ps = &s; if(a.good) s.setstate(s.failbit); return s; }
~sassert()
{
if(good && ps) ps->clear();
if(!good && ps) *ps << std::endl;
}
//move semantics allow sassert to be a result of a calculation
sassert(sassert&& s) :ps(s.ps), good(s.good) { s.ps=nullptr; }
sassert& operator=(sassert s){ ps=s.ps; good=s.good; s.ps=0; return *this; }
private:
std::ostream* ps;
bool good;
};
int main()
{
std::cout << sassert(false) << "this is a failed assertion";
std::cout << sassert(true) << "this is a good assertion";
std::cout << sassert(false) << "this is another failed assertion";
std::cout << sassert(true) << "this is another good assertion";
return 0;
}
this is a failed assertion
this is another failed assertion
class AssertMessage {
public:
AssertMessage(): _out(nullptr) {}
AssertMessage(std::ostream& out): _out(&out) {}
AssertMessage(AssertMessage&& other): _out(other._out) { other._out = nullptr; }
AssertMessage& operator=(AssertMessage&& other) {
if (_out) { _out << "\n"; }
_out = other._out;
other._out = nullptr;
return *this;
}
~AssertMessage() { if (_out) { _out << "\n"; } }
template <typename T>
AssertMessage& operator<<(T const& t) {
if (_out) { *_out << t; }
}
private:
std::ostream* _out;
}; // class AssertMessage
AssertMessage UnitTest::assert(bool i) {
return i ? AssertMessage() : AssertMessage(std::cerr);
}
#define UT_ASSERT(Cond_) \
assert(Cond_, #Cond_, __func__, __FILE__, __LINE__)
AssertMessage assert(bool test,
char const* condition,
char const* func,
char const* file,
int line)
{
if (test) { return AssertMessage(); }
return AssertMessage(std::cerr <<
"Failed assert at " << file << "#" << line <<
" in " << func << ": '" << condition << "', ");
}
Failed assert at project/test.cpp#45 in foo: 'x != 85', <your message>
#define UT_ASSERT(Cond_, Message_) \
while (!(Cond_)) { std::cerr << .... << Message_ << '\n'; break; }