]> git.ipfire.org Git - thirdparty/squid.git/commitdiff
Implement EnumIterator
authorFrancesco Chemolli <kinkie@squid-cache.org>
Sat, 5 Sep 2015 11:28:21 +0000 (13:28 +0200)
committerFrancesco Chemolli <kinkie@squid-cache.org>
Sat, 5 Sep 2015 11:28:21 +0000 (13:28 +0200)
acinclude/ax_cxx_0x_types.m4
configure.ac
src/Makefile.am
src/base/EnumIterator.h [new file with mode: 0644]
src/base/Makefile.am
src/icp_opcode.h
src/icp_v2.cc
src/neighbors.cc
src/tests/testEnumIterator.cc [new file with mode: 0644]
src/tests/testEnumIterator.h [new file with mode: 0644]

index 270d378e2f1e3bdc753864a567aec0a5a476e265..426a7fe5cd569beff9f50c18ce827582c5b799ff 100644 (file)
@@ -80,3 +80,26 @@ AC_DEFUN([AX_CXX_TYPE_UNIFORM_DISTRIBUTIONS],[
 
   AC_LANG_POP
 ])
+
+## SQUID_CXX_STD_UNDERLYING_TYPE
+## checks whether the std::underlying_type<enumType>::type trait exists
+AC_DEFUN([SQUID_CXX_STD_UNDERLYING_TYPE],[
+  AC_CACHE_CHECK([whether compiler supports std::underlying_type],
+    [squid_cv_have_std_underlying_type],[
+      AC_REQUIRE([AC_PROG_CXX])
+      AC_LANG_PUSH([C++])
+      AC_COMPILE_IFELSE([
+        AC_LANG_PROGRAM([
+#include <type_traits>
+enum class testEnum { one, two, three };
+        ],[
+        std::underlying_type<testEnum>::type testNum = 0;
+        ])],
+        [squid_cv_have_std_underlying_type=yes],
+        [squid_cv_have_std_underlying_type=no])
+      AC_LANG_POP
+  ])
+  SQUID_DEFINE_BOOL([HAVE_STD_UNDERLYING_TYPE],
+     [$squid_cv_have_std_underlying_type],
+     [Define if stdlibc support std::underlying_type for enums])
+])
index d0c55fed2b1b94687e0c37e4e22e6cd6ce22db36..1f56a26882f9916504417b399789aa91d50f6209 100644 (file)
@@ -2939,6 +2939,7 @@ dnl Some C++11 types we try to use
 AX_CXX_TYPE_NULLPTR
 AX_CXX_TYPE_UNIQUE_PTR
 AX_CXX_TYPE_UNIFORM_DISTRIBUTIONS
+SQUID_CXX_STD_UNDERLYING_TYPE
 
 dnl On Solaris 9 x86, gcc may includes a "fixed" set of old system include files
 dnl that is incompatible with the updated Solaris header files.
index 8a9d04aefcadee15a80365fe9be16edd9278bc92..8593d17efa70690c22b404c2867f0d009292c1fd 100644 (file)
@@ -902,6 +902,7 @@ check_PROGRAMS+=\
        tests/testCharacterSet \
        tests/testDiskIO \
        tests/testDns \
+       tests/testEnumIterator \
        tests/testEvent \
        tests/testEventLoop \
        tests/test_http_range \
@@ -3771,6 +3772,23 @@ tests_testLookupTable_LDADD = \
        $(XTRA_LIBS)
 tests_testLookupTable_DEPENDENCIES = $(SQUID_CPPUNIT_LA)
 
+tests_testEnumIterator_SOURCES = \
+       base/EnumIterator.h \
+       tests/stub_debug.cc \
+       tests/stub_libmem.cc \
+       tests/testEnumIterator.h \
+       tests/testEnumIterator.cc
+nodist_tests_testEnumIterator_SOURCES = \
+       $(TESTSOURCES)
+tests_testEnumIterator_LDFLAGS = $(LIBADD_DL)
+tests_testEnumIterator_LDADD = \
+       base/libbase.la \
+       $(SQUID_CPPUNIT_LIBS) \
+       $(COMPAT_LIB) \
+       $(SQUID_CPPUNIT_LA) \
+       $(XTRA_LIBS)
+tests_testEnumIterator_DEPENDENCIES =
+
 TESTS += testHeaders
 
 ## Special Universal .h dependency test script
