From: Francesco Chemolli Date: Sat, 5 Sep 2015 11:28:21 +0000 (+0200) Subject: Implement EnumIterator X-Git-Tag: SQUID_4_0_1~72 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=2fefc43ca061bd3b427d5d8c0f81e3c2a049b6f6;p=thirdparty%2Fsquid.git Implement EnumIterator --- diff --git a/acinclude/ax_cxx_0x_types.m4 b/acinclude/ax_cxx_0x_types.m4 index 270d378e2f..426a7fe5cd 100644 --- a/acinclude/ax_cxx_0x_types.m4 +++ b/acinclude/ax_cxx_0x_types.m4 @@ -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::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 +enum class testEnum { one, two, three }; + ],[ + std::underlying_type::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]) +]) diff --git a/configure.ac b/configure.ac index d0c55fed2b..1f56a26882 100644 --- a/configure.ac +++ b/configure.ac @@ -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. diff --git a/src/Makefile.am b/src/Makefile.am index 8a9d04aefc..8593d17efa 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -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 index 0000000000..33d527f30d --- /dev/null +++ b/src/base/EnumIterator.h @@ -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 +#include + +/** 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 +class EnumIteratorBase : public std::iterator +{ +protected: +#if HAVE_STD_UNDERLYING_TYPE + typedef typename std::underlying_type::type iterator_type; +#else + typedef int iterator_type; +#endif + +public: + explicit EnumIteratorBase(EnumType e) : current(static_cast(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(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 +class EnumIterator : public EnumIteratorBase +{ +public: + explicit EnumIterator(EnumType e) : EnumIteratorBase(e) {} + + EnumIterator& operator++() { + ++ EnumIteratorBase::current; + return *this; + } + + EnumIterator& operator++(int) { + EnumIterator rv(*this); + ++ EnumIteratorBase::current; + return rv; + } + + EnumIterator& operator--() { + -- EnumIteratorBase::current; + return *this; + } + + EnumIterator& operator--(int) { + EnumIterator rv(*this); + -- EnumIteratorBase::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 +class ReverseEnumIterator : public EnumIteratorBase +{ +public: + explicit ReverseEnumIterator(EnumType e) : EnumIteratorBase(e) {} + + // prefix increment + ReverseEnumIterator& operator++() { + -- EnumIteratorBase::current; + return *this; + } + + // postfix increment + ReverseEnumIterator& operator++(int) { + ReverseEnumIterator rv(*this); + -- EnumIteratorBase::current; + return rv; + } + + // prefix decrement + ReverseEnumIterator& operator--() { + ++ EnumIteratorBase::current; + return *this; + } + + // postfix decrement + ReverseEnumIterator& operator--(int) { + ReverseEnumIterator rv(*this); + ++ EnumIteratorBase::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(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 +class EnumRangeT +{ +public: + typedef EnumIterator iterator; + typedef ReverseEnumIterator 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 +EnumRangeT EnumRange(EnumType begin, EnumType one_past_end) +{ + return EnumRangeT(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()) { + * do_stuff(); + * } + * \endcode + */ +template +class WholeEnum : public EnumRangeT +{ +public: + WholeEnum() : EnumRangeT(EnumType::enumBegin_, EnumType::enumEnd_) {} +}; + +#endif /* SQUID_BASE_ENUMITERATOR_H */ + diff --git a/src/base/Makefile.am b/src/base/Makefile.am index 2b33f2d9de..23086ae5e4 100644 --- a/src/base/Makefile.am +++ b/src/base/Makefile.am @@ -24,6 +24,7 @@ libbase_la_SOURCES = \ CbDataList.h \ CharacterSet.h \ CharacterSet.cc \ + EnumIterator.h \ InstanceId.h \ Lock.h \ LookupTable.h \ diff --git a/src/icp_opcode.h b/src/icp_opcode.h index 0e6927ae10..967a64a7dc 100644 --- a/src/icp_opcode.h +++ b/src/icp_opcode.h @@ -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[]; diff --git a/src/icp_v2.cc b/src/icp_v2.cc index 27b3726ec2..65cc8b8676 100644 --- a/src/icp_v2.cc +++ b/src/icp_v2.cc @@ -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(icp_opcode::ICP_END)) return ICP_INVALID; - return (icp_opcode)opcode; + return static_cast(opcode); } /* ICPState */ diff --git a/src/neighbors.cc b/src/neighbors.cc index 093b5ee7d4..627dc5c5af 100644 --- a/src/neighbors.cc +++ b/src/neighbors.cc @@ -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()) { if (e->icp.counts[op] == 0) continue; diff --git a/src/tests/testEnumIterator.cc b/src/tests/testEnumIterator.cc new file mode 100644 index 0000000000..3200b6e10b --- /dev/null +++ b/src/tests/testEnumIterator.cc @@ -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_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::iterator i = WholeEnum().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().end()); +} + +void +testEnumIterator::testReverseIter() +{ + WholeEnum::reverse_iterator i = WholeEnum().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().rend()); +} + +void +testEnumIterator::testBidirectionalIter() +{ + WholeEnum::iterator i = WholeEnum().begin(); + CPPUNIT_ASSERT(*i == TestEnum::zero); + ++i; + CPPUNIT_ASSERT(*i == TestEnum::one); + --i; + CPPUNIT_ASSERT(*i == TestEnum::zero); + + auto enumBegin=WholeEnum().begin(); + auto enumEnd=WholeEnum().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()) { + (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().rbegin(); e != WholeEnum().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 index 0000000000..78118a3601 --- /dev/null +++ b/src/tests/testEnumIterator.h @@ -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 + +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_ */ +