]> git.ipfire.org Git - thirdparty/squid.git/commitdiff
Add AtMostOnce stream manipulator (#1742)
authorFrancesco Chemolli <5175948+kinkie@users.noreply.github.com>
Mon, 8 Apr 2024 15:24:39 +0000 (15:24 +0000)
committerSquid Anubis <squid-anubis@squid-cache.org>
Mon, 8 Apr 2024 15:24:44 +0000 (15:24 +0000)
src/base/IoManip.h
src/tests/testIoManip.cc

index 58fdd7f2becb14f4ab0a4a714ed2ca1284a90957..3983c796644920659ab63443b44839213981b466 100644 (file)
@@ -220,5 +220,48 @@ operator <<(std::ostream &os, const AsList<Container> &manipulator)
 template <typename Container>
 inline auto asList(const Container &c) { return AsList<Container>(c); }
 
+/// Helps print T object at most once per AtMostOnce<T> 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 T>
+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 <class T>
+inline auto &
+operator <<(std::ostream &os, AtMostOnce<T> &a) {
+    a.print(os);
+    return os;
+}
+
 #endif /* SQUID_SRC_BASE_IOMANIP_H */
 
index 22abc609938929354225240f1f9a13b7fef67c9f..80b0f024f7fcf1e7cb90a6410cae4572bb38b38b 100644 (file)
@@ -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[])
 {