diff --git a/src/base/EnumIterator.h b/src/base/EnumIterator.h
new file mode 100644 (file)
index 0000000..33d527f
--- /dev/null
@@ -0,0 +1,226 @@
+/*
+ * Copyright (C) 1996-2015 The Squid Software Foundation and contributors
+ *
+ * Squid software is distributed under GPLv2+ license and includes
+ * contributions from numerous individuals and organizations.
+ * Please see the COPYING and CONTRIBUTORS files for details.
+ */
+
+#ifndef SQUID_BASE_ENUMITERATOR_H
+#define SQUID_BASE_ENUMITERATOR_H
+
+#include <iterator>
+#include <type_traits>
+
+/** Shared functionality between forward and reverse enum iterators
+ *
+ * This class is not very useful by itself, it contains code shared by
+ * EnumIterator and ReverseEnumIterator.
+ *
+ * \see EnumIterator, ReverseEnumIterator
+ */
+template <typename EnumType>
+class EnumIteratorBase : public std::iterator<std::bidirectional_iterator_tag, EnumType>
+{
+protected:
+#if HAVE_STD_UNDERLYING_TYPE
+    typedef typename std::underlying_type<EnumType>::type iterator_type;
+#else
+    typedef int iterator_type;
+#endif
+
+public:
+    explicit EnumIteratorBase(EnumType e) : current(static_cast<iterator_type>(e)) {}
+
+    bool operator==(const EnumIteratorBase &i) const {
+        return current == i.current;
+    }
+
+    bool operator!=(const EnumIteratorBase &i) const {
+        return current != i.current;
+    }
+
+    EnumType operator*() const {
+        return static_cast<EnumType>(current);
+    }
+protected:
+    iterator_type current;
+};
+
+/** bidirectional iterator over an enum type
+ *
+ * It can be instantiated using any enum (or C++11 strongly-typed enum)
+ * value; the most common expected use scenario has iterators emitted by
+ * EnumRange and WholeEnum via standard begin() and end() calls.
+ *
+ * In order for the iterator to work, it is mandatory that the underlying
+ * enum type's representation values be sequential.
+ *
+ * The iterator does not check for bounds when incrementing or decrementing,
+ * that responsibility is left to the caller.
+ *
+ * \see EnumRange, WholeEnum, ReverseEnumIterator
+ */
+template <typename EnumType>
+class EnumIterator : public EnumIteratorBase<EnumType>
+{
+public:
+    explicit EnumIterator(EnumType e) : EnumIteratorBase<EnumType>(e) {}
+
+    EnumIterator& operator++() {
+        ++ EnumIteratorBase<EnumType>::current;
+        return *this;
+    }
+
+    EnumIterator& operator++(int) {
+        EnumIterator rv(*this);
+        ++ EnumIteratorBase<EnumType>::current;
+        return rv;
+    }
+
+    EnumIterator& operator--() {
+        -- EnumIteratorBase<EnumType>::current;
+        return *this;
+    }
+
+    EnumIterator& operator--(int) {
+        EnumIterator rv(*this);
+        -- EnumIteratorBase<EnumType>::current;
+        return rv;
+    }
+};
+
+/** bidirectional reverse iterator over an enum type
+ *
+ * It can be instantiated using any enum (or C++11 strongly-typed enum)
+ * value; the most common expected use scenario has iterators emitted by
+ * EnumRange and WholeEnum via standard rbegin() and rend() calls.
+ *
+ * In order for the iterator to work, it is mandatory that the underlying
+ * enum type's representation values be sequential.
+ *
+ * The iterator does not check for bounds; behavior is undefined if the iterator
+ * is incremented (or decremented) outside the range representing valid
+ * enum symbols (remember: an enum is not a data structure).
+ *
+ * \see EnumRange, WholeEnum, EnumIterator
+ */
+template <typename EnumType>
+class ReverseEnumIterator : public EnumIteratorBase<EnumType>
+{
+public:
+    explicit ReverseEnumIterator(EnumType e) : EnumIteratorBase<EnumType>(e) {}
+
+    // prefix increment
+    ReverseEnumIterator& operator++() {
+        -- EnumIteratorBase<EnumType>::current;
+        return *this;
+    }
+
+    // postfix increment
+    ReverseEnumIterator& operator++(int) {
+        ReverseEnumIterator rv(*this);
+        -- EnumIteratorBase<EnumType>::current;
+        return rv;
+    }
+
+    // prefix decrement
+    ReverseEnumIterator& operator--() {
+        ++ EnumIteratorBase<EnumType>::current;
+        return *this;
+    }
+
+    // postfix decrement
+    ReverseEnumIterator& operator--(int) {
+        ReverseEnumIterator rv(*this);
+        ++ EnumIteratorBase<EnumType>::current;
+        return rv;
+    }
+};
+
+/** Class expressing a continuous range of an enum for range-for expressions
+ *
+ * This class requires that the underlying enum values be represented by
+ * continuous values of an integral type.
+ * Users will usually not rely on this class directly but on the more convenient
+ * EnumRange function
+ *
+ * \note EnumIterator<enum>(EnumType::firstmember,EnumType::lastmember)
+ * will miss EnumType::lastmember while iterating. If you need to iterate
+ * over all of EnumType, use class WholeEnum.
+ *
+ * \see EnumRange, WholeEnum
+ */
+template <typename EnumType>
+class EnumRangeT
+{
+public:
+    typedef EnumIterator<EnumType> iterator;
+    typedef ReverseEnumIterator<EnumType> reverse_iterator;
+    EnumRangeT(EnumType first, EnumType one_past_last) : begin_(first), end_(one_past_last) { }
+    iterator begin() const { return iterator(begin_);}
+    iterator end() const { return iterator(end_);}
+    reverse_iterator rbegin() const { return ++reverse_iterator(end_); }
+    reverse_iterator rend() const { return ++reverse_iterator(begin_); }
+private:
+    EnumType begin_;
+    EnumType end_;
+};
+
+/** Generate a continuous range of an enum for range-for expressions
+ *
+ * convenience function to deduce the right type for instantiating EnumRangeT.
+ * See EnumRangeT for more detailed documentation and caveats.
+ *
+ * Typical use:
+ * \code
+ * enum class EnumType {
+ *   blue, red, yellow, green, pink
+ * };
+ * for (auto enumvalue : EnumRange(EnumType::red,EnumType::green)) {
+ *   do_stuff(enumvalue); // will be called twice, with arguments red and yellow
+ * }
+ * \endcode
+ */
+template <typename EnumType>
+EnumRangeT<EnumType> EnumRange(EnumType begin, EnumType one_past_end)
+{
+    return EnumRangeT<EnumType>(begin,one_past_end);
+}
+
+/** Class expressing a continuous range of a whole enum for range-for expressions
+ *
+ * Class for iterating all enum values, from EnumType::enumBegin_ up to, but
+ * not including, EnumType::enumEnd_.
+ *
+ * This class requires that:
+ * - the underlying enum values be represented by continuous values of
+ *   an integral type.
+ * - both enumBegin_ and enumEnd_ markers must be present as EnumType values;
+ * - enumBegin_ must have the same representation as the first element of the
+ *   enum
+ * - enumEnd_ must have a representation that is one past the last
+ *   user-accessible value of the enum.
+ *
+ * Typical use:
+ * \code
+ * enum class EnumType {
+ *   enumBegin_ = 0,
+ *   first_value = enumBegin_,
+ *   second_value,
+ *   enumEnd_
+ * };
+ * for(auto enumvalue : WholeEnum<EnumType>()) {
+ *   do_stuff();
+ * }
+ * \endcode
+ */
+template <typename EnumType>
+class WholeEnum : public EnumRangeT<EnumType>
+{
+public:
+    WholeEnum() : EnumRangeT<EnumType>(EnumType::enumBegin_, EnumType::enumEnd_) {}
+};
+
+#endif /* SQUID_BASE_ENUMITERATOR_H */
+
index 2b33f2d9de2b2afc1572c5d6408e6df9ab627997..23086ae5e4be3d7df6fa50007bfc4d507069c92d 100644 (file)
@@ -24,6 +24,7 @@ libbase_la_SOURCES = \
        CbDataList.h \
        CharacterSet.h \
        CharacterSet.cc \
