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