Commit a3541d1c authored by Karel Král's avatar Karel Král Committed by Commit Bot

trace_conversion_helper: SetTracedValueArg

Implement an overloaded function to trace its argument into a given
TracedValue pointer. Thus enable using type deduction to decide which
way to trace a given argument.

Bug: 1111787
Change-Id: I058c3436f59f3b6d98e34c1f620b467e4d5c41e5
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2364872
Commit-Queue: Karel Král <karelkral@google.com>
Reviewed-by: default avatarEric Seckler <eseckler@chromium.org>
Reviewed-by: default avatarŁukasz Anforowicz <lukasza@chromium.org>
Reviewed-by: default avatarAlexander Timin <altimin@chromium.org>
Cr-Commit-Position: refs/heads/master@{#804307}
parent 7ca1bf98
...@@ -8,6 +8,7 @@ ...@@ -8,6 +8,7 @@
#include <sstream> #include <sstream>
#include <string> #include <string>
#include "base/optional.h"
#include "base/strings/string_number_conversions.h" #include "base/strings/string_number_conversions.h"
#include "base/trace_event/traced_value.h" #include "base/trace_event/traced_value.h"
...@@ -26,18 +27,37 @@ std::string OstreamValueToString(const ValueType& value) { ...@@ -26,18 +27,37 @@ std::string OstreamValueToString(const ValueType& value) {
return ss.str(); return ss.str();
} }
// Helper class to mark call priority. Lower number has precedence: // Helper class to mark call priority when trying different options in SFINAE.
// Lower number has precedence.
// //
// Example: // Example: Suppose that we are looking if there is a method |T::foo| or
// void foo(ValueToStringPriority<1>); // |T::bar| and if there are both we should use |T::foo|.
// void foo(ValueToStringPriority<3>); //
// foo(ValueToStringPriority<0>()); // Calls |foo(ValueToStringPriority<1>)|. // // decltype will return the type of the last expression (in this case int)
// // if the first expression compiles. If the first part does not compile
// // other versions of |getValueHelper| will be considered, as "substitution
// // failure is not an error".
// decltype(std::declval<const T&>().foo(), int())
// getValueHelper(SFINAEPriority<0>, const T& obj) {
// return obj.foo();
// }
//
// // If both versions are defined (there are both |T::foo| and |T::bar|) then
// // this version is not used because of the type of the first argument
// // (SFINAEPriority<0> takes precedence during overload resolution because
// // it is the more specific (child) class).
// decltype(std::declval<const T&>().bar(), int())
// getValueHelper(SFINAEPriority<1>, const T& obj) {
// return obj.bar();
// }
template <int N> template <int N>
class ValueToStringPriority : public ValueToStringPriority<N + 1> {}; class SFINAEPriority : public SFINAEPriority<N + 1> {};
template <> template <>
// The number must be the same as in the fallback version of // The number must be at least the largest used.
// |ValueToStringHelper|. // Functions that use this class:
class ValueToStringPriority<5> {}; // ValueToStringHelper
// SetTracedValueArgHelper
class SFINAEPriority<6> {};
// Use SFINAE to decide how to extract a string from the given parameter. // Use SFINAE to decide how to extract a string from the given parameter.
...@@ -49,7 +69,7 @@ class ValueToStringPriority<5> {}; ...@@ -49,7 +69,7 @@ class ValueToStringPriority<5> {};
// faster than using |std::ostream::operator<<|. // faster than using |std::ostream::operator<<|.
template <typename ValueType> template <typename ValueType>
decltype(base::NumberToString(std::declval<const ValueType>()), std::string()) decltype(base::NumberToString(std::declval<const ValueType>()), std::string())
ValueToStringHelper(ValueToStringPriority<0>, ValueToStringHelper(SFINAEPriority<0>,
const ValueType& value, const ValueType& value,
std::string /* unused */) { std::string /* unused */) {
return base::NumberToString(value); return base::NumberToString(value);
...@@ -59,7 +79,7 @@ ValueToStringHelper(ValueToStringPriority<0>, ...@@ -59,7 +79,7 @@ ValueToStringHelper(ValueToStringPriority<0>,
// |std::string|, use this. Else use other methods. // |std::string|, use this. Else use other methods.
template <typename ValueType> template <typename ValueType>
decltype(std::string(std::declval<const ValueType>().ToString())) decltype(std::string(std::declval<const ValueType>().ToString()))
ValueToStringHelper(ValueToStringPriority<1>, ValueToStringHelper(SFINAEPriority<1>,
const ValueType& value, const ValueType& value,
std::string /* unused */) { std::string /* unused */) {
return value.ToString(); return value.ToString();
...@@ -70,7 +90,7 @@ template <typename ValueType> ...@@ -70,7 +90,7 @@ template <typename ValueType>
decltype( decltype(
std::declval<std::ostream>().operator<<(std::declval<const ValueType>()), std::declval<std::ostream>().operator<<(std::declval<const ValueType>()),
std::string()) std::string())
ValueToStringHelper(ValueToStringPriority<2>, ValueToStringHelper(SFINAEPriority<2>,
const ValueType& value, const ValueType& value,
std::string /* unused */) { std::string /* unused */) {
return OstreamValueToString(value); return OstreamValueToString(value);
...@@ -79,9 +99,9 @@ ValueToStringHelper(ValueToStringPriority<2>, ...@@ -79,9 +99,9 @@ ValueToStringHelper(ValueToStringPriority<2>,
// Use |ValueType::operator<<| if applicable. // Use |ValueType::operator<<| if applicable.
template <typename ValueType> template <typename ValueType>
decltype(operator<<(std::declval<std::ostream&>(), decltype(operator<<(std::declval<std::ostream&>(),
std::declval<const ValueType>()), std::declval<const ValueType&>()),
std::string()) std::string())
ValueToStringHelper(ValueToStringPriority<3>, ValueToStringHelper(SFINAEPriority<3>,
const ValueType& value, const ValueType& value,
std::string /* unused */) { std::string /* unused */) {
return OstreamValueToString(value); return OstreamValueToString(value);
...@@ -91,7 +111,7 @@ ValueToStringHelper(ValueToStringPriority<3>, ...@@ -91,7 +111,7 @@ ValueToStringHelper(ValueToStringPriority<3>,
// |std::string|, use it. // |std::string|, use it.
template <typename ValueType> template <typename ValueType>
decltype(std::string(std::declval<const ValueType>().data())) decltype(std::string(std::declval<const ValueType>().data()))
ValueToStringHelper(ValueToStringPriority<4>, ValueToStringHelper(SFINAEPriority<4>,
const ValueType& value, const ValueType& value,
std::string /* unused */) { std::string /* unused */) {
return value.data(); return value.data();
...@@ -100,20 +120,106 @@ ValueToStringHelper(ValueToStringPriority<4>, ...@@ -100,20 +120,106 @@ ValueToStringHelper(ValueToStringPriority<4>,
// Fallback returns the |fallback_value|. Needs to have |ValueToStringPriority| // Fallback returns the |fallback_value|. Needs to have |ValueToStringPriority|
// with the highest number (to be called last). // with the highest number (to be called last).
template <typename ValueType> template <typename ValueType>
std::string ValueToStringHelper(ValueToStringPriority<5>, std::string ValueToStringHelper(SFINAEPriority<5>,
const ValueType& /* unused */, const ValueType& /* unused */,
std::string fallback_value) { std::string fallback_value) {
return fallback_value; return fallback_value;
} }
/*********************************************
********* SetTracedValueArg methods. *********
*********************************************/
// SFINAEPriority parameter is there to define ordering in which the following
// methods will be considered. Note that for instance |bool| type is also
// |std::is_integral|, so we need to test |bool| before testing for integral.
template <typename T>
typename std::enable_if<std::is_same<T, bool>::value>::type
SetTracedValueArgHelper(SFINAEPriority<0>,
TracedValue* traced_value,
const char* name,
const T& value) {
traced_value->SetBoolean(name, value);
}
// std::is_integral<bool>::value == true
// This needs to be considered only when T is not bool (has higher
// SFINAEPriority).
template <typename T>
typename std::enable_if<std::is_integral<T>::value>::type
SetTracedValueArgHelper(SFINAEPriority<1>,
TracedValue* traced_value,
const char* name,
const T& value) {
// Avoid loss of precision.
if (sizeof(int) < sizeof(value)) {
// TODO(crbug.com/1111787): Add 64-bit support to TracedValue.
traced_value->SetString(name, base::NumberToString(value));
} else {
traced_value->SetInteger(name, value);
}
}
// Any floating point type is converted to double.
template <typename T>
typename std::enable_if<std::is_floating_point<T>::value>::type
SetTracedValueArgHelper(SFINAEPriority<2>,
TracedValue* traced_value,
const char* name,
const T& value) {
traced_value->SetDouble(name, static_cast<double>(value));
}
// |void*| is traced natively.
template <typename T>
typename std::enable_if<std::is_same<T, void*>::value>::type
SetTracedValueArgHelper(SFINAEPriority<3>,
TracedValue* traced_value,
const char* name,
const T& value) {
traced_value->SetPointer(name, value);
}
// |const char*| is traced natively.
template <typename T>
typename std::enable_if<std::is_same<T, const char*>::value>::type
SetTracedValueArgHelper(SFINAEPriority<4>,
TracedValue* traced_value,
const char* name,
const T& value) {
traced_value->SetString(name, value);
}
// If an instance of |base::StringPiece| can be constructed from an instance of
// |T| trace |value| as a string.
template <typename T>
decltype(base::StringPiece(std::declval<const T>()), void())
SetTracedValueArgHelper(SFINAEPriority<5>,
TracedValue* traced_value,
const char* name,
const T& value) {
traced_value->SetString(name, value);
}
// Fallback.
template <typename T>
void SetTracedValueArgHelper(SFINAEPriority<6>,
TracedValue* traced_value,
const char* name,
const T& /* unused */) {
// TODO(crbug.com/1111787): Add fallback to |ValueToString|. Crashes on
// operator<< have been seen with it.
traced_value->SetString(name, "<value>");
}
} // namespace internal } // namespace internal
// The function to be used. // The function to be used.
template <typename ValueType> template <typename ValueType>
std::string ValueToString(const ValueType& value, std::string ValueToString(const ValueType& value,
std::string fallback_value = "<value>") { std::string fallback_value = "<value>") {
return internal::ValueToStringHelper(internal::ValueToStringPriority<0>(), return internal::ValueToStringHelper(internal::SFINAEPriority<0>(), value,
value, std::move(fallback_value)); std::move(fallback_value));
} }
// ToTracedValue helpers simplify using |AsValueInto| method to capture by // ToTracedValue helpers simplify using |AsValueInto| method to capture by
...@@ -134,6 +240,43 @@ std::unique_ptr<TracedValue> ToTracedValue(T* value) { ...@@ -134,6 +240,43 @@ std::unique_ptr<TracedValue> ToTracedValue(T* value) {
return ToTracedValue(*value); return ToTracedValue(*value);
} }
// Method to trace |value| into the given |traced_value|. Support types where
// there is |TracedValue::SetT| natively.
//
// TODO(crbug.com/1111787): Add support for:
// base::Optional
// AsValueInto (T& and T*)
// array and map types
// fallback to ValueToString
template <typename ValueType>
void SetTracedValueArg(TracedValue* traced_value,
const char* name,
const ValueType& value) {
internal::SetTracedValueArgHelper(internal::SFINAEPriority<0>(), traced_value,
name, value);
}
// Parameter pack support: do nothing for an empty parameter pack.
//
// Inline this to avoid linker duplicate symbol error.
inline void SetTracedValueArg(TracedValue* traced_value, const char* name) {}
// Parameter pack support. All of the packed parameters are traced under the
// same name. Serves to trace a parameter pack, all parameters having the same
// name (of the parameter pack) is desired.
//
// Example use when |args| is a parameter pack:
// SetTracedValueArg(traced_value, name, args...);
template <typename ValueType, typename... ValueTypes>
void SetTracedValueArg(TracedValue* traced_value,
const char* name,
const ValueType& value,
const ValueTypes&... args) {
SetTracedValueArg(traced_value, name, value);
// Trace the rest from the parameter pack.
SetTracedValueArg(traced_value, name, args...);
}
} // namespace trace_event } // namespace trace_event
} // namespace base } // namespace base
......
...@@ -15,13 +15,79 @@ ...@@ -15,13 +15,79 @@
namespace base { namespace base {
namespace trace_event { namespace trace_event {
TEST(TraceEventArgumentTest, SetTracedValueArgParameterPack) {
std::unique_ptr<TracedValue> value(new TracedValue());
TracedValue* raw_value = value.get();
SetTracedValueArg(raw_value, "not_traced");
SetTracedValueArg(raw_value, "single_value", 42);
SetTracedValueArg(raw_value, "pack", 42, false, 0.0, "hello");
std::string json;
value->AppendAsTraceFormat(&json);
EXPECT_EQ(
"{"
"\"single_value\":42,"
"\"pack\":42,"
"\"pack\":false,"
"\"pack\":0.0,"
"\"pack\":\"hello\""
"}",
json);
}
TEST(TraceEventArgumentTest, SetTracedValueArgCompatibleTypes) {
std::unique_ptr<TracedValue> value(new TracedValue());
TracedValue* raw_value = value.get();
SetTracedValueArg(raw_value, "float_literal", 0.0f);
float my_float = 0.0f;
SetTracedValueArg(raw_value, "my_float", my_float);
char my_char = 13;
SetTracedValueArg(raw_value, "my_char", my_char);
std::string json;
value->AppendAsTraceFormat(&json);
EXPECT_EQ(
"{"
"\"float_literal\":0.0,"
"\"my_float\":0.0,"
"\"my_char\":13"
"}",
json);
}
class UseFallback {};
TEST(TraceEventArgumentTest, SetTracedValueArgBasicTypes) {
std::unique_ptr<TracedValue> value(new TracedValue());
TracedValue* raw_value = value.get();
SetTracedValueArg(raw_value, "my_int", 1);
SetTracedValueArg(raw_value, "my_double", 0.1);
SetTracedValueArg(raw_value, "my_bool", false);
SetTracedValueArg(raw_value, "my_literal", "hello");
SetTracedValueArg(raw_value, "my_string",
std::string("wonderful_") + std::string("world"));
SetTracedValueArg(raw_value, "my_void_ptr", static_cast<void*>(nullptr));
SetTracedValueArg(raw_value, "use_fallback", UseFallback());
UseFallback usefallback_with_variable;
SetTracedValueArg(raw_value, "use_fallback", usefallback_with_variable);
std::string json;
value->AppendAsTraceFormat(&json);
EXPECT_EQ(
"{\"my_int\":1,"
"\"my_double\":0.1,"
"\"my_bool\":false,"
"\"my_literal\":\"hello\","
"\"my_string\":\"wonderful_world\","
"\"my_void_ptr\":\"0x0\","
"\"use_fallback\":\"\\u003Cvalue>\","
"\"use_fallback\":\"\\u003Cvalue>\""
"}",
json);
}
TEST(TraceEventConversionHelperTest, OstreamValueToString) { TEST(TraceEventConversionHelperTest, OstreamValueToString) {
std::string zero = internal::OstreamValueToString(0); std::string zero = internal::OstreamValueToString(0);
EXPECT_EQ("0", zero); EXPECT_EQ("0", zero);
} }
class UseFallback {};
TEST(TraceEventConversionHelperTest, UseFallback) { TEST(TraceEventConversionHelperTest, UseFallback) {
std::string answer = ValueToString(UseFallback(), "fallback"); std::string answer = ValueToString(UseFallback(), "fallback");
EXPECT_EQ("fallback", answer); EXPECT_EQ("fallback", answer);
......
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