v8/test/unittests/base/template-utils-unittest.cc

Ignoring revisions in .git-blame-ignore-revs. Click here to bypass and see the normal blame view.

170 lines
6.1 KiB
C++
Raw Normal View History

// 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 {};
Make v8 build with -Wmicrosoft-cast under clang-cl. gcc and clang (and the standard) don't allow implicit conversion of function pointers to object pointers. MSVC does allow that, and since system headers require this to work, clang-cl allows it too -- but it emits a -Wmicrosoft-cast warning (which we currently suppress in the Chromium build, but which we want to enable.) As a side effect, when printing a function pointer to a stream, MSVC (and clang-cl) will pick the operator<<(void*) overload, while gcc and clang will pick operator<<(bool) since the best allowed conversion they find is from function pointer to bool. To prevent the clang-cl warning, we need to make sure that we never directly print a function pointer to a stream. In v8, this requires two changes: 1. Give PrintCheckOperand() an explicit specialization for function pointers and explicitly cast to void* there. This ports https://codereview.chromium.org/2515283002/ to V8, and also fixes a bug on non-Windows where DCHECK() of function pointers would print "(1 vs 1)" instead of the function's addresses. (The bug remains with member function pointers, where it's not clear what to print instead of the 1.) 2. has_output_operator<T> must not use operator<< on its argument in an evaluated context if T is a function pointer. This patch modifies has_output_operator<> to use an unevaluated context instead, which is simpler than the current approach (and matches what Chromium's base does), but changes behavior in minor (boring) ways (see template-utils-unittest.cc), since operator<<() is now called with a temporary and only operator<<() implementations callable with a temporary are considered. A more complicated but behavior-preserving alternative would be to add an explicit specialization for function pointers. You can see this variant in patch set 1 on gerrit. Bug: chromium:550065 Change-Id: Idc2854d6c258b7fc0b959604006d8952a79eca3d Reviewed-on: https://chromium-review.googlesource.com/940004 Commit-Queue: Nico Weber <thakis@chromium.org> Reviewed-by: Clemens Hammacher <clemensh@chromium.org> Cr-Commit-Position: refs/heads/master@{#51636}
2018-02-28 16:11:12 +00:00
extern std::ostream& operator<<(std::ostream& str, const TestClass2&);
class TestClass3 {};
extern std::ostream& operator<<(std::ostream& str, TestClass3);
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");
Make v8 build with -Wmicrosoft-cast under clang-cl. gcc and clang (and the standard) don't allow implicit conversion of function pointers to object pointers. MSVC does allow that, and since system headers require this to work, clang-cl allows it too -- but it emits a -Wmicrosoft-cast warning (which we currently suppress in the Chromium build, but which we want to enable.) As a side effect, when printing a function pointer to a stream, MSVC (and clang-cl) will pick the operator<<(void*) overload, while gcc and clang will pick operator<<(bool) since the best allowed conversion they find is from function pointer to bool. To prevent the clang-cl warning, we need to make sure that we never directly print a function pointer to a stream. In v8, this requires two changes: 1. Give PrintCheckOperand() an explicit specialization for function pointers and explicitly cast to void* there. This ports https://codereview.chromium.org/2515283002/ to V8, and also fixes a bug on non-Windows where DCHECK() of function pointers would print "(1 vs 1)" instead of the function's addresses. (The bug remains with member function pointers, where it's not clear what to print instead of the 1.) 2. has_output_operator<T> must not use operator<< on its argument in an evaluated context if T is a function pointer. This patch modifies has_output_operator<> to use an unevaluated context instead, which is simpler than the current approach (and matches what Chromium's base does), but changes behavior in minor (boring) ways (see template-utils-unittest.cc), since operator<<() is now called with a temporary and only operator<<() implementations callable with a temporary are considered. A more complicated but behavior-preserving alternative would be to add an explicit specialization for function pointers. You can see this variant in patch set 1 on gerrit. Bug: chromium:550065 Change-Id: Idc2854d6c258b7fc0b959604006d8952a79eca3d Reviewed-on: https://chromium-review.googlesource.com/940004 Commit-Queue: Nico Weber <thakis@chromium.org> Reviewed-by: Clemens Hammacher <clemensh@chromium.org> Cr-Commit-Position: refs/heads/master@{#51636}
2018-02-28 16:11:12 +00:00
static_assert(has_output_operator<const TestClass2>::value,
"const TestClass2 can be output");
static_assert(has_output_operator<TestClass3>::value,
"non-const TestClass3 can be output");
static_assert(has_output_operator<const TestClass3>::value,
"const TestClass3 can 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 = std::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