Commit 84dc3679 authored by Clemens Hammacher's avatar Clemens Hammacher Committed by Commit Bot

Move helper struct from logging.h to template-utils.h

I want to reuse the PassType helper in another CL, thus move it from
logging.h to template-utils.h, and rename it to pass_value_or_ref to
match other helpers there.
Also, add a boolean template parameter to declare whether array
dimensions should be removed. The default is to do so, which helps to
reduce the number of template instantiations by always passing arrays
as pointers.

Also, fix the usages in logging.h to actually use that helper when
instantiating other template functions. This will reduce the number of
instantiations.

And finally, we now have unit tests for the template utils, to document
what we expect, and test that this works on all architectures.

R=ishell@chromium.org, tebbi@chromium.org

Change-Id: I1ef5d2a489a5cfc7601c5ab13748674e3aa86cd6
Reviewed-on: https://chromium-review.googlesource.com/594247
Commit-Queue: Clemens Hammacher <clemensh@chromium.org>
Reviewed-by: 's avatarTobias Tebbi <tebbi@chromium.org>
Cr-Commit-Position: refs/heads/master@{#47191}
parent 32055b9d
...@@ -12,6 +12,7 @@ ...@@ -12,6 +12,7 @@
#include "src/base/base-export.h" #include "src/base/base-export.h"
#include "src/base/build_config.h" #include "src/base/build_config.h"
#include "src/base/compiler-specific.h" #include "src/base/compiler-specific.h"
#include "src/base/template-utils.h"
[[noreturn]] PRINTF_FORMAT(3, 4) V8_BASE_EXPORT [[noreturn]] PRINTF_FORMAT(3, 4) V8_BASE_EXPORT
void V8_Fatal(const char* file, int line, const char* format, ...); void V8_Fatal(const char* file, int line, const char* format, ...);
...@@ -66,21 +67,23 @@ V8_BASE_EXPORT void SetPrintStackTrace(void (*print_stack_trace_)()); ...@@ -66,21 +67,23 @@ V8_BASE_EXPORT void SetPrintStackTrace(void (*print_stack_trace_)());
// Helper macro for binary operators. // Helper macro for binary operators.
// Don't use this macro directly in your code, use CHECK_EQ et al below. // Don't use this macro directly in your code, use CHECK_EQ et al below.
#define CHECK_OP(name, op, lhs, rhs) \ #define CHECK_OP(name, op, lhs, rhs) \
do { \ do { \
if (std::string* _msg = \ if (std::string* _msg = ::v8::base::Check##name##Impl< \
::v8::base::Check##name##Impl<decltype(lhs), decltype(rhs)>( \ typename v8::base::pass_value_or_ref<decltype(lhs)>::type, \
(lhs), (rhs), #lhs " " #op " " #rhs)) { \ typename v8::base::pass_value_or_ref<decltype(rhs)>::type>( \
V8_Fatal(__FILE__, __LINE__, "Check failed: %s.", _msg->c_str()); \ (lhs), (rhs), #lhs " " #op " " #rhs)) { \
delete _msg; \ V8_Fatal(__FILE__, __LINE__, "Check failed: %s.", _msg->c_str()); \
} \ delete _msg; \
} \
} while (0) } while (0)
#define DCHECK_OP(name, op, lhs, rhs) \ #define DCHECK_OP(name, op, lhs, rhs) \
do { \ do { \
if (std::string* _msg = \ if (std::string* _msg = ::v8::base::Check##name##Impl< \
::v8::base::Check##name##Impl<decltype(lhs), decltype(rhs)>( \ typename v8::base::pass_value_or_ref<decltype(lhs)>::type, \
(lhs), (rhs), #lhs " " #op " " #rhs)) { \ typename v8::base::pass_value_or_ref<decltype(rhs)>::type>( \
(lhs), (rhs), #lhs " " #op " " #rhs)) { \
V8_Fatal(__FILE__, __LINE__, "Debug check failed: %s.", _msg->c_str()); \ V8_Fatal(__FILE__, __LINE__, "Debug check failed: %s.", _msg->c_str()); \
delete _msg; \ delete _msg; \
} \ } \
...@@ -91,24 +94,17 @@ V8_BASE_EXPORT void SetPrintStackTrace(void (*print_stack_trace_)()); ...@@ -91,24 +94,17 @@ V8_BASE_EXPORT void SetPrintStackTrace(void (*print_stack_trace_)());
// Make all CHECK functions discard their log strings to reduce code // Make all CHECK functions discard their log strings to reduce code
// bloat for official release builds. // bloat for official release builds.
#define CHECK_OP(name, op, lhs, rhs) \ #define CHECK_OP(name, op, lhs, rhs) \
do { \ do { \
bool _cmp = \ bool _cmp = ::v8::base::Cmp##name##Impl< \
::v8::base::Cmp##name##Impl<decltype(lhs), decltype(rhs)>(lhs, rhs); \ typename v8::base::pass_value_or_ref<decltype(lhs)>::type, \
CHECK_WITH_MSG(_cmp, #lhs " " #op " " #rhs); \ typename v8::base::pass_value_or_ref<decltype(rhs)>::type>((lhs), \
(rhs)); \
CHECK_WITH_MSG(_cmp, #lhs " " #op " " #rhs); \
} while (0) } while (0)
#endif #endif
// Helper to determine how to pass values: Pass scalars and arrays by value,
// others by const reference. std::decay<T> provides the type which should be
// used to pass T by value, e.g. converts array to pointer and removes const,
// volatile and reference.
template <typename T>
struct PassType : public std::conditional<
std::is_scalar<typename std::decay<T>::type>::value,
typename std::decay<T>::type, T const&> {};
template <typename Op> template <typename Op>
void PrintCheckOperand(std::ostream& os, Op op) { void PrintCheckOperand(std::ostream& os, Op op) {
os << op; os << op;
...@@ -135,9 +131,7 @@ DEFINE_PRINT_CHECK_OPERAND_CHAR(unsigned char) ...@@ -135,9 +131,7 @@ DEFINE_PRINT_CHECK_OPERAND_CHAR(unsigned char)
// be out of line, while the "Impl" code should be inline. Caller // be out of line, while the "Impl" code should be inline. Caller
// takes ownership of the returned string. // takes ownership of the returned string.
template <typename Lhs, typename Rhs> template <typename Lhs, typename Rhs>
std::string* MakeCheckOpString(typename PassType<Lhs>::type lhs, std::string* MakeCheckOpString(Lhs lhs, Rhs rhs, char const* msg) {
typename PassType<Rhs>::type rhs,
char const* msg) {
std::ostringstream ss; std::ostringstream ss;
ss << msg << " ("; ss << msg << " (";
PrintCheckOperand(ss, lhs); PrintCheckOperand(ss, lhs);
...@@ -235,18 +229,18 @@ DEFINE_SIGNED_MISMATCH_COMP(is_unsigned_vs_signed, GE, CmpLEImpl(rhs, lhs)) ...@@ -235,18 +229,18 @@ DEFINE_SIGNED_MISMATCH_COMP(is_unsigned_vs_signed, GE, CmpLEImpl(rhs, lhs))
V8_INLINE \ V8_INLINE \
typename std::enable_if<!is_signed_vs_unsigned<Lhs, Rhs>::value && \ typename std::enable_if<!is_signed_vs_unsigned<Lhs, Rhs>::value && \
!is_unsigned_vs_signed<Lhs, Rhs>::value, \ !is_unsigned_vs_signed<Lhs, Rhs>::value, \
bool>::type \ bool>::type Cmp##NAME##Impl(Lhs lhs, Rhs rhs) { \
Cmp##NAME##Impl(typename PassType<Lhs>::type lhs, \
typename PassType<Rhs>::type rhs) { \
return lhs op rhs; \ return lhs op rhs; \
} \ } \
template <typename Lhs, typename Rhs> \ template <typename Lhs, typename Rhs> \
V8_INLINE std::string* Check##NAME##Impl(typename PassType<Lhs>::type lhs, \ V8_INLINE std::string* Check##NAME##Impl(Lhs lhs, Rhs rhs, \
typename PassType<Rhs>::type rhs, \
char const* msg) { \ char const* msg) { \
bool cmp = Cmp##NAME##Impl<Lhs, Rhs>(lhs, rhs); \ using LhsPassT = typename pass_value_or_ref<Lhs>::type; \
return V8_LIKELY(cmp) ? nullptr \ using RhsPassT = typename pass_value_or_ref<Rhs>::type; \
: MakeCheckOpString<Lhs, Rhs>(lhs, rhs, msg); \ bool cmp = Cmp##NAME##Impl<LhsPassT, RhsPassT>(lhs, rhs); \
return V8_LIKELY(cmp) \
? nullptr \
: MakeCheckOpString<LhsPassT, RhsPassT>(lhs, rhs, msg); \
} \ } \
extern template V8_BASE_EXPORT std::string* Check##NAME##Impl<float, float>( \ extern template V8_BASE_EXPORT std::string* Check##NAME##Impl<float, float>( \
float lhs, float rhs, char const* msg); \ float lhs, float rhs, char const* msg); \
......
...@@ -62,6 +62,22 @@ A implicit_cast(A x) { ...@@ -62,6 +62,22 @@ A implicit_cast(A x) {
return x; return x;
} }
// Helper to determine how to pass values: Pass scalars and arrays by value,
// others by const reference (even if it was a non-const ref before; this is
// disallowed by the style guide anyway).
// The default is to also remove array extends (int[5] -> int*), but this can be
// disabled by setting {remove_array_extend} to false.
template <typename T, bool remove_array_extend = true>
struct pass_value_or_ref {
using noref_t = typename std::remove_reference<T>::type;
using decay_t = typename std::conditional<
std::is_array<noref_t>::value && !remove_array_extend, noref_t,
typename std::decay<noref_t>::type>::type;
using type = typename std::conditional<std::is_scalar<decay_t>::value ||
std::is_array<decay_t>::value,
decay_t, const decay_t&>::type;
};
} // namespace base } // namespace base
} // namespace v8 } // namespace v8
......
...@@ -37,6 +37,7 @@ v8_executable("unittests") { ...@@ -37,6 +37,7 @@ v8_executable("unittests") {
"base/platform/semaphore-unittest.cc", "base/platform/semaphore-unittest.cc",
"base/platform/time-unittest.cc", "base/platform/time-unittest.cc",
"base/sys-info-unittest.cc", "base/sys-info-unittest.cc",
"base/template-utils-unittest.cc",
"base/utils/random-number-generator-unittest.cc", "base/utils/random-number-generator-unittest.cc",
"cancelable-tasks-unittest.cc", "cancelable-tasks-unittest.cc",
"char-predicates-unittest.cc", "char-predicates-unittest.cc",
......
// 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 {
////////////////////////////
// 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&);
} // namespace base
} // namespace v8
...@@ -34,6 +34,7 @@ ...@@ -34,6 +34,7 @@
'base/platform/semaphore-unittest.cc', 'base/platform/semaphore-unittest.cc',
'base/platform/time-unittest.cc', 'base/platform/time-unittest.cc',
'base/sys-info-unittest.cc', 'base/sys-info-unittest.cc',
'base/template-utils-unittest.cc',
'base/utils/random-number-generator-unittest.cc', 'base/utils/random-number-generator-unittest.cc',
'cancelable-tasks-unittest.cc', 'cancelable-tasks-unittest.cc',
'char-predicates-unittest.cc', 'char-predicates-unittest.cc',
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment