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 */
{
CPPUNIT_TEST_SUITE(TestIoManip);
CPPUNIT_TEST(testAsHex);
+ CPPUNIT_TEST(testAtMostOnce);
CPPUNIT_TEST_SUITE_END();
protected:
void testAsHex();
+ void testAtMostOnce();
};
CPPUNIT_TEST_SUITE_REGISTRATION( TestIoManip );
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[])
{