DebugStringConvertible.h 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364
  1. /*
  2. * Copyright (c) Facebook, Inc. and its affiliates.
  3. *
  4. * This source code is licensed under the MIT license found in the
  5. * LICENSE file in the root directory of this source tree.
  6. */
  7. #pragma once
  8. #include <climits>
  9. #include <memory>
  10. #include <string>
  11. #include <unordered_set>
  12. #include <vector>
  13. namespace facebook {
  14. namespace react {
  15. #ifndef NDEBUG
  16. #define RN_DEBUG_STRING_CONVERTIBLE 1
  17. #endif
  18. #if RN_DEBUG_STRING_CONVERTIBLE
  19. class DebugStringConvertible;
  20. using SharedDebugStringConvertible =
  21. std::shared_ptr<const DebugStringConvertible>;
  22. using SharedDebugStringConvertibleList =
  23. std::vector<SharedDebugStringConvertible>;
  24. struct DebugStringConvertibleOptions {
  25. bool format{true};
  26. int depth{0};
  27. int maximumDepth{INT_MAX};
  28. };
  29. /*
  30. * Abstract class describes conformance to DebugStringConvertible concept
  31. * and implements basic recursive debug string assembly algorithm.
  32. * Use this as a base class for providing a debugging textual representation
  33. * of your class.
  34. *
  35. * The `DebugStringConvertible` *class* is obsolete. Whenever possible prefer
  36. * implementing standalone functions that conform to the informal
  37. * `DebugStringConvertible`-like interface instead of extending this class.
  38. */
  39. class DebugStringConvertible {
  40. public:
  41. virtual ~DebugStringConvertible() = default;
  42. // Returns a name of the object.
  43. // Default implementation returns "Node".
  44. virtual std::string getDebugName() const;
  45. // Returns a value associate with the object.
  46. // Default implementation returns an empty string.
  47. virtual std::string getDebugValue() const;
  48. // Returns a list of `DebugStringConvertible` objects which can be considered
  49. // as *children* of the object.
  50. // Default implementation returns an empty list.
  51. virtual SharedDebugStringConvertibleList getDebugChildren() const;
  52. // Returns a list of `DebugStringConvertible` objects which can be considered
  53. // as *properties* of the object.
  54. // Default implementation returns an empty list.
  55. virtual SharedDebugStringConvertibleList getDebugProps() const;
  56. // Returns a string which represents the object in a human-readable way.
  57. // Default implementation returns a description of the subtree
  58. // rooted at this node, represented in XML-like format.
  59. virtual std::string getDebugDescription(
  60. DebugStringConvertibleOptions options = {}) const;
  61. // Do same as `getDebugDescription` but return only *children* and
  62. // *properties* parts (which are used in `getDebugDescription`).
  63. virtual std::string getDebugPropsDescription(
  64. DebugStringConvertibleOptions options = {}) const;
  65. virtual std::string getDebugChildrenDescription(
  66. DebugStringConvertibleOptions options = {}) const;
  67. };
  68. #else
  69. class DebugStringConvertible {};
  70. #endif
  71. #if RN_DEBUG_STRING_CONVERTIBLE
  72. /*
  73. * Set of particular-format-opinionated functions that convert base types to
  74. * `std::string`; practically incapsulate `folly:to<>` and `folly::format`.
  75. */
  76. std::string toString(std::string const &value);
  77. std::string toString(int const &value);
  78. std::string toString(bool const &value);
  79. std::string toString(float const &value);
  80. std::string toString(double const &value);
  81. std::string toString(void const *value);
  82. /*
  83. * *Informal* `DebugStringConvertible` interface.
  84. *
  85. * The interface consts of several functions which are designed to be composable
  86. * and reusable relying on C++ overloading mechanism. Implement appropriate
  87. * versions of those functions for your custom type to enable conformance to the
  88. * interface:
  89. *
  90. * - `getDebugName`: Returns a name of the object. Default implementation
  91. * returns "Node".
  92. *
  93. * - `getDebugValue`: Returns a value associate with the object. Default
  94. * implementation returns an empty string.
  95. *
  96. * - `getDebugChildren`: Returns a list of `DebugStringConvertible`-compatible
  97. * objects which can be considered as *children* of the object. Default
  98. * implementation returns an empty list.
  99. *
  100. * - `getDebugProps`: Returns a list of `DebugStringConvertible` objects which
  101. * can be considered as *properties* of the object. Default implementation
  102. * returns an empty list.
  103. *
  104. * - `getDebugDescription`: Returns a string which represents the object in a
  105. * human-readable way. Default implementation returns a description of the
  106. * subtree rooted at this node, represented in XML-like format using functions
  107. * above to form the tree.
  108. */
  109. /*
  110. * Universal implementation of `getDebugDescription`-family functions for all
  111. * types.
  112. */
  113. template <typename T>
  114. std::string getDebugName(T const &object) {
  115. return "Node";
  116. }
  117. template <typename T>
  118. std::string getDebugValue(T const &object) {
  119. return "";
  120. }
  121. template <typename T>
  122. std::vector<T> getDebugChildren(
  123. T const &object,
  124. DebugStringConvertibleOptions options) {
  125. return {};
  126. }
  127. template <typename T>
  128. std::vector<T> getDebugProps(
  129. T const &object,
  130. DebugStringConvertibleOptions options) {
  131. return {};
  132. }
  133. template <typename T>
  134. std::string getDebugPropsDescription(
  135. T const &object,
  136. DebugStringConvertibleOptions options) {
  137. if (options.depth >= options.maximumDepth) {
  138. return "";
  139. }
  140. std::string propsString = "";
  141. options.depth++;
  142. for (auto prop : getDebugProps(object, options)) {
  143. auto name = getDebugName(prop);
  144. auto value = getDebugValue(prop);
  145. auto children = getDebugPropsDescription(prop, options);
  146. auto valueAndChildren =
  147. value + (children.empty() ? "" : "(" + children + ")");
  148. propsString +=
  149. " " + name + (valueAndChildren.empty() ? "" : "=" + valueAndChildren);
  150. }
  151. if (!propsString.empty()) {
  152. // Removing leading space character.
  153. propsString.erase(propsString.begin());
  154. }
  155. return propsString;
  156. }
  157. template <typename T>
  158. std::string getDebugChildrenDescription(
  159. T const &object,
  160. DebugStringConvertibleOptions options) {
  161. if (options.depth >= options.maximumDepth) {
  162. return "";
  163. }
  164. auto trailing = options.format ? std::string{"\n"} : std::string{""};
  165. auto childrenString = std::string{""};
  166. options.depth++;
  167. for (auto child : getDebugChildren(object, options)) {
  168. childrenString += getDebugDescription(child, options) + trailing;
  169. }
  170. if (!childrenString.empty() && !trailing.empty()) {
  171. // Removing trailing fragment.
  172. childrenString.erase(childrenString.end() - 1);
  173. }
  174. return childrenString;
  175. }
  176. template <typename T>
  177. std::string getDebugDescription(
  178. T const &object,
  179. DebugStringConvertibleOptions options) {
  180. auto nameString = getDebugName(object);
  181. auto valueString = getDebugValue(object);
  182. // Convention:
  183. // If `name` and `value` are empty, `description` is also empty.
  184. if (nameString.empty() && valueString.empty()) {
  185. return "";
  186. }
  187. // Convention:
  188. // If `name` is empty and `value` isn't empty, `description` equals `value`.
  189. if (nameString.empty()) {
  190. return valueString;
  191. }
  192. auto childrenString = getDebugChildrenDescription(object, options);
  193. auto propsString = getDebugPropsDescription(object, options);
  194. auto leading =
  195. options.format ? std::string(options.depth * 2, ' ') : std::string{""};
  196. auto trailing = options.format ? std::string{"\n"} : std::string{""};
  197. return leading + "<" + nameString +
  198. (valueString.empty() ? "" : "=" + valueString) +
  199. (propsString.empty() ? "" : " " + propsString) +
  200. (childrenString.empty() ? "/>"
  201. : ">" + trailing + childrenString + trailing +
  202. leading + "</" + nameString + ">");
  203. }
  204. /*
  205. * Functions of `getDebugDescription`-family for primitive types.
  206. */
  207. // `int`
  208. inline std::string getDebugDescription(
  209. int number,
  210. DebugStringConvertibleOptions options) {
  211. return toString(number);
  212. }
  213. // `float`
  214. inline std::string getDebugDescription(
  215. float number,
  216. DebugStringConvertibleOptions options) {
  217. return toString(number);
  218. }
  219. // `double`
  220. inline std::string getDebugDescription(
  221. double number,
  222. DebugStringConvertibleOptions options) {
  223. return toString(number);
  224. }
  225. // `bool`
  226. inline std::string getDebugDescription(
  227. bool boolean,
  228. DebugStringConvertibleOptions options) {
  229. return toString(boolean);
  230. }
  231. // `void *`
  232. inline std::string getDebugDescription(
  233. void *pointer,
  234. DebugStringConvertibleOptions options) {
  235. return toString(pointer);
  236. }
  237. // `std::string`
  238. inline std::string getDebugDescription(
  239. std::string const &string,
  240. DebugStringConvertibleOptions options) {
  241. return string;
  242. }
  243. // `std::vector<T>`
  244. template <typename T, typename... Ts>
  245. std::string getDebugName(std::vector<T, Ts...> const &vector) {
  246. return "List";
  247. }
  248. template <typename T, typename... Ts>
  249. std::vector<T, Ts...> getDebugChildren(
  250. std::vector<T, Ts...> const &vector,
  251. DebugStringConvertibleOptions options) {
  252. return vector;
  253. }
  254. // `std::unordered_set<T>`
  255. template <typename T, typename... Ts>
  256. std::string getDebugName(std::unordered_set<T, Ts...> const &set) {
  257. return "Set";
  258. }
  259. template <typename T, typename... Ts>
  260. std::vector<T> getDebugChildren(
  261. std::unordered_set<T, Ts...> const &set,
  262. DebugStringConvertibleOptions options) {
  263. auto vector = std::vector<T>{};
  264. vector.insert(vector.end(), set.begin(), set.end());
  265. return vector;
  266. }
  267. // `std::shared_ptr<T>`
  268. template <typename T>
  269. inline std::string getDebugDescription(
  270. std::shared_ptr<T> const &pointer,
  271. DebugStringConvertibleOptions options) {
  272. return getDebugDescription((void *)pointer.get(), options) + "(shared)";
  273. }
  274. // `std::weak_ptr<T>`
  275. template <typename T>
  276. inline std::string getDebugDescription(
  277. std::weak_ptr<T> const &pointer,
  278. DebugStringConvertibleOptions options) {
  279. return getDebugDescription((void *)pointer.lock().get(), options) + "(weak)";
  280. }
  281. // `std::unique_ptr<T>`
  282. template <typename T>
  283. inline std::string getDebugDescription(
  284. std::unique_ptr<T const> const &pointer,
  285. DebugStringConvertibleOptions options) {
  286. return getDebugDescription((void *)pointer.get(), options) + "(unique)";
  287. }
  288. /*
  289. * Trivial container for `name` and `value` pair that supports
  290. * static `DebugStringConvertible` informal interface.
  291. */
  292. struct DebugStringConvertibleObject {
  293. std::string name;
  294. std::string value;
  295. };
  296. inline std::string getDebugName(DebugStringConvertibleObject const &object) {
  297. return object.name;
  298. }
  299. inline std::string getDebugValue(DebugStringConvertibleObject const &object) {
  300. return object.value;
  301. }
  302. #endif
  303. } // namespace react
  304. } // namespace facebook