DbgMacro.h (6163B)
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */ 3 /* This Source Code Form is subject to the terms of the Mozilla Public 4 * License, v. 2.0. If a copy of the MPL was not distributed with this 5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 6 7 #ifndef mozilla_DbgMacro_h 8 #define mozilla_DbgMacro_h 9 10 /* a MOZ_DBG macro that outputs a wrapped value to stderr then returns it */ 11 12 #include "mozilla/MacroForEach.h" 13 #include "mozilla/Span.h" 14 15 #include <cstdio> 16 #include <sstream> 17 18 template <typename T> 19 class nsTSubstring; 20 21 #ifdef ANDROID 22 # include <android/log.h> 23 #endif 24 25 namespace mozilla { 26 27 namespace detail { 28 29 // Predicate to check whether T can be inserted into an ostream. 30 template <typename T, typename = void> 31 struct supports_os : std::false_type {}; 32 33 template <typename T> 34 struct supports_os<T, std::void_t<decltype(std::declval<std::ostream&>() 35 << std::declval<T&>())>> 36 : std::true_type {}; 37 38 } // namespace detail 39 40 // Helper function to write a value to an ostream. 41 // 42 // This handles both pointer values where the type being pointed to supports 43 // being inserted into an ostream (in which case we write out the value being 44 // pointed to in addition to the pointer value), and pointer types that cannot 45 // be dereferenced (in which cases we just write the pointer value). 46 template <typename T> 47 std::ostream& DebugValue(std::ostream& aOut, T* aValue) { 48 if constexpr (detail::supports_os<T>::value) { 49 if (aValue) { 50 return aOut << *aValue << " @ " << aValue; 51 } else { 52 return aOut << "null"; 53 } 54 } else { 55 return aOut << aValue; 56 } 57 } 58 59 // Helper function to write a value to an ostream. 60 // 61 // This handle all non-pointer types, with a specialization for XPCOM types. 62 template <typename T> 63 std::ostream& DebugValue(std::ostream& aOut, const T& aValue) { 64 if constexpr (std::is_base_of<nsTSubstring<char>, T>::value || 65 std::is_base_of<nsTSubstring<char16_t>, T>::value) { 66 return aOut << '"' << aValue << '"'; 67 } else { 68 return aOut << aValue; 69 } 70 } 71 72 namespace detail { 73 74 // Helper function template for MOZ_DBG. 75 template <typename T> 76 auto&& MozDbg(const char* aFile, int aLine, const char* aExpression, 77 T&& aValue) { 78 std::ostringstream s; 79 s << "[MozDbg] [" << aFile << ':' << aLine << "] " << aExpression << " = "; 80 mozilla::DebugValue(s, std::forward<T>(aValue)) << '\n'; 81 #ifdef ANDROID 82 __android_log_print(ANDROID_LOG_INFO, "Gecko", "%s", s.str().c_str()); 83 #else 84 fputs(s.str().c_str(), stderr); 85 #endif 86 return std::forward<T>(aValue); 87 } 88 89 } // namespace detail 90 91 } // namespace mozilla 92 93 template <class ElementType, size_t Extent> 94 std::ostream& operator<<(std::ostream& aOut, 95 const mozilla::Span<ElementType, Extent>& aSpan) { 96 aOut << '['; 97 if (!aSpan.IsEmpty()) { 98 aOut << aSpan[0]; 99 for (size_t i = 1; i < aSpan.Length(); ++i) { 100 aOut << ", " << aSpan[i]; 101 } 102 } 103 return aOut << ']'; 104 } 105 106 // Don't define this for char[], since operator<<(ostream&, char*) is already 107 // defined. 108 template <typename T, size_t N, 109 typename = std::enable_if_t<!std::is_same<T, char>::value>> 110 std::ostream& operator<<(std::ostream& aOut, const T (&aArray)[N]) { 111 return aOut << mozilla::Span(aArray); 112 } 113 114 // MOZ_DBG is a macro like the Rust dbg!() macro -- it will print out the 115 // expression passed to it to stderr and then return the value. It is not 116 // available in MOZILLA_OFFICIAL builds, so you shouldn't land any uses of it in 117 // the tree. 118 // 119 // It should work for any type T that has an operator<<(std::ostream&, const T&) 120 // defined for it. 121 // 122 // Note 1: Using MOZ_DBG may cause copies to be made of temporary values: 123 // 124 // struct A { 125 // A(int); 126 // A(const A&); 127 // 128 // int x; 129 // }; 130 // 131 // void f(A); 132 // 133 // f(A{1}); // may (and, in C++17, will) elide the creation of a temporary 134 // // for A{1} and instead initialize the function argument 135 // // directly using the A(int) constructor 136 // 137 // f(MOZ_DBG(A{1})); // will create and return a temporary for A{1}, which 138 // // then will be passed to the A(const A&) copy 139 // // constructor to initialize f's argument 140 // 141 // Note 2: MOZ_DBG cannot be used to wrap a prvalue that is being used to 142 // initialize an object if its type has no move constructor: 143 // 144 // struct B { 145 // B() = default; 146 // B(B&&) = delete; 147 // }; 148 // 149 // B b1 = B(); // fine, initializes b1 directly 150 // 151 // B b2 = MOZ_DBG(B()); // compile error: MOZ_DBG needs to materialize a 152 // // temporary for B() so it can be passed to 153 // // operator<<, but that temporary is returned from 154 // // MOZ_DBG as an rvalue reference and so wants to 155 // // invoke B's move constructor to initialize b2 156 #ifndef MOZILLA_OFFICIAL 157 # define MOZ_DBG(...) \ 158 mozilla::detail::MozDbg(__FILE__, __LINE__, #__VA_ARGS__, __VA_ARGS__) 159 #endif 160 161 // Helper macro for MOZ_DEFINE_DBG. 162 #define MOZ_DBG_FIELD(name_) << #name_ << " = " << aValue.name_ 163 164 // Macro to define an operator<<(ostream&) for a struct or class that displays 165 // the type name and the values of the specified member variables. Must be 166 // called inside the struct or class. 167 // 168 // For example: 169 // 170 // struct Point { 171 // float x; 172 // float y; 173 // 174 // MOZ_DEFINE_DBG(Point, x, y) 175 // }; 176 // 177 // generates an operator<< that outputs strings like 178 // "Point { x = 1.0, y = 2.0 }". 179 #define MOZ_DEFINE_DBG(type_, ...) \ 180 friend std::ostream& operator<<(std::ostream& aOut, const type_& aValue) { \ 181 return aOut << #type_ \ 182 << (MOZ_ARG_COUNT(__VA_ARGS__) == 0 ? "" : " { ") \ 183 MOZ_FOR_EACH_SEPARATED(MOZ_DBG_FIELD, (<< ", "), (), \ 184 (__VA_ARGS__)) \ 185 << (MOZ_ARG_COUNT(__VA_ARGS__) == 0 ? "" : " }"); \ 186 } 187 188 #endif // mozilla_DbgMacro_h