From 70da3acb70078758d97c955c029b747efddefcd7 Mon Sep 17 00:00:00 2001 From: Paul Fultz II Date: Wed, 28 Mar 2018 21:51:22 -0500 Subject: [PATCH] Add foreach emulation for older compilers (#1138) --- lib/foreach.h | 91 ++++++++++++++++++++++++ test/testforeach.cpp | 160 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 251 insertions(+) create mode 100644 lib/foreach.h create mode 100644 test/testforeach.cpp diff --git a/lib/foreach.h b/lib/foreach.h new file mode 100644 index 000000000..abcca6ec7 --- /dev/null +++ b/lib/foreach.h @@ -0,0 +1,91 @@ +/* + * Cppcheck - A tool for static C/C++ code analysis + * Copyright (C) 2018 Cppcheck team. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + + +//--------------------------------------------------------------------------- +#ifndef foreachH +#define foreachH +//--------------------------------------------------------------------------- + +#include + +#ifndef CPPCHECK_HAS_RANGE_FOR +#if (defined(__GNUC__) && !defined (__clang__) && __GNUC__ == 4 && __GNUC_MINOR__ < 6) || defined(_MSC_VER) +#define CPPCHECK_HAS_RANGE_FOR 0 +#else +#define CPPCHECK_HAS_RANGE_FOR 1 +#endif +#endif + +#if !CPPCHECK_HAS_RANGE_FOR + +template +typename Range::iterator range_begin(Range& r) +{ + return r.begin(); +} + +template +typename Range::iterator range_end(Range& r) +{ + return r.end(); +} + +template +typename Range::const_iterator range_begin(const Range& r) +{ + return r.begin(); +} + +template +typename Range::const_iterator range_end(const Range& r) +{ + return r.end(); +} + +template< class T, std::size_t N > +T* range_begin( T (&array)[N] ) +{ + return array; +} + +template< class T, std::size_t N > +T* range_end( T (&array)[N] ) +{ + return array+N; +} + +#define CPPCHECK_PRIVATE_VAR_CAT_IMPL(x, y) x ## y +#define CPPCHECK_PRIVATE_VAR_CAT(x, y) CPPCHECK_PRIVATE_VAR_CAT_IMPL(x, y) +#define CPPCHECK_PRIVATE_VAR(x) CPPCHECK_PRIVATE_VAR_CAT(_cppcheck_ ## x, __LINE__) + +#define FOREACH(VAR, ...) \ + if(bool CPPCHECK_PRIVATE_VAR(done) = false) {} \ + else for(auto && CPPCHECK_PRIVATE_VAR(rng) = (__VA_ARGS__); !CPPCHECK_PRIVATE_VAR(done);) \ + for(auto CPPCHECK_PRIVATE_VAR(begin) = range_begin(CPPCHECK_PRIVATE_VAR(rng)); !CPPCHECK_PRIVATE_VAR(done); \ + CPPCHECK_PRIVATE_VAR(done) = true) \ + for(auto CPPCHECK_PRIVATE_VAR(end) = range_end(CPPCHECK_PRIVATE_VAR(rng)); \ + !CPPCHECK_PRIVATE_VAR(done) && CPPCHECK_PRIVATE_VAR(begin) != CPPCHECK_PRIVATE_VAR(end); ++CPPCHECK_PRIVATE_VAR(begin)) \ + if(!(CPPCHECK_PRIVATE_VAR(done) = true)) {} \ + else for(VAR = *CPPCHECK_PRIVATE_VAR(begin); CPPCHECK_PRIVATE_VAR(done); CPPCHECK_PRIVATE_VAR(done) = false) \ + +#else +#define FOREACH(VAR, ...) for(VAR : (__VA_ARGS__)) +#endif + +#endif diff --git a/test/testforeach.cpp b/test/testforeach.cpp new file mode 100644 index 000000000..4b0100afc --- /dev/null +++ b/test/testforeach.cpp @@ -0,0 +1,160 @@ +/* + * Cppcheck - A tool for static C/C++ code analysis + * Copyright (C) 2007-2017 Cppcheck team. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "foreach.h" +#include "testsuite.h" +#include + + +class TestForeach : public TestFixture { +public: + TestForeach() + : TestFixture("TestForeach"), getVectorOnceCount(0) + {} + +private: + + int getVectorOnceCount; + const std::vector& getVectorOnce() + { + static std::vector v(5, 2); + getVectorOnceCount++; + return v; + } + + std::vector getVector() + { + std::vector v(5, 2); + return v; + } + + void run() { + TEST_CASE(container); + TEST_CASE(rvalue); + TEST_CASE(checkBreak); + TEST_CASE(checkContinue); + TEST_CASE(arrays); + TEST_CASE(callOnce); + TEST_CASE(nested); + } + + void container() { + std::vector v(5, 2); + int total = 0; + int count = 0; + + FOREACH(int x, v) { + count++; + total += x; + } + + ASSERT_EQUALS(total, 10); + ASSERT_EQUALS(count, 5); + } + + void rvalue() { + int total = 0; + int count = 0; + + FOREACH(int x, getVector()) { + count++; + total += x; + } + + ASSERT_EQUALS(total, 10); + ASSERT_EQUALS(count, 5); + } + + void checkBreak() { + std::vector v(5, 2); + int total = 0; + int count = 0; + + FOREACH(int x, v) { + count++; + if (count == 3) break; + total += x; + } + + ASSERT_EQUALS(total, 4); + ASSERT_EQUALS(count, 3); + } + + void checkContinue() { + std::vector v(5, 2); + int total = 0; + int count = 0; + + FOREACH(int x, v) { + count++; + if (count == 3) continue; + total += x; + } + + ASSERT_EQUALS(total, 8); + ASSERT_EQUALS(count, 5); + } + + void arrays() { + int arr[] = {2, 2, 2, 2, 2}; + int total = 0; + int count = 0; + + FOREACH(int x, arr) { + count++; + total += x; + } + + ASSERT_EQUALS(total, 10); + ASSERT_EQUALS(count, 5); + } + + void callOnce() { + int total = 0; + int count = 0; + + FOREACH(int x, getVectorOnce()) { + count++; + total += x; + } + + ASSERT_EQUALS(total, 10); + ASSERT_EQUALS(count, 5); + ASSERT_EQUALS(getVectorOnceCount, 1); + } + + void nested() { + std::vector v(5, 2); + int total = 0; + int count = 0; + + FOREACH(int x, v) { + FOREACH(int y, v) { + count++; + total += x; + (void)y; + } + } + + ASSERT_EQUALS(total, 50); + ASSERT_EQUALS(count, 25); + } + +}; + +REGISTER_TEST(TestForeach)