C++ 自动换行ostream

C++ 自动换行ostream,c++,std,ostream,C++,Std,Ostream,为这个糟糕的标题提前道歉,不知道该怎么称呼我正在尝试做的事情 一些背景知识,如果你不想读,跳到下一段。我有一个单元测试类,在这个类中,我使用一些条件调用assert,如果它失败,我将输出传入的一些字符串。我发现,如果我想说索引失败:“+I”,那么构建一个字符串来发送给它是非常烦人的。我的想法是返回一个std::ostream,而不是使用std::string。如果断言失败,我将返回std::cerr,如果断言通过,我将返回std::stringstream。我想我可以把这一切都做好。我必须在单元

为这个糟糕的标题提前道歉,不知道该怎么称呼我正在尝试做的事情

一些背景知识,如果你不想读,跳到下一段。我有一个单元测试类,在这个类中,我使用一些条件调用assert,如果它失败,我将输出传入的一些字符串。我发现,如果我想说索引失败:“+I”,那么构建一个字符串来发送给它是非常烦人的。我的想法是返回一个
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; }