+       EnumIterator.h \
        InstanceId.h \
        Lock.h \
        LookupTable.h \
index 0e6927ae10c972c32d5eef127efa8071f55c8126..967a64a7dca5d52654d931a62a580c436a0de592 100644 (file)
@@ -10,8 +10,9 @@
 #define _SQUID_ICP_OPCODE_H
 
 /// \ingroup ServerProtocolICPAPI
-typedef enum {
-    ICP_INVALID,
+enum icp_opcode {
+    enumBegin_ = 0,
+    ICP_INVALID = enumBegin_,
     ICP_QUERY,
     ICP_HIT,
     ICP_MISS,
@@ -35,8 +36,9 @@ typedef enum {
     ICP_MISS_NOFETCH,
     ICP_DENIED,
     ICP_HIT_OBJ,
-    ICP_END
-} icp_opcode;
+    ICP_END,
+    enumEnd_ = ICP_END // We misuse ICP_END in stats. Do not do this elsewhere.
+};
 
 extern const char *icp_opcode_str[];
 
index 27b3726ec20b5bef5f4930443b4f4392b6e30207..65cc8b8676f1faef81aaddde9d93568b25f98cb3 100644 (file)
@@ -101,10 +101,10 @@ _icp_common_t::_icp_common_t(char *buf, unsigned int len) :
 icp_opcode
 _icp_common_t::getOpCode() const
 {
-    if (opcode > (char)ICP_END)
+    if (opcode > static_cast<char>(icp_opcode::ICP_END))
         return ICP_INVALID;
 
-    return (icp_opcode)opcode;
+    return static_cast<icp_opcode>(opcode);
 }
 
 /* ICPState */
index 093b5ee7d4933a940ef0d3a6f440d9ea7015436c..627dc5c5afd00449446845081b1ca4e65aaa9ccc 100644 (file)
@@ -11,6 +11,7 @@
 #include "squid.h"
 #include "acl/FilledChecklist.h"
 #include "anyp/PortCfg.h"
+#include "base/EnumIterator.h"
 #include "CacheDigest.h"
 #include "CachePeer.h"
 #include "comm/Connection.h"
@@ -1572,7 +1573,6 @@ static void
 dump_peers(StoreEntry * sentry, CachePeer * peers)
 {
     char ntoabuf[MAX_IPSTRLEN];
-    icp_opcode op;
     int i;
 
     if (peers == NULL)
@@ -1634,7 +1634,7 @@ dump_peers(StoreEntry * sentry, CachePeer * peers)
             } else {
 #endif
 
-                for (op = ICP_INVALID; op < ICP_END; ++op) {
+                for (auto op : WholeEnum<icp_opcode>()) {
                     if (e->icp.counts[op] == 0)
                         continue;
 
diff --git a/src/tests/testEnumIterator.cc b/src/tests/testEnumIterator.cc
new file mode 100644 (file)
index 0000000..3200b6e
--- /dev/null
@@ -0,0 +1,143 @@
+/*
+ * Copyright (C) 1996-2015 The Squid Software Foundation and contributors
+ *
+ * Squid software is distributed under GPLv2+ license and includes
+ * contributions from numerous individuals and organizations.
+ * Please see the COPYING and CONTRIBUTORS files for details.
+ */
+
+#include "squid.h"
+#include "tests/testEnumIterator.h"
+#include "unitTestMain.h"
+
+#include <cppunit/TestAssert.h>
+
+CPPUNIT_TEST_SUITE_REGISTRATION( testEnumIterator );
+
+enum class TestEnum {
+    enumBegin_ = 0,
+    zero = enumBegin_,
+    one,
+    two,
+    three,
+    four,
+    enumEnd_
+};
+
+enum class UnsignedTestEnum : unsigned char {
+    enumBegin_ = 0,
+    zero = enumBegin_,
+    one,
+    two,
+    three,
+    four,
+    enumEnd_
+};
+
+void
+testEnumIterator::testForwardIter()
+{
+    WholeEnum<TestEnum>::iterator i = WholeEnum<TestEnum>().begin();
+    CPPUNIT_ASSERT(*i == TestEnum::zero);
+    ++i;
+    CPPUNIT_ASSERT(*i == TestEnum::one);
+    ++i;
+    CPPUNIT_ASSERT(*i == TestEnum::two);
+    ++i;
+    CPPUNIT_ASSERT(*i == TestEnum::three);
+    ++i;
+    CPPUNIT_ASSERT(*i == TestEnum::four);
+    ++i;
+    CPPUNIT_ASSERT(i == WholeEnum<TestEnum>().end());
+}
+
+void
+testEnumIterator::testReverseIter()
+{
+    WholeEnum<TestEnum>::reverse_iterator i = WholeEnum<TestEnum>().rbegin();
+    CPPUNIT_ASSERT(*i == TestEnum::four);
+    ++i;
+    CPPUNIT_ASSERT(*i == TestEnum::three);
+    ++i;
+    CPPUNIT_ASSERT(*i == TestEnum::two);
+    ++i;
+    CPPUNIT_ASSERT(*i == TestEnum::one);
+    ++i;
+    CPPUNIT_ASSERT(*i == TestEnum::zero);
+    ++i;
+    CPPUNIT_ASSERT(i == WholeEnum<TestEnum>().rend());
+}
+
+void
+testEnumIterator::testBidirectionalIter()
+{
+    WholeEnum<TestEnum>::iterator i = WholeEnum<TestEnum>().begin();
+    CPPUNIT_ASSERT(*i == TestEnum::zero);
+    ++i;
+    CPPUNIT_ASSERT(*i == TestEnum::one);
+    --i;
+    CPPUNIT_ASSERT(*i == TestEnum::zero);
+
+    auto enumBegin=WholeEnum<TestEnum>().begin();
+    auto enumEnd=WholeEnum<TestEnum>().end();
+    i=enumBegin;
+    int count=0;
+    while (i != enumEnd) {
+        ++i;
+        ++count;
+        if (count > 20) // prevent infinite loops in test
+            break;
+    }
+    while (i != enumBegin) {
+        --i;
+        ++count;
+        if (count > 20) // prevent infinite loops in test
+            break;
+    }
+    CPPUNIT_ASSERT_EQUAL(10, count);
+
+    --i; //intentional out-of-bounds
+    CPPUNIT_ASSERT(i != enumBegin);
+    CPPUNIT_ASSERT(*i != TestEnum::zero);
+}
+
+void
+testEnumIterator::testRangeFor()
+{
+    int j = 0;
+    for (auto e : WholeEnum<TestEnum>()) {
+        (void)e;
+        ++j;
+        if (j > 20) // prevent infinite loops in test
+            break;
+    }
+    CPPUNIT_ASSERT_EQUAL(5,j);
+}
+
+void
+testEnumIterator::testRangeForRange()
+{
+    int j = 0;
+    // free function-based range
+    for (auto e : EnumRange(TestEnum::two, TestEnum::four)) {
+        (void)e;
+        ++j;
+        if (j > 20) // prevent infinite loops in test
+            break;
+    }
+    CPPUNIT_ASSERT_EQUAL(2,j);
+}
+
+void
+testEnumIterator::testUnsignedEnum()
+{
+    int j = 0;
+    for (auto e = WholeEnum<TestEnum>().rbegin(); e != WholeEnum<TestEnum>().rend(); ++e ) {
+        (void)e;
+        ++j;
+        if (j > 20) // prevent infinite loops in test
+            break;
+    }
+    CPPUNIT_ASSERT_EQUAL(5,j);
+}
+
diff --git a/src/tests/testEnumIterator.h b/src/tests/testEnumIterator.h
new file mode 100644 (file)
index 0000000..78118a3
--- /dev/null
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 1996-2015 The Squid Software Foundation and contributors
+ *
+ * Squid software is distributed under GPLv2+ license and includes
+ * contributions from numerous individuals and organizations.
+ * Please see the COPYING and CONTRIBUTORS files for details.
+ */
+
+#ifndef SQUID_TESTENUMITERATOR_H_
+#define SQUID_TESTENUMITERATOR_H_
+
+#include "base/EnumIterator.h"
+
+#include <cppunit/extensions/HelperMacros.h>
+
+class testEnumIterator : public CPPUNIT_NS::TestFixture
+{
+    CPPUNIT_TEST_SUITE( testEnumIterator );
+    CPPUNIT_TEST( testForwardIter );
+    CPPUNIT_TEST( testReverseIter );
+    CPPUNIT_TEST( testBidirectionalIter );
+    CPPUNIT_TEST( testRangeFor );
+    CPPUNIT_TEST( testRangeForRange );
+    CPPUNIT_TEST( testUnsignedEnum );
+    CPPUNIT_TEST_SUITE_END();
+
+protected:
+    void testForwardIter();
+    void testReverseIter();
+    void testBidirectionalIter();
+    void testRangeFor();
+    void testRangeForRange();
+    void testUnsignedEnum();
+};
+
+#endif /* SQUID_TESTENUMITERATOR_H_ */
+