1 // Copyright 2023 The Pigweed Authors 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); you may not 4 // use this file except in compliance with the License. You may obtain a copy of 5 // the License at 6 // 7 // https://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 11 // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 12 // License for the specific language governing permissions and limitations under 13 // the License. 14 15 #pragma once 16 #include <string> 17 #include <type_traits> 18 #include <utility> 19 20 namespace bt { 21 namespace detail { 22 23 // Contains a true value if type |const T&| has a valid |.ToString()| method. 24 template <typename T, typename = void> 25 struct HasToStringMemberFn : std::false_type {}; 26 27 template <typename T> 28 struct HasToStringMemberFn< 29 T, 30 std::void_t<decltype(std::declval<const T&>().ToString())>> 31 : std::is_same<std::string, decltype(std::declval<const T&>().ToString())> { 32 }; 33 34 template <typename T> 35 constexpr bool HasToStringMemberFnV = HasToStringMemberFn<T>::value; 36 37 } // namespace detail 38 39 namespace internal { 40 41 // Instantiates on |T| to provide an implementation for bt_str(…) if T has a 42 // .ToString() method. 43 template <typename T> 44 std::enable_if_t<detail::HasToStringMemberFnV<T>, std::string> ToString( 45 const T& value) { 46 return value.ToString(); 47 } 48 49 // Contains a true value if there is a valid overload for 50 // |bt::internal::ToString(const T&)|. 51 template <typename T, typename = void> 52 struct HasToString : std::false_type {}; 53 54 template <typename T> 55 struct HasToString< 56 T, 57 std::void_t<decltype(::bt::internal::ToString(std::declval<const T&>()))>> 58 : std::is_same<std::string, 59 decltype(::bt::internal::ToString( 60 std::declval<const T&>()))> {}; 61 62 template <typename T> 63 constexpr bool HasToStringV = HasToString<T>::value; 64 65 } // namespace internal 66 67 // Compatibility for ostream-style string conversions for ToString-able types in 68 // namespace bt _only_, due to argument-dependent lookup (ADL) rules. 69 // std::ostream is implied through a template parameter to avoid directly 70 // including <ostream>. 71 // 72 // This is particularly useful for GoogleTest expectations. When a check failure 73 // against a type like bt::UUID prints this: 74 // 24-byte object <00-00 00-00 00-00 00-00 FB-34 9B-5F 80-00 00-80 00-10 00-00 75 // 0D-18 00-00> 76 // then including this file will cause its value printer to use this overload. 77 // Note that only including this file is not enough for types in other 78 // namespaces, including those nested in bt. For example, using this with 79 // bt::gap::Peer in a test requires the following: 80 // namespace bt::gap { 81 // using ::bt::operator<<; 82 // } // namespace bt::gap 83 template <typename T, 84 typename OStream, 85 typename = std::enable_if_t<internal::HasToStringV<T>>> 86 auto& operator<<(OStream& os, const T& t) { 87 return os << ::bt::internal::ToString(t); 88 } 89 90 } // namespace bt 91 92 // Convenience macro for printf-style formatting of an object with a ToString() 93 // function overload e.g.: 94 // bt_log(INFO, "tag", "foo happened: %s", bt_str(id)); 95 // 96 // This library provides an ToString() overload that forwards to .ToString() 97 // method if it exists. 98 #define bt_str(id) (::bt::internal::ToString(id).c_str()) 99