Automatic update

Sorry in advance for the poor headline, not sure what to name what I'm trying to do.

In some cases, if you do not want to read, continue to the next paragraph. I have a unit test class where I call assert with some condition, and if it fails, I output some string that was passed. I found it quite difficult to create a string to send if, for example, I want to say "Failed on index: " + i . My idea is to return std::ostream instead of accepting std::string . If assert fails, I return std::cerr , if the statement passes, I return a std::stringstream . I suppose I could do all this perfectly. I would need to store std::stringstream in my unit test class so that I can return the link.

What would I like to do, instead of returning the standard std::ostream returning the extended std::ostream , which std::endl when this is done, so I don’t need to remember it for every statement. In particular, the idea is this:

 UnitTest("My test"); ut.assert(false) << "Hello world"; ut.assert(1 == 0) << "On the next line"; 

The idea is that upon destruction, this new class draws the final line and that it will be destroyed as soon as it is no longer used (i.e. no more than <<operators). So far this is what I have (I deleted a part of the code in assert, and this is really inside the class, but this is enough to show what is happening):

 class newline_ostream : public std::ostream { public: newline_ostream(std::ostream& other) : std::ostream(other.rdbuf()){} ~newline_ostream() { (*this) << std::endl; } }; newline_ostream& assert(bool condition, std::string error) { if(!condition) { return newline_ostream(std::cerr); } return newline_ostream(std::stringstream()); } 

When I try to use this method, I get some material, basically telling me that returning the object I just created is wrong, because it is not an lvalue value. When I try to change it so as not to return a link, it complains that there is no copy constructor (presumably this is because I am extending std::ostream and it does not have a copy constructor).

What I'm looking for is some method that forces the compiler to create a temporary newline_ostream, which assert() will write its result to, which will die as soon as it is no longer used (i.e. no more than <<statements), is this possible , And if so, how?

+4
source share
5 answers

Copying std::cerr not possible (copy constructor std::basic_ostream removed). Therefore, creating a derived class that implements the copy constructor is not really an option.

I suggest you create your newline_ostream as a class containing a reference to (and not derived from) 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; } 
+2
source

It may be heresy, but instead, to obtain a stream to create another type of stream, a more generalized way might be to define a manipulator:

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

Creation will start

 this is a failed assertion this is another failed assertion 
+2
source

It really depends on the characteristics of what you want to achieve.

If you've never heard of Type Tunneling , for example, this might be a good time to read about it. There is a way to use pads to do crazy things ...

Otherwise, this is a simple version:

 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 

Notice how, by nesting a pointer, we don’t need a global β€œnull” object? This is the main difference between pointers and links. Also pay attention to the use of the assignment operator move / move to avoid outputting lines of 2 or more.

Then you can write the assert method:

 AssertMessage UnitTest::assert(bool i) { return i ? AssertMessage() : AssertMessage(std::cerr); } 

However .... I would seriously consider using a macro if you were you, because you get additional benefits:

 #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 << "', "); } 

And then you get something like:

 Failed assert at project/test.cpp#45 in foo: 'x != 85', <your message> 

In large test suites, it is invaluable to have a file name and line number (at least).

Finally, the macro gets you even more: if you call a function in your message, for example ut.assert(x) << x.foo(); , then x.foo() needs to be evaluated completely, even if the message is not printed; it's pretty wasteful. However, with the macro:

 #define UT_ASSERT(Cond_, Message_) \ while (!(Cond_)) { std::cerr << .... << Message_ << '\n'; break; } 

then if the condition is true , the while body is not executed at all.

+1
source

You can also use a preprocessor to do this:

 #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)); 

However, this and the method you are already using (with jogojapan modifications) are basically the only alternatives. This is due to the fact that even if you start working with buffers, you cannot tell when one output operation is performed, and then the next starts, so you do not know when to put in a new line.

0
source

I found a way that works for me (in particular, I return a copy, not a link):

 class newline_ostream : public std::ostream { public: newline_ostream(const std::ostream& other) : std::ostream(other.rdbuf()){} newline_ostream(const newline_ostream& other) : std::ostream(other.rdbuf()){} ~newline_ostream() { (*this) << std::endl; } }; newline_ostream assert(bool condition, std::string error) { if(!condition) { return newline_ostream(std::cerr); } return newline_ostream(nullStream); // nullStream defined elsewhere (in the class) } 

Using:

 ut.assert(ps.getFaces()[i] == expected[i], ss.str()) << "Test " << i; 

Output:

 Test 0 Test 1 Test 2 Test 3 Test 5 Test 7 Test 9 Test 10 
0
source

Source: https://habr.com/ru/post/1442575/


All Articles