catch_tostring.hpp 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669
  1. // Copyright Catch2 Authors
  2. // Distributed under the Boost Software License, Version 1.0.
  3. // (See accompanying file LICENSE_1_0.txt or copy at
  4. // https://www.boost.org/LICENSE_1_0.txt)
  5. // SPDX-License-Identifier: BSL-1.0
  6. #ifndef CATCH_TOSTRING_HPP_INCLUDED
  7. #define CATCH_TOSTRING_HPP_INCLUDED
  8. #include <vector>
  9. #include <cstddef>
  10. #include <type_traits>
  11. #include <string>
  12. #include <catch2/internal/catch_compiler_capabilities.hpp>
  13. #include <catch2/internal/catch_config_wchar.hpp>
  14. #include <catch2/internal/catch_reusable_string_stream.hpp>
  15. #include <catch2/internal/catch_void_type.hpp>
  16. #include <catch2/interfaces/catch_interfaces_enum_values_registry.hpp>
  17. #ifdef CATCH_CONFIG_CPP17_STRING_VIEW
  18. #include <string_view>
  19. #endif
  20. #ifdef _MSC_VER
  21. #pragma warning(push)
  22. #pragma warning(disable:4180) // We attempt to stream a function (address) by const&, which MSVC complains about but is harmless
  23. #endif
  24. // We need a dummy global operator<< so we can bring it into Catch namespace later
  25. struct Catch_global_namespace_dummy{};
  26. std::ostream& operator<<(std::ostream&, Catch_global_namespace_dummy);
  27. namespace Catch {
  28. // Bring in global namespace operator<< for ADL lookup in
  29. // `IsStreamInsertable` below.
  30. using ::operator<<;
  31. namespace Detail {
  32. inline std::size_t catch_strnlen(const char *str, std::size_t n) {
  33. auto ret = std::char_traits<char>::find(str, n, '\0');
  34. if (ret != nullptr) {
  35. return static_cast<std::size_t>(ret - str);
  36. }
  37. return n;
  38. }
  39. constexpr StringRef unprintableString = "{?}"_sr;
  40. //! Encases `string in quotes, and optionally escapes invisibles
  41. std::string convertIntoString( StringRef string, bool escapeInvisibles );
  42. //! Encases `string` in quotes, and escapes invisibles if user requested
  43. //! it via CLI
  44. std::string convertIntoString( StringRef string );
  45. std::string rawMemoryToString( const void *object, std::size_t size );
  46. template<typename T>
  47. std::string rawMemoryToString( const T& object ) {
  48. return rawMemoryToString( &object, sizeof(object) );
  49. }
  50. template<typename T>
  51. class IsStreamInsertable {
  52. template<typename Stream, typename U>
  53. static auto test(int)
  54. -> decltype(std::declval<Stream&>() << std::declval<U>(), std::true_type());
  55. template<typename, typename>
  56. static auto test(...)->std::false_type;
  57. public:
  58. static const bool value = decltype(test<std::ostream, const T&>(0))::value;
  59. };
  60. template<typename E>
  61. std::string convertUnknownEnumToString( E e );
  62. template<typename T>
  63. std::enable_if_t<
  64. !std::is_enum<T>::value && !std::is_base_of<std::exception, T>::value,
  65. std::string> convertUnstreamable( T const& ) {
  66. return std::string(Detail::unprintableString);
  67. }
  68. template<typename T>
  69. std::enable_if_t<
  70. !std::is_enum<T>::value && std::is_base_of<std::exception, T>::value,
  71. std::string> convertUnstreamable(T const& ex) {
  72. return ex.what();
  73. }
  74. template<typename T>
  75. std::enable_if_t<
  76. std::is_enum<T>::value,
  77. std::string> convertUnstreamable( T const& value ) {
  78. return convertUnknownEnumToString( value );
  79. }
  80. #if defined(_MANAGED)
  81. //! Convert a CLR string to a utf8 std::string
  82. template<typename T>
  83. std::string clrReferenceToString( T^ ref ) {
  84. if (ref == nullptr)
  85. return std::string("null");
  86. auto bytes = System::Text::Encoding::UTF8->GetBytes(ref->ToString());
  87. cli::pin_ptr<System::Byte> p = &bytes[0];
  88. return std::string(reinterpret_cast<char const *>(p), bytes->Length);
  89. }
  90. #endif
  91. } // namespace Detail
  92. // If we decide for C++14, change these to enable_if_ts
  93. template <typename T, typename = void>
  94. struct StringMaker {
  95. template <typename Fake = T>
  96. static
  97. std::enable_if_t<::Catch::Detail::IsStreamInsertable<Fake>::value, std::string>
  98. convert(const Fake& value) {
  99. ReusableStringStream rss;
  100. // NB: call using the function-like syntax to avoid ambiguity with
  101. // user-defined templated operator<< under clang.
  102. rss.operator<<(value);
  103. return rss.str();
  104. }
  105. template <typename Fake = T>
  106. static
  107. std::enable_if_t<!::Catch::Detail::IsStreamInsertable<Fake>::value, std::string>
  108. convert( const Fake& value ) {
  109. #if !defined(CATCH_CONFIG_FALLBACK_STRINGIFIER)
  110. return Detail::convertUnstreamable(value);
  111. #else
  112. return CATCH_CONFIG_FALLBACK_STRINGIFIER(value);
  113. #endif
  114. }
  115. };
  116. namespace Detail {
  117. // This function dispatches all stringification requests inside of Catch.
  118. // Should be preferably called fully qualified, like ::Catch::Detail::stringify
  119. template <typename T>
  120. std::string stringify(const T& e) {
  121. return ::Catch::StringMaker<std::remove_cv_t<std::remove_reference_t<T>>>::convert(e);
  122. }
  123. template<typename E>
  124. std::string convertUnknownEnumToString( E e ) {
  125. return ::Catch::Detail::stringify(static_cast<std::underlying_type_t<E>>(e));
  126. }
  127. #if defined(_MANAGED)
  128. template <typename T>
  129. std::string stringify( T^ e ) {
  130. return ::Catch::StringMaker<T^>::convert(e);
  131. }
  132. #endif
  133. } // namespace Detail
  134. // Some predefined specializations
  135. template<>
  136. struct StringMaker<std::string> {
  137. static std::string convert(const std::string& str);
  138. };
  139. #ifdef CATCH_CONFIG_CPP17_STRING_VIEW
  140. template<>
  141. struct StringMaker<std::string_view> {
  142. static std::string convert(std::string_view str);
  143. };
  144. #endif
  145. template<>
  146. struct StringMaker<char const *> {
  147. static std::string convert(char const * str);
  148. };
  149. template<>
  150. struct StringMaker<char *> {
  151. static std::string convert(char * str);
  152. };
  153. #if defined(CATCH_CONFIG_WCHAR)
  154. template<>
  155. struct StringMaker<std::wstring> {
  156. static std::string convert(const std::wstring& wstr);
  157. };
  158. # ifdef CATCH_CONFIG_CPP17_STRING_VIEW
  159. template<>
  160. struct StringMaker<std::wstring_view> {
  161. static std::string convert(std::wstring_view str);
  162. };
  163. # endif
  164. template<>
  165. struct StringMaker<wchar_t const *> {
  166. static std::string convert(wchar_t const * str);
  167. };
  168. template<>
  169. struct StringMaker<wchar_t *> {
  170. static std::string convert(wchar_t * str);
  171. };
  172. #endif // CATCH_CONFIG_WCHAR
  173. template<size_t SZ>
  174. struct StringMaker<char[SZ]> {
  175. static std::string convert(char const* str) {
  176. return Detail::convertIntoString(
  177. StringRef( str, Detail::catch_strnlen( str, SZ ) ) );
  178. }
  179. };
  180. template<size_t SZ>
  181. struct StringMaker<signed char[SZ]> {
  182. static std::string convert(signed char const* str) {
  183. auto reinterpreted = reinterpret_cast<char const*>(str);
  184. return Detail::convertIntoString(
  185. StringRef(reinterpreted, Detail::catch_strnlen(reinterpreted, SZ)));
  186. }
  187. };
  188. template<size_t SZ>
  189. struct StringMaker<unsigned char[SZ]> {
  190. static std::string convert(unsigned char const* str) {
  191. auto reinterpreted = reinterpret_cast<char const*>(str);
  192. return Detail::convertIntoString(
  193. StringRef(reinterpreted, Detail::catch_strnlen(reinterpreted, SZ)));
  194. }
  195. };
  196. #if defined(CATCH_CONFIG_CPP17_BYTE)
  197. template<>
  198. struct StringMaker<std::byte> {
  199. static std::string convert(std::byte value);
  200. };
  201. #endif // defined(CATCH_CONFIG_CPP17_BYTE)
  202. template<>
  203. struct StringMaker<int> {
  204. static std::string convert(int value);
  205. };
  206. template<>
  207. struct StringMaker<long> {
  208. static std::string convert(long value);
  209. };
  210. template<>
  211. struct StringMaker<long long> {
  212. static std::string convert(long long value);
  213. };
  214. template<>
  215. struct StringMaker<unsigned int> {
  216. static std::string convert(unsigned int value);
  217. };
  218. template<>
  219. struct StringMaker<unsigned long> {
  220. static std::string convert(unsigned long value);
  221. };
  222. template<>
  223. struct StringMaker<unsigned long long> {
  224. static std::string convert(unsigned long long value);
  225. };
  226. template<>
  227. struct StringMaker<bool> {
  228. static std::string convert(bool b) {
  229. using namespace std::string_literals;
  230. return b ? "true"s : "false"s;
  231. }
  232. };
  233. template<>
  234. struct StringMaker<char> {
  235. static std::string convert(char c);
  236. };
  237. template<>
  238. struct StringMaker<signed char> {
  239. static std::string convert(signed char c);
  240. };
  241. template<>
  242. struct StringMaker<unsigned char> {
  243. static std::string convert(unsigned char c);
  244. };
  245. template<>
  246. struct StringMaker<std::nullptr_t> {
  247. static std::string convert(std::nullptr_t) {
  248. using namespace std::string_literals;
  249. return "nullptr"s;
  250. }
  251. };
  252. template<>
  253. struct StringMaker<float> {
  254. static std::string convert(float value);
  255. CATCH_EXPORT static int precision;
  256. };
  257. template<>
  258. struct StringMaker<double> {
  259. static std::string convert(double value);
  260. CATCH_EXPORT static int precision;
  261. };
  262. template <typename T>
  263. struct StringMaker<T*> {
  264. template <typename U>
  265. static std::string convert(U* p) {
  266. if (p) {
  267. return ::Catch::Detail::rawMemoryToString(p);
  268. } else {
  269. return "nullptr";
  270. }
  271. }
  272. };
  273. template <typename R, typename C>
  274. struct StringMaker<R C::*> {
  275. static std::string convert(R C::* p) {
  276. if (p) {
  277. return ::Catch::Detail::rawMemoryToString(p);
  278. } else {
  279. return "nullptr";
  280. }
  281. }
  282. };
  283. #if defined(_MANAGED)
  284. template <typename T>
  285. struct StringMaker<T^> {
  286. static std::string convert( T^ ref ) {
  287. return ::Catch::Detail::clrReferenceToString(ref);
  288. }
  289. };
  290. #endif
  291. namespace Detail {
  292. template<typename InputIterator, typename Sentinel = InputIterator>
  293. std::string rangeToString(InputIterator first, Sentinel last) {
  294. ReusableStringStream rss;
  295. rss << "{ ";
  296. if (first != last) {
  297. rss << ::Catch::Detail::stringify(*first);
  298. for (++first; first != last; ++first)
  299. rss << ", " << ::Catch::Detail::stringify(*first);
  300. }
  301. rss << " }";
  302. return rss.str();
  303. }
  304. }
  305. } // namespace Catch
  306. //////////////////////////////////////////////////////
  307. // Separate std-lib types stringification, so it can be selectively enabled
  308. // This means that we do not bring in their headers
  309. #if defined(CATCH_CONFIG_ENABLE_ALL_STRINGMAKERS)
  310. # define CATCH_CONFIG_ENABLE_PAIR_STRINGMAKER
  311. # define CATCH_CONFIG_ENABLE_TUPLE_STRINGMAKER
  312. # define CATCH_CONFIG_ENABLE_VARIANT_STRINGMAKER
  313. # define CATCH_CONFIG_ENABLE_OPTIONAL_STRINGMAKER
  314. #endif
  315. // Separate std::pair specialization
  316. #if defined(CATCH_CONFIG_ENABLE_PAIR_STRINGMAKER)
  317. #include <utility>
  318. namespace Catch {
  319. template<typename T1, typename T2>
  320. struct StringMaker<std::pair<T1, T2> > {
  321. static std::string convert(const std::pair<T1, T2>& pair) {
  322. ReusableStringStream rss;
  323. rss << "{ "
  324. << ::Catch::Detail::stringify(pair.first)
  325. << ", "
  326. << ::Catch::Detail::stringify(pair.second)
  327. << " }";
  328. return rss.str();
  329. }
  330. };
  331. }
  332. #endif // CATCH_CONFIG_ENABLE_PAIR_STRINGMAKER
  333. #if defined(CATCH_CONFIG_ENABLE_OPTIONAL_STRINGMAKER) && defined(CATCH_CONFIG_CPP17_OPTIONAL)
  334. #include <optional>
  335. namespace Catch {
  336. template<typename T>
  337. struct StringMaker<std::optional<T> > {
  338. static std::string convert(const std::optional<T>& optional) {
  339. if (optional.has_value()) {
  340. return ::Catch::Detail::stringify(*optional);
  341. } else {
  342. return "{ }";
  343. }
  344. }
  345. };
  346. }
  347. #endif // CATCH_CONFIG_ENABLE_OPTIONAL_STRINGMAKER
  348. // Separate std::tuple specialization
  349. #if defined(CATCH_CONFIG_ENABLE_TUPLE_STRINGMAKER)
  350. #include <tuple>
  351. namespace Catch {
  352. namespace Detail {
  353. template<
  354. typename Tuple,
  355. std::size_t N = 0,
  356. bool = (N < std::tuple_size<Tuple>::value)
  357. >
  358. struct TupleElementPrinter {
  359. static void print(const Tuple& tuple, std::ostream& os) {
  360. os << (N ? ", " : " ")
  361. << ::Catch::Detail::stringify(std::get<N>(tuple));
  362. TupleElementPrinter<Tuple, N + 1>::print(tuple, os);
  363. }
  364. };
  365. template<
  366. typename Tuple,
  367. std::size_t N
  368. >
  369. struct TupleElementPrinter<Tuple, N, false> {
  370. static void print(const Tuple&, std::ostream&) {}
  371. };
  372. }
  373. template<typename ...Types>
  374. struct StringMaker<std::tuple<Types...>> {
  375. static std::string convert(const std::tuple<Types...>& tuple) {
  376. ReusableStringStream rss;
  377. rss << '{';
  378. Detail::TupleElementPrinter<std::tuple<Types...>>::print(tuple, rss.get());
  379. rss << " }";
  380. return rss.str();
  381. }
  382. };
  383. }
  384. #endif // CATCH_CONFIG_ENABLE_TUPLE_STRINGMAKER
  385. #if defined(CATCH_CONFIG_ENABLE_VARIANT_STRINGMAKER) && defined(CATCH_CONFIG_CPP17_VARIANT)
  386. #include <variant>
  387. namespace Catch {
  388. template<>
  389. struct StringMaker<std::monostate> {
  390. static std::string convert(const std::monostate&) {
  391. return "{ }";
  392. }
  393. };
  394. template<typename... Elements>
  395. struct StringMaker<std::variant<Elements...>> {
  396. static std::string convert(const std::variant<Elements...>& variant) {
  397. if (variant.valueless_by_exception()) {
  398. return "{valueless variant}";
  399. } else {
  400. return std::visit(
  401. [](const auto& value) {
  402. return ::Catch::Detail::stringify(value);
  403. },
  404. variant
  405. );
  406. }
  407. }
  408. };
  409. }
  410. #endif // CATCH_CONFIG_ENABLE_VARIANT_STRINGMAKER
  411. namespace Catch {
  412. // Import begin/ end from std here
  413. using std::begin;
  414. using std::end;
  415. namespace Detail {
  416. template <typename T, typename = void>
  417. struct is_range_impl : std::false_type {};
  418. template <typename T>
  419. struct is_range_impl<T, void_t<decltype(begin(std::declval<T>()))>> : std::true_type {};
  420. } // namespace Detail
  421. template <typename T>
  422. struct is_range : Detail::is_range_impl<T> {};
  423. #if defined(_MANAGED) // Managed types are never ranges
  424. template <typename T>
  425. struct is_range<T^> {
  426. static const bool value = false;
  427. };
  428. #endif
  429. template<typename Range>
  430. std::string rangeToString( Range const& range ) {
  431. return ::Catch::Detail::rangeToString( begin( range ), end( range ) );
  432. }
  433. // Handle vector<bool> specially
  434. template<typename Allocator>
  435. std::string rangeToString( std::vector<bool, Allocator> const& v ) {
  436. ReusableStringStream rss;
  437. rss << "{ ";
  438. bool first = true;
  439. for( bool b : v ) {
  440. if( first )
  441. first = false;
  442. else
  443. rss << ", ";
  444. rss << ::Catch::Detail::stringify( b );
  445. }
  446. rss << " }";
  447. return rss.str();
  448. }
  449. template<typename R>
  450. struct StringMaker<R, std::enable_if_t<is_range<R>::value && !::Catch::Detail::IsStreamInsertable<R>::value>> {
  451. static std::string convert( R const& range ) {
  452. return rangeToString( range );
  453. }
  454. };
  455. template <typename T, size_t SZ>
  456. struct StringMaker<T[SZ]> {
  457. static std::string convert(T const(&arr)[SZ]) {
  458. return rangeToString(arr);
  459. }
  460. };
  461. } // namespace Catch
  462. // Separate std::chrono::duration specialization
  463. #include <ctime>
  464. #include <ratio>
  465. #include <chrono>
  466. namespace Catch {
  467. template <class Ratio>
  468. struct ratio_string {
  469. static std::string symbol() {
  470. Catch::ReusableStringStream rss;
  471. rss << '[' << Ratio::num << '/'
  472. << Ratio::den << ']';
  473. return rss.str();
  474. }
  475. };
  476. template <>
  477. struct ratio_string<std::atto> {
  478. static char symbol() { return 'a'; }
  479. };
  480. template <>
  481. struct ratio_string<std::femto> {
  482. static char symbol() { return 'f'; }
  483. };
  484. template <>
  485. struct ratio_string<std::pico> {
  486. static char symbol() { return 'p'; }
  487. };
  488. template <>
  489. struct ratio_string<std::nano> {
  490. static char symbol() { return 'n'; }
  491. };
  492. template <>
  493. struct ratio_string<std::micro> {
  494. static char symbol() { return 'u'; }
  495. };
  496. template <>
  497. struct ratio_string<std::milli> {
  498. static char symbol() { return 'm'; }
  499. };
  500. ////////////
  501. // std::chrono::duration specializations
  502. template<typename Value, typename Ratio>
  503. struct StringMaker<std::chrono::duration<Value, Ratio>> {
  504. static std::string convert(std::chrono::duration<Value, Ratio> const& duration) {
  505. ReusableStringStream rss;
  506. rss << duration.count() << ' ' << ratio_string<Ratio>::symbol() << 's';
  507. return rss.str();
  508. }
  509. };
  510. template<typename Value>
  511. struct StringMaker<std::chrono::duration<Value, std::ratio<1>>> {
  512. static std::string convert(std::chrono::duration<Value, std::ratio<1>> const& duration) {
  513. ReusableStringStream rss;
  514. rss << duration.count() << " s";
  515. return rss.str();
  516. }
  517. };
  518. template<typename Value>
  519. struct StringMaker<std::chrono::duration<Value, std::ratio<60>>> {
  520. static std::string convert(std::chrono::duration<Value, std::ratio<60>> const& duration) {
  521. ReusableStringStream rss;
  522. rss << duration.count() << " m";
  523. return rss.str();
  524. }
  525. };
  526. template<typename Value>
  527. struct StringMaker<std::chrono::duration<Value, std::ratio<3600>>> {
  528. static std::string convert(std::chrono::duration<Value, std::ratio<3600>> const& duration) {
  529. ReusableStringStream rss;
  530. rss << duration.count() << " h";
  531. return rss.str();
  532. }
  533. };
  534. ////////////
  535. // std::chrono::time_point specialization
  536. // Generic time_point cannot be specialized, only std::chrono::time_point<system_clock>
  537. template<typename Clock, typename Duration>
  538. struct StringMaker<std::chrono::time_point<Clock, Duration>> {
  539. static std::string convert(std::chrono::time_point<Clock, Duration> const& time_point) {
  540. return ::Catch::Detail::stringify(time_point.time_since_epoch()) + " since epoch";
  541. }
  542. };
  543. // std::chrono::time_point<system_clock> specialization
  544. template<typename Duration>
  545. struct StringMaker<std::chrono::time_point<std::chrono::system_clock, Duration>> {
  546. static std::string convert(std::chrono::time_point<std::chrono::system_clock, Duration> const& time_point) {
  547. auto converted = std::chrono::system_clock::to_time_t(time_point);
  548. #ifdef _MSC_VER
  549. std::tm timeInfo = {};
  550. gmtime_s(&timeInfo, &converted);
  551. #else
  552. std::tm* timeInfo = std::gmtime(&converted);
  553. #endif
  554. auto const timeStampSize = sizeof("2017-01-16T17:06:45Z");
  555. char timeStamp[timeStampSize];
  556. const char * const fmt = "%Y-%m-%dT%H:%M:%SZ";
  557. #ifdef _MSC_VER
  558. std::strftime(timeStamp, timeStampSize, fmt, &timeInfo);
  559. #else
  560. std::strftime(timeStamp, timeStampSize, fmt, timeInfo);
  561. #endif
  562. return std::string(timeStamp, timeStampSize - 1);
  563. }
  564. };
  565. }
  566. #include <catch2/interfaces/catch_interfaces_registry_hub.hpp>
  567. #define INTERNAL_CATCH_REGISTER_ENUM( enumName, ... ) \
  568. namespace Catch { \
  569. template<> struct StringMaker<enumName> { \
  570. static std::string convert( enumName value ) { \
  571. static const auto& enumInfo = ::Catch::getMutableRegistryHub().getMutableEnumValuesRegistry().registerEnum( #enumName, #__VA_ARGS__, { __VA_ARGS__ } ); \
  572. return static_cast<std::string>(enumInfo.lookup( static_cast<int>( value ) )); \
  573. } \
  574. }; \
  575. }
  576. #define CATCH_REGISTER_ENUM( enumName, ... ) INTERNAL_CATCH_REGISTER_ENUM( enumName, __VA_ARGS__ )
  577. #ifdef _MSC_VER
  578. #pragma warning(pop)
  579. #endif
  580. #endif // CATCH_TOSTRING_HPP_INCLUDED