From f874a19948386469110763d636331167de41869c Mon Sep 17 00:00:00 2001 From: Maxim Pimenov Date: Fri, 1 Dec 2017 16:38:54 +0300 Subject: [PATCH] [base] ControlFlowWrapper. --- base/base_tests/CMakeLists.txt | 1 + base/base_tests/base_tests.pro | 1 + base/base_tests/control_flow_tests.cpp | 57 +++++++++++++++++++++++ base/control_flow.hpp | 34 ++++++++++++++ coding/multilang_utf8_string.hpp | 24 ++-------- xcode/base/base.xcodeproj/project.pbxproj | 4 ++ 6 files changed, 101 insertions(+), 20 deletions(-) create mode 100644 base/base_tests/control_flow_tests.cpp diff --git a/base/base_tests/CMakeLists.txt b/base/base_tests/CMakeLists.txt index 910aeaa0fc..cc622c2f55 100644 --- a/base/base_tests/CMakeLists.txt +++ b/base/base_tests/CMakeLists.txt @@ -13,6 +13,7 @@ set( collection_cast_test.cpp condition_test.cpp containers_test.cpp + control_flow_tests.cpp levenshtein_dfa_test.cpp logging_test.cpp math_test.cpp diff --git a/base/base_tests/base_tests.pro b/base/base_tests/base_tests.pro index 44cf6b8921..d1b5df589d 100644 --- a/base/base_tests/base_tests.pro +++ b/base/base_tests/base_tests.pro @@ -23,6 +23,7 @@ SOURCES += \ collection_cast_test.cpp \ condition_test.cpp \ containers_test.cpp \ + control_flow_tests.cpp \ levenshtein_dfa_test.cpp \ logging_test.cpp \ newtype_test.cpp \ diff --git a/base/base_tests/control_flow_tests.cpp b/base/base_tests/control_flow_tests.cpp new file mode 100644 index 0000000000..ba50886996 --- /dev/null +++ b/base/base_tests/control_flow_tests.cpp @@ -0,0 +1,57 @@ +#include "testing/testing.hpp" + +#include "base/control_flow.hpp" + +#include + +using namespace base; + +namespace +{ +struct Repeater +{ + explicit Repeater(uint32_t repetitions) : m_repetitions(repetitions) {} + + template + void ForEach(Fn && fn) + { + ControlFlowWrapper wrapper(std::forward(fn)); + for (uint32_t i = 0; i < m_repetitions; ++i) + { + ++m_calls; + if (wrapper() == ControlFlow::Break) + break; + } + } + + uint32_t m_repetitions = 0; + uint32_t m_calls = 0; +}; +} // namespace + +UNIT_TEST(ControlFlow_Smoke) +{ + { + Repeater repeater(10); + uint32_t c = 0; + repeater.ForEach([&c] { ++c; }); + + TEST_EQUAL(c, 10, ()); + TEST_EQUAL(c, repeater.m_repetitions, ()); + TEST_EQUAL(c, repeater.m_calls, ()); + } + + { + Repeater repeater(10); + uint32_t c = 0; + repeater.ForEach([&c] { + ++c; + if (c == 5) + return ControlFlow::Break; + return ControlFlow::Continue; + }); + + TEST_EQUAL(c, 5, ()); + TEST_EQUAL(c, repeater.m_calls, ()); + } +} diff --git a/base/control_flow.hpp b/base/control_flow.hpp index 38b5358478..7695a95a51 100644 --- a/base/control_flow.hpp +++ b/base/control_flow.hpp @@ -1,5 +1,7 @@ #pragma once +#include + namespace base { // This enum is used to control the flow of ForEach invocations. @@ -8,4 +10,36 @@ enum class ControlFlow Break, Continue }; + +// A wrapper that calls |fn| with arguments |args|. +// To avoid excessive calls, |fn| may signal the end of execution via its return value, +// which should then be checked by the wrapper's user. +template +struct ControlFlowWrapper +{ + template + explicit ControlFlowWrapper(Gn && gn) : m_fn(std::forward(gn)) + { + } + + template + typename std::enable_if< + std::is_same::type, base::ControlFlow>::value, + base::ControlFlow>::type + operator()(Args &&... args) + { + return m_fn(std::forward(args)...); + } + + template + typename std::enable_if::type, void>::value, + base::ControlFlow>::type + operator()(Args &&... args) + { + m_fn(std::forward(args)...); + return ControlFlow::Continue; + } + + Fn m_fn; +}; } // namespace base diff --git a/coding/multilang_utf8_string.hpp b/coding/multilang_utf8_string.hpp index e89f9b495d..f30f29cda2 100644 --- a/coding/multilang_utf8_string.hpp +++ b/coding/multilang_utf8_string.hpp @@ -88,37 +88,21 @@ public: AddString(l, utf8s); } - // Calls |fn| for each pair of |lang| and |utf8s| stored in this multilang string. - // To avoid excessive calls, |fn| may signal the end of execution via its return value. template - typename enable_if< - is_same::type, base::ControlFlow>::value, - void>::type - ForEach(Fn && fn) const + void ForEach(Fn && fn) const { size_t i = 0; size_t const sz = m_s.size(); + base::ControlFlowWrapper wrapper(std::forward(fn)); while (i < sz) { size_t const next = GetNextIndex(i); - if (fn((m_s[i] & 0x3F), m_s.substr(i + 1, next - i - 1)) == base::ControlFlow::Break) - return; + if (wrapper((m_s[i] & 0x3F), m_s.substr(i + 1, next - i - 1)) == base::ControlFlow::Break) + break; i = next; } } - // Calls |fn| for each pair of |lang| and |utf8s| stored in this multilang string. - template - typename enable_if::type, void>::value, - void>::type - ForEach(Fn && fn) const - { - ForEach([&](int8_t lang, std::string utf8s) { - fn(lang, utf8s); - return base::ControlFlow::Continue; - }); - } - bool GetString(int8_t lang, string & utf8s) const; bool GetString(string const & lang, string & utf8s) const { diff --git a/xcode/base/base.xcodeproj/project.pbxproj b/xcode/base/base.xcodeproj/project.pbxproj index 3bdf2f0098..89260c7c8e 100644 --- a/xcode/base/base.xcodeproj/project.pbxproj +++ b/xcode/base/base.xcodeproj/project.pbxproj @@ -16,6 +16,7 @@ 3446C67D1DDCAA4900146687 /* uni_string_dfa_test.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 3446C6791DDCAA2500146687 /* uni_string_dfa_test.cpp */; }; 3446C6821DDCAA7400146687 /* newtype_test.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 3446C67E1DDCAA6E00146687 /* newtype_test.cpp */; }; 3446C6831DDCAA7800146687 /* ref_counted_tests.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 3446C67F1DDCAA6E00146687 /* ref_counted_tests.cpp */; }; + 39B89C3A1FD1898A001104AF /* control_flow_tests.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 39B89C391FD1898A001104AF /* control_flow_tests.cpp */; }; 39BC0FD01FD057FA00B6C276 /* control_flow.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 39BC0FCF1FD057F900B6C276 /* control_flow.hpp */; }; 39FD271E1CC65AD000AFF551 /* testingmain.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 39FD27011CC65A2800AFF551 /* testingmain.cpp */; }; 39FD271F1CC65AD000AFF551 /* assert_test.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 39FD26C81CC65A0E00AFF551 /* assert_test.cpp */; }; @@ -143,6 +144,7 @@ 3446C67F1DDCAA6E00146687 /* ref_counted_tests.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = ref_counted_tests.cpp; sourceTree = ""; }; 34BA2D6A1DBE169E00FAB345 /* common-debug.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = "common-debug.xcconfig"; path = "../common-debug.xcconfig"; sourceTree = ""; }; 34BA2D6B1DBE169E00FAB345 /* common-release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = "common-release.xcconfig"; path = "../common-release.xcconfig"; sourceTree = ""; }; + 39B89C391FD1898A001104AF /* control_flow_tests.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = control_flow_tests.cpp; sourceTree = ""; }; 39BC0FCF1FD057F900B6C276 /* control_flow.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = control_flow.hpp; sourceTree = ""; }; 39FD26C81CC65A0E00AFF551 /* assert_test.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = assert_test.cpp; sourceTree = ""; }; 39FD26CA1CC65A0E00AFF551 /* bits_test.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = bits_test.cpp; sourceTree = ""; }; @@ -286,6 +288,7 @@ 39FD26C71CC659D200AFF551 /* base_tests */ = { isa = PBXGroup; children = ( + 39B89C391FD1898A001104AF /* control_flow_tests.cpp */, 67E40EC71E4DC0D500A6D200 /* small_set_test.cpp */, 3446C67E1DDCAA6E00146687 /* newtype_test.cpp */, 3446C67F1DDCAA6E00146687 /* ref_counted_tests.cpp */, @@ -674,6 +677,7 @@ 3D3731FE1F9A445500D2121B /* url_helpers.cpp in Sources */, 675341F91A3F57E400A0A8C3 /* src_point.cpp in Sources */, 675342031A3F57E400A0A8C3 /* strings_bundle.cpp in Sources */, + 39B89C3A1FD1898A001104AF /* control_flow_tests.cpp in Sources */, 3D74EF141F8B902C0081202C /* bwt.cpp in Sources */, 3D74EF131F8B902C0081202C /* move_to_front.cpp in Sources */, 675341CD1A3F57E400A0A8C3 /* base.cpp in Sources */,