| // Copyright 2017 the V8 project authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "src/base/template-utils.h" |
| |
| #include "test/unittests/test-utils.h" |
| |
| namespace v8 { |
| namespace base { |
| namespace template_utils_unittest { |
| |
| //////////////////////////// |
| // Test make_array. |
| //////////////////////////// |
| |
| namespace { |
| template <typename T, size_t Size> |
| void CheckArrayEquals(const std::array<T, Size>& arr1, |
| const std::array<T, Size>& arr2) { |
| for (size_t i = 0; i < Size; ++i) { |
| CHECK_EQ(arr1[i], arr2[i]); |
| } |
| } |
| } // namespace |
| |
| TEST(TemplateUtilsTest, MakeArraySimple) { |
| auto computed_array = base::make_array<3>([](int i) { return 1 + (i * 3); }); |
| std::array<int, 3> expected{{1, 4, 7}}; |
| CheckArrayEquals(computed_array, expected); |
| } |
| |
| namespace { |
| constexpr int doubleIntValue(int i) { return i * 2; } |
| }; // namespace |
| |
| TEST(TemplateUtilsTest, MakeArrayConstexpr) { |
| constexpr auto computed_array = base::make_array<3>(doubleIntValue); |
| constexpr std::array<int, 3> expected{{0, 2, 4}}; |
| CheckArrayEquals(computed_array, expected); |
| } |
| |
| //////////////////////////// |
| // Test pass_value_or_ref. |
| //////////////////////////// |
| |
| // Wrap into this helper struct, such that the type is printed on errors. |
| template <typename T1, typename T2> |
| struct CheckIsSame { |
| static_assert(std::is_same<T1, T2>::value, "test failure"); |
| }; |
| |
| #define TEST_PASS_VALUE_OR_REF0(remove_extend, expected, given) \ |
| static_assert( \ |
| sizeof(CheckIsSame<expected, \ |
| pass_value_or_ref<given, remove_extend>::type>) > 0, \ |
| "check") |
| |
| #define TEST_PASS_VALUE_OR_REF(expected, given) \ |
| static_assert( \ |
| sizeof(CheckIsSame<expected, pass_value_or_ref<given>::type>) > 0, \ |
| "check") |
| |
| TEST_PASS_VALUE_OR_REF(int, int&); |
| TEST_PASS_VALUE_OR_REF(int, int&&); |
| TEST_PASS_VALUE_OR_REF(const char*, const char[14]); |
| TEST_PASS_VALUE_OR_REF(const char*, const char*&&); |
| TEST_PASS_VALUE_OR_REF(const char*, const char (&)[14]); |
| TEST_PASS_VALUE_OR_REF(const std::string&, std::string); |
| TEST_PASS_VALUE_OR_REF(const std::string&, std::string&); |
| TEST_PASS_VALUE_OR_REF(const std::string&, const std::string&); |
| TEST_PASS_VALUE_OR_REF(int, const int); |
| TEST_PASS_VALUE_OR_REF(int, const int&); |
| TEST_PASS_VALUE_OR_REF(const int*, const int*); |
| TEST_PASS_VALUE_OR_REF(const int*, const int* const); |
| TEST_PASS_VALUE_OR_REF0(false, const char[14], const char[14]); |
| TEST_PASS_VALUE_OR_REF0(false, const char[14], const char (&)[14]); |
| TEST_PASS_VALUE_OR_REF0(false, const std::string&, std::string); |
| TEST_PASS_VALUE_OR_REF0(false, const std::string&, std::string&); |
| TEST_PASS_VALUE_OR_REF0(false, const std::string&, const std::string&); |
| TEST_PASS_VALUE_OR_REF0(false, int, const int); |
| TEST_PASS_VALUE_OR_REF0(false, int, const int&); |
| |
| ////////////////////////////// |
| // Test has_output_operator. |
| ////////////////////////////// |
| |
| // Intrinsic types: |
| static_assert(has_output_operator<int>::value, "int can be output"); |
| static_assert(has_output_operator<void*>::value, "void* can be output"); |
| static_assert(has_output_operator<uint64_t>::value, "int can be output"); |
| |
| // Classes: |
| class TestClass1 {}; |
| class TestClass2 {}; |
| extern std::ostream& operator<<(std::ostream& str, TestClass2&); |
| static_assert(!has_output_operator<TestClass1>::value, |
| "TestClass1 can not be output"); |
| static_assert(has_output_operator<TestClass2>::value, |
| "non-const TestClass2 can be output"); |
| static_assert(!has_output_operator<const TestClass2>::value, |
| "const TestClass2 can not be output"); |
| |
| ////////////////////////////// |
| // Test fold. |
| ////////////////////////////// |
| |
| struct FoldAllSameType { |
| constexpr uint32_t operator()(uint32_t a, uint32_t b) const { return a | b; } |
| }; |
| static_assert(base::fold(FoldAllSameType{}, 3, 6) == 7, "check fold"); |
| // Test that it works if implicit conversion is needed for one of the |
| // parameters. |
| static_assert(base::fold(FoldAllSameType{}, uint8_t{1}, 256) == 257, |
| "check correct type inference"); |
| // Test a single parameter. |
| static_assert(base::fold(FoldAllSameType{}, 25) == 25, |
| "check folding a single argument"); |
| |
| TEST(TemplateUtilsTest, FoldDifferentType) { |
| auto fn = [](std::string str, char c) { |
| str.push_back(c); |
| return str; |
| }; |
| CHECK_EQ(base::fold(fn, std::string("foo"), 'b', 'a', 'r'), "foobar"); |
| } |
| |
| TEST(TemplateUtilsTest, FoldMoveOnlyType) { |
| auto fn = [](std::unique_ptr<std::string> str, char c) { |
| str->push_back(c); |
| return str; |
| }; |
| std::unique_ptr<std::string> str = base::make_unique<std::string>("foo"); |
| std::unique_ptr<std::string> folded = |
| base::fold(fn, std::move(str), 'b', 'a', 'r'); |
| CHECK_NULL(str); |
| CHECK_NOT_NULL(folded); |
| CHECK_EQ(*folded, "foobar"); |
| } |
| |
| struct TemplatizedFoldFunctor { |
| template <typename T, typename... Tup> |
| std::tuple<Tup..., typename std::decay<T>::type> operator()( |
| std::tuple<Tup...> tup, T&& val) { |
| return std::tuple_cat(std::move(tup), |
| std::make_tuple(std::forward<T>(val))); |
| } |
| }; |
| TEST(TemplateUtilsTest, FoldToTuple) { |
| auto input = std::make_tuple(char{'x'}, int{4}, double{3.2}, |
| std::unique_ptr<uint8_t>{}, std::string{"foo"}); |
| auto result = |
| base::fold(TemplatizedFoldFunctor{}, std::make_tuple(), |
| std::get<0>(input), std::get<1>(input), std::get<2>(input), |
| std::unique_ptr<uint8_t>{}, std::get<4>(input)); |
| static_assert(std::is_same<decltype(result), decltype(input)>::value, |
| "the resulting tuple should have the same type as the input"); |
| DCHECK_EQ(input, result); |
| } |
| |
| } // namespace template_utils_unittest |
| } // namespace base |
| } // namespace v8 |