From: Francesco Chemolli <5175948+kinkie@users.noreply.github.com> Date: Mon, 8 Apr 2024 15:24:39 +0000 (+0000) Subject: Add AtMostOnce stream manipulator (#1742) X-Git-Tag: SQUID_7_0_1~146 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=3996c0dc867a1f0a016aa828e3928a9270735419;p=thirdparty%2Fsquid.git Add AtMostOnce stream manipulator (#1742) --- diff --git a/src/base/IoManip.h b/src/base/IoManip.h index 58fdd7f2be..3983c79664 100644 --- a/src/base/IoManip.h +++ b/src/base/IoManip.h @@ -220,5 +220,48 @@ operator <<(std::ostream &os, const AsList &manipulator) template inline auto asList(const Container &c) { return AsList(c); } +/// Helps print T object at most once per AtMostOnce object lifetime. +/// T objects are printed to std::ostream using operator "<<". +/// +/// \code +/// auto headerOnce = AtMostOnce("Transaction Details:\n"); +/// if (detailOne) +/// os << headerOnce << *detailOne; +/// if (const auto detailTwo = findAnotherDetail()) +/// os << headerOnce << *detailTwo; +/// \endcode +template +class AtMostOnce +{ +public: + /// caller must ensure `t` lifetime extends to the last use of this AtMostOnce instance + explicit AtMostOnce(const T &t): toPrint(t) {} + + void print(std::ostream &os) { + if (!printed) { + os << toPrint; + printed = true; + } + } + +private: + const T &toPrint; + bool printed = false; +}; + +/// Prints AtMostOnce argument if needed. The argument is not constant to +/// prevent wrong usage: +/// +/// \code +/// /* Compiler error: cannot bind non-const lvalue reference to an rvalue */ +/// os << AtMostOnce(x); +/// \endcode +template +inline auto & +operator <<(std::ostream &os, AtMostOnce &a) { + a.print(os); + return os; +} + #endif /* SQUID_SRC_BASE_IOMANIP_H */ diff --git a/src/tests/testIoManip.cc b/src/tests/testIoManip.cc index 22abc60993..80b0f024f7 100644 --- a/src/tests/testIoManip.cc +++ b/src/tests/testIoManip.cc @@ -19,10 +19,12 @@ class TestIoManip: public CPPUNIT_NS::TestFixture { CPPUNIT_TEST_SUITE(TestIoManip); CPPUNIT_TEST(testAsHex); + CPPUNIT_TEST(testAtMostOnce); CPPUNIT_TEST_SUITE_END(); protected: void testAsHex(); + void testAtMostOnce(); }; CPPUNIT_TEST_SUITE_REGISTRATION( TestIoManip ); @@ -153,6 +155,34 @@ TestIoManip::testAsHex() resetStream(ss); } +void +TestIoManip::testAtMostOnce() +{ + { + std::ostringstream ss; + auto textOnce = AtMostOnce("text1"); + ss << textOnce; + CPPUNIT_ASSERT_EQUAL(std::string("text1"), ss.str()); + ss << textOnce; + ss << textOnce; + CPPUNIT_ASSERT_EQUAL(std::string("text1"), ss.str()); + } + + { + std::ostringstream ss; + // Cannot create std::string when creating textOnce because the string may be + // destroyed before we are done with textOnce: + // auto textOnce = AtMostOnce(std::string("do not do this")); + const std::string s("text2"); + auto textOnce = AtMostOnce(s); + ss << textOnce; + CPPUNIT_ASSERT_EQUAL(std::string("text2"), ss.str()); + ss << textOnce; + ss << textOnce; + CPPUNIT_ASSERT_EQUAL(std::string("text2"), ss.str()); + } +} + int main(int argc, char *argv[]) {