2017-08-07 10:34:47 +00:00
|
|
|
// 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 {
|
2017-09-28 17:55:52 +00:00
|
|
|
namespace template_utils_unittest {
|
2017-08-07 10:34:47 +00:00
|
|
|
|
|
|
|
////////////////////////////
|
|
|
|
// 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; }
|
2019-02-13 00:33:17 +00:00
|
|
|
} // namespace
|
2017-08-07 10:34:47 +00:00
|
|
|
|
|
|
|
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&);
|
|
|
|
|
2017-09-21 12:58:36 +00:00
|
|
|
//////////////////////////////
|
|
|
|
// 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);
|
2017-09-21 12:58:36 +00:00
|
|
|
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");
|
2017-09-21 12:58:36 +00:00
|
|
|
|
2017-10-24 16:37:00 +00:00
|
|
|
//////////////////////////////
|
|
|
|
// 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;
|
|
|
|
};
|
2019-09-10 10:12:00 +00:00
|
|
|
std::unique_ptr<std::string> str = std::make_unique<std::string>("foo");
|
2017-10-24 16:37:00 +00:00
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
2017-09-28 17:55:52 +00:00
|
|
|
} // namespace template_utils_unittest
|
2017-08-07 10:34:47 +00:00
|
|
|
} // namespace base
|
|
|
|
} // namespace v8
|