@@ -62,34 +62,55 @@ template <> struct fp_hex_representation<double> : support_flag<true> {
62
62
63
63
} // namespace detail
64
64
65
- // Provides generic stringification for logging purposes
66
- template <typename T> static std::string stringify (T val) {
67
- if constexpr (std::is_convertible_v<T, std::string>) {
68
- return val;
69
- } else if constexpr (type_traits::is_sycl_floating_point_v<T>) {
70
- // Define the output precision based on the type precision itself
71
- // For example, state 9 decimal digits for 32-bit floating point
72
- const auto significand_decimal_digits = sizeof (T) * 2 + 1 ;
73
-
74
- std::ostringstream out;
75
- out.precision (significand_decimal_digits);
76
- out << val << " [" ;
77
- if constexpr (detail::fp_hex_representation<T>::is_supported ()) {
78
- // Print out hex representation using type-punning
79
- using hex_type = typename detail::fp_hex_representation<T>::type;
80
- const auto &representation = reinterpret_cast <const hex_type &>(val);
81
- out << " 0x" << std::hex << representation;
65
+ // Provides a stringification helper for safe specialization
66
+ // Benefits:
67
+ // - makes partial specialization possible
68
+ // - avoids unexpected behaviour for function specializations and overloads
69
+ template <typename T> struct StringMaker {
70
+ static std::string stringify (T val) {
71
+ if constexpr (std::is_convertible_v<T, std::string>) {
72
+ return val;
73
+ } else if constexpr (type_traits::is_sycl_floating_point_v<T>) {
74
+ // Define the output precision based on the type precision itself
75
+ // For example, state 9 decimal digits for 32-bit floating point
76
+ const auto significand_decimal_digits = sizeof (T) * 2 + 1 ;
77
+
78
+ std::ostringstream out;
79
+ out.precision (significand_decimal_digits);
80
+ out << val << " [" ;
81
+ if constexpr (detail::fp_hex_representation<T>::is_supported ()) {
82
+ // Print out hex representation using type-punning
83
+ using hex_type = typename detail::fp_hex_representation<T>::type;
84
+ const auto &representation = reinterpret_cast <const hex_type &>(val);
85
+ out << " 0x" << std::hex << representation;
86
+ } else {
87
+ // Make support gap explicit to address if required
88
+ out << " - " ;
89
+ }
90
+ out << " ]" ;
91
+ return out.str ();
92
+ } else if constexpr (std::is_enum_v<T>) {
93
+ // Any enumeration requires explicit cast to the underlying type
94
+ // Please note that StringMaker can be safely specialized for any
95
+ // enumeration type for more human-readable logs if required
96
+ return std::to_string (static_cast <std::underlying_type_t <T>>(val));
82
97
} else {
83
- // Make support gap explicit to address if required
84
- out << " - " ;
98
+ return std::to_string (val);
85
99
}
86
- out << " ]" ;
87
- return out.str ();
88
- } else {
89
- return std::to_string (val);
90
100
}
101
+ };
102
+
103
+ // Provides generic stringification for logging purposes
104
+ // To tweak for specific type, please:
105
+ // - either provide overload,
106
+ // - or specialize the StringMaker for this type
107
+ template <typename T> static std::string stringify (T val) {
108
+ return log::StringMaker<T>::stringify (val);
91
109
}
92
110
111
+ // Overload to improve performance a bit; works as the first-class citizen
112
+ inline const std::string &stringify (const std::string &val) { return val; }
113
+
93
114
// Print line to the log using a custom set of parameters
94
115
//
95
116
// Lambda can be used to print out any custom information,
@@ -138,7 +159,7 @@ template <typename... argsT> inline void print_line(const argsT &...args) {
138
159
// logging disabled
139
160
template <typename ... argsT> inline void debug (const argsT &...args) {
140
161
#ifdef ESIMD_TESTS_VERBOSE_LOG
141
- print_line (args);
162
+ print_line (args... );
142
163
#else
143
164
// Suppress unused variables warning
144
165
(static_cast <void >(args), ...);
0 commit comments