/*
- * Copyright 2016 Facebook, Inc.
+ * Copyright 2017 Facebook, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
#include <folly/Foreach.h>
-#include <folly/Benchmark.h>
-#include <gtest/gtest.h>
+#include <array>
+#include <initializer_list>
+#include <iterator>
+#include <list>
#include <map>
#include <string>
+#include <tuple>
#include <vector>
-#include <list>
+
+#include <folly/portability/GTest.h>
using namespace folly;
using namespace folly::detail;
+namespace folly {
+namespace test {
+
+class TestRValueConstruct {
+ public:
+ TestRValueConstruct() = default;
+ TestRValueConstruct(TestRValueConstruct&&) noexcept {
+ this->constructed_from_rvalue = true;
+ }
+ TestRValueConstruct(const TestRValueConstruct&) {
+ this->constructed_from_rvalue = false;
+ }
+ TestRValueConstruct& operator=(const TestRValueConstruct&) = delete;
+ TestRValueConstruct& operator=(TestRValueConstruct&&) = delete;
+
+ bool constructed_from_rvalue{false};
+};
+
+class TestAdlIterable {
+ public:
+ std::vector<int> vec{0, 1, 2, 3};
+};
+
+auto begin(TestAdlIterable& instance) {
+ return instance.vec.begin();
+}
+auto begin(const TestAdlIterable& instance) {
+ return instance.vec.begin();
+}
+auto end(TestAdlIterable& instance) {
+ return instance.vec.end();
+}
+auto end(const TestAdlIterable& instance) {
+ return instance.vec.end();
+}
+
+class TestBothIndexingAndIter {
+ public:
+ class Iterator {
+ public:
+ using difference_type = std::size_t;
+ using value_type = int;
+ using pointer = int*;
+ using reference = int&;
+ using iterator_category = std::random_access_iterator_tag;
+ int& operator*() {
+ return this->val;
+ }
+ Iterator operator+(int) {
+ return *this;
+ }
+ explicit Iterator(int& val_in) : val{val_in} {}
+ int& val;
+ };
+ auto begin() {
+ this->called_begin = true;
+ return Iterator{val};
+ }
+ auto end() {
+ return Iterator{val};
+ }
+ int& operator[](int) {
+ return this->val;
+ }
+
+ int val{0};
+ bool called_begin = false;
+};
+} // namespace test
+} // namespace folly
+
+TEST(Foreach, ForEachFunctionBasic) {
+ auto range = std::make_tuple(1, 2, 3);
+ auto result_range = std::vector<int>{};
+ auto correct_result_range = std::vector<int>{1, 2, 3};
+
+ folly::for_each(range, [&](auto ele) { result_range.push_back(ele); });
+
+ EXPECT_TRUE(std::equal(
+ result_range.begin(), result_range.end(), correct_result_range.begin()));
+}
+
+TEST(Foreach, ForEachFunctionBasicRuntimeOneArg) {
+ auto range = std::vector<int>{1, 2, 3};
+ auto current = 0;
+ folly::for_each(range, [&](auto ele) {
+ if (current == 0) {
+ EXPECT_EQ(ele, 1);
+ } else if (current == 1) {
+ EXPECT_EQ(ele, 2);
+ } else {
+ EXPECT_EQ(ele, 3);
+ }
+ ++current;
+ });
+}
+
+TEST(Foreach, ForEachFunctionBasicRuntimeTwoArg) {
+ auto range = std::vector<int>{1, 2, 3};
+ folly::for_each(range, [](auto ele, auto index) {
+ EXPECT_TRUE(index < 3);
+ if (index == 0) {
+ EXPECT_EQ(ele, 1);
+ } else if (index == 1) {
+ EXPECT_EQ(ele, 2);
+ } else if (index == 2) {
+ EXPECT_EQ(ele, 3);
+ }
+ });
+}
+
+TEST(Foreach, ForEachFunctionBasicRuntimeThreeArg) {
+ auto range = std::list<int>{1, 2, 3};
+ auto result_range = std::list<int>{1, 3};
+ folly::for_each(range, [&](auto ele, auto, auto iter) {
+ if (ele == 2) {
+ range.erase(iter);
+ }
+ });
+ EXPECT_TRUE(std::equal(range.begin(), range.end(), result_range.begin()));
+}
+
+TEST(Foreach, ForEachFunctionBasicTupleOneArg) {
+ auto range = std::make_tuple(1, 2, 3);
+ auto current = 0;
+ folly::for_each(range, [&](auto ele) {
+ if (current == 0) {
+ EXPECT_EQ(ele, 1);
+ } else if (current == 1) {
+ EXPECT_EQ(ele, 2);
+ } else {
+ EXPECT_EQ(ele, 3);
+ }
+ ++current;
+ });
+}
+
+TEST(Foreach, ForEachFunctionBasicTupleTwoArg) {
+ auto range = std::make_tuple(1, 2, 3);
+ folly::for_each(range, [](auto ele, auto index) {
+ EXPECT_TRUE(index < 3);
+ if (index == 0) {
+ EXPECT_EQ(ele, 1);
+ } else if (index == 1) {
+ EXPECT_EQ(ele, 2);
+ } else if (index == 2) {
+ EXPECT_EQ(ele, 3);
+ }
+ });
+}
+
+TEST(Foreach, ForEachFunctionBreakRuntimeOneArg) {
+ auto range = std::vector<int>{1, 2, 3};
+ auto iterations = 0;
+ folly::for_each(range, [&](auto) {
+ ++iterations;
+ if (iterations == 1) {
+ return folly::loop_break;
+ }
+ return folly::loop_continue;
+ });
+ EXPECT_EQ(iterations, 1);
+}
+
+TEST(Foreach, ForEachFunctionBreakRuntimeTwoArg) {
+ auto range = std::vector<int>{1, 2, 3};
+ auto iterations = 0;
+ folly::for_each(range, [&](auto, auto index) {
+ ++iterations;
+ if (index == 1) {
+ return folly::loop_break;
+ }
+ return folly::loop_continue;
+ });
+ EXPECT_EQ(iterations, 2);
+}
+
+TEST(Foreach, ForEachFunctionBreakRuntimeThreeArg) {
+ auto range = std::vector<int>{1, 2, 3};
+ auto iterations = 0;
+ folly::for_each(range, [&](auto, auto index, auto) {
+ ++iterations;
+ if (index == 1) {
+ return folly::loop_break;
+ }
+ return folly::loop_continue;
+ });
+ EXPECT_EQ(iterations, 2);
+}
+
+TEST(Foreach, ForEachFunctionBreakTupleOneArg) {
+ auto range = std::vector<int>{1, 2, 3};
+ auto iterations = 0;
+ folly::for_each(range, [&](auto) {
+ ++iterations;
+ if (iterations == 1) {
+ return folly::loop_break;
+ }
+ return folly::loop_continue;
+ });
+ EXPECT_EQ(iterations, 1);
+}
+
+TEST(Foreach, ForEachFunctionBreakTupleTwoArg) {
+ auto range = std::vector<int>{1, 2, 3};
+ auto iterations = 0;
+ folly::for_each(range, [&](auto, auto index) {
+ ++iterations;
+ if (index == 1) {
+ return folly::loop_break;
+ }
+ return folly::loop_continue;
+ });
+ EXPECT_EQ(iterations, 2);
+}
+
+TEST(Foreach, ForEachFunctionArray) {
+ auto range = std::array<int, 3>{{1, 2, 3}};
+ auto iterations = 0;
+ folly::for_each(range, [&](auto, auto index) {
+ ++iterations;
+ if (index == 1) {
+ return folly::loop_break;
+ }
+ return folly::loop_continue;
+ });
+ EXPECT_EQ(iterations, 2);
+}
+
+TEST(Foreach, ForEachFunctionInitializerListBasic) {
+ folly::for_each(std::initializer_list<int>{1, 2, 3}, [](auto ele) { ++ele; });
+}
+
+TEST(Foreach, ForEachFunctionTestForward) {
+ using folly::test::TestRValueConstruct;
+ auto range_one = std::vector<TestRValueConstruct>{};
+ range_one.resize(3);
+
+ folly::for_each(std::move(range_one), [](auto ele) {
+ EXPECT_FALSE(ele.constructed_from_rvalue);
+ });
+
+ folly::for_each(
+ std::make_tuple(TestRValueConstruct{}, TestRValueConstruct{}),
+ [](auto ele) { EXPECT_TRUE(ele.constructed_from_rvalue); });
+}
+
+TEST(Foreach, ForEachFunctionAdlIterable) {
+ auto range = test::TestAdlIterable{};
+ auto iterations = 0;
+ folly::for_each(range, [&](auto ele, auto index) {
+ ++iterations;
+ EXPECT_EQ(ele, index);
+ });
+ EXPECT_EQ(iterations, 4);
+}
+
+TEST(ForEach, FetchRandomAccessIterator) {
+ auto vec = std::vector<int>{1, 2, 3};
+ auto& second = folly::fetch(vec, 1);
+ EXPECT_EQ(second, 2);
+ second = 3;
+ EXPECT_EQ(second, 3);
+}
+
+TEST(ForEach, FetchIndexing) {
+ auto mp = std::map<int, int>{{1, 2}};
+ auto& ele = folly::fetch(mp, 1);
+ EXPECT_EQ(ele, 2);
+ ele = 3;
+ EXPECT_EQ(ele, 3);
+}
+
+TEST(ForEach, FetchTuple) {
+ auto mp = std::make_tuple(1, 2, 3);
+ auto& ele = folly::fetch(mp, std::integral_constant<int, 1>{});
+ EXPECT_EQ(ele, 2);
+ ele = 3;
+ EXPECT_EQ(ele, 3);
+}
+
+TEST(ForEach, FetchTestPreferIterator) {
+ auto range = test::TestBothIndexingAndIter{};
+ auto& ele = folly::fetch(range, 0);
+ EXPECT_TRUE(range.called_begin);
+ EXPECT_EQ(ele, 0);
+ ele = 2;
+ EXPECT_EQ(folly::fetch(range, 0), 2);
+}
+
TEST(Foreach, ForEachRvalue) {
const char* const hello = "hello";
int n = 0;
EXPECT_EQ(0, n);
}
+TEST(Foreach, ForEachNested) {
+ const std::string hello = "hello";
+ size_t n = 0;
+ FOR_EACH(i, hello) {
+ FOR_EACH(j, hello) {
+ ++n;
+ }
+ }
+ auto len = hello.size();
+ EXPECT_EQ(len * len, n);
+}
+
TEST(Foreach, ForEachKV) {
std::map<std::string, int> testMap;
testMap["abc"] = 1;
}
EXPECT_EQ(10, sum);
}
-
-// Benchmarks:
-// 1. Benchmark iterating through the man with FOR_EACH, and also assign
-// iter->first and iter->second to local vars inside the FOR_EACH loop.
-// 2. Benchmark iterating through the man with FOR_EACH, but use iter->first and
-// iter->second as is, without assigning to local variables.
-// 3. Use FOR_EACH_KV loop to iterate through the map.
-
-std::map<int, std::string> bmMap; // For use in benchmarks below.
-
-void setupBenchmark(size_t iters) {
- bmMap.clear();
- for (size_t i = 0; i < iters; ++i) {
- bmMap[i] = "teststring";
- }
-}
-
-BENCHMARK(ForEachKVNoMacroAssign, iters) {
- int sumKeys = 0;
- std::string sumValues;
-
- BENCHMARK_SUSPEND {
- setupBenchmark(iters);
- }
-
- FOR_EACH (iter, bmMap) {
- const int k = iter->first;
- const std::string v = iter->second;
- sumKeys += k;
- sumValues += v;
- }
-}
-
-BENCHMARK(ForEachKVNoMacroNoAssign, iters) {
- int sumKeys = 0;
- std::string sumValues;
-
- BENCHMARK_SUSPEND {
- setupBenchmark(iters);
- }
-
- FOR_EACH (iter, bmMap) {
- sumKeys += iter->first;
- sumValues += iter->second;
- }
-}
-
-BENCHMARK(ManualLoopNoAssign, iters) {
- int sumKeys = 0;
- std::string sumValues;
-
- BENCHMARK_SUSPEND {
- setupBenchmark(iters);
- }
-
- for (auto iter = bmMap.begin(); iter != bmMap.end(); ++iter) {
- sumKeys += iter->first;
- sumValues += iter->second;
- }
-}
-
-BENCHMARK(ForEachKVMacro, iters) {
- int sumKeys = 0;
- std::string sumValues;
-
- BENCHMARK_SUSPEND {
- setupBenchmark(iters);
- }
-
- FOR_EACH_KV (k, v, bmMap) {
- sumKeys += k;
- sumValues += v;
- }
-}
-
-BENCHMARK(ForEachManual, iters) {
- int sum = 1;
- for (size_t i = 1; i < iters; ++i) {
- sum *= i;
- }
- doNotOptimizeAway(sum);
-}
-
-BENCHMARK(ForEachRange, iters) {
- int sum = 1;
- FOR_EACH_RANGE (i, 1, iters) {
- sum *= i;
- }
- doNotOptimizeAway(sum);
-}
-
-BENCHMARK(ForEachDescendingManual, iters) {
- int sum = 1;
- for (size_t i = iters; i-- > 1; ) {
- sum *= i;
- }
- doNotOptimizeAway(sum);
-}
-
-BENCHMARK(ForEachRangeR, iters) {
- int sum = 1;
- FOR_EACH_RANGE_R (i, 1U, iters) {
- sum *= i;
- }
- doNotOptimizeAway(sum);
-}
-
-int main(int argc, char** argv) {
- testing::InitGoogleTest(&argc, argv);
- auto r = RUN_ALL_TESTS();
- if (r) {
- return r;
- }
- runBenchmarks();
- return 0;
-}