Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2025-09-15 08:54:37

0001 
0002 //              Copyright Catch2 Authors
0003 // Distributed under the Boost Software License, Version 1.0.
0004 //   (See accompanying file LICENSE.txt or copy at
0005 //        https://www.boost.org/LICENSE_1_0.txt)
0006 
0007 // SPDX-License-Identifier: BSL-1.0
0008 #ifndef CATCH_TOSTRING_HPP_INCLUDED
0009 #define CATCH_TOSTRING_HPP_INCLUDED
0010 
0011 
0012 #include <vector>
0013 #include <cstddef>
0014 #include <type_traits>
0015 #include <string>
0016 
0017 #include <catch2/internal/catch_compiler_capabilities.hpp>
0018 #include <catch2/internal/catch_config_wchar.hpp>
0019 #include <catch2/internal/catch_reusable_string_stream.hpp>
0020 #include <catch2/internal/catch_void_type.hpp>
0021 #include <catch2/interfaces/catch_interfaces_enum_values_registry.hpp>
0022 
0023 #ifdef CATCH_CONFIG_CPP17_STRING_VIEW
0024 #include <string_view>
0025 #endif
0026 
0027 #ifdef _MSC_VER
0028 #pragma warning(push)
0029 #pragma warning(disable:4180) // We attempt to stream a function (address) by const&, which MSVC complains about but is harmless
0030 #endif
0031 
0032 // We need a dummy global operator<< so we can bring it into Catch namespace later
0033 struct Catch_global_namespace_dummy{};
0034 std::ostream& operator<<(std::ostream&, Catch_global_namespace_dummy);
0035 
0036 namespace Catch {
0037     // Bring in global namespace operator<< for ADL lookup in
0038     // `IsStreamInsertable` below.
0039     using ::operator<<;
0040 
0041     namespace Detail {
0042 
0043         inline std::size_t catch_strnlen(const char *str, std::size_t n) {
0044             auto ret = std::char_traits<char>::find(str, n, '\0');
0045             if (ret != nullptr) {
0046                 return static_cast<std::size_t>(ret - str);
0047             }
0048             return n;
0049         }
0050 
0051         constexpr StringRef unprintableString = "{?}"_sr;
0052 
0053         //! Encases `string in quotes, and optionally escapes invisibles
0054         std::string convertIntoString( StringRef string, bool escapeInvisibles );
0055 
0056         //! Encases `string` in quotes, and escapes invisibles if user requested
0057         //! it via CLI
0058         std::string convertIntoString( StringRef string );
0059 
0060         std::string rawMemoryToString( const void *object, std::size_t size );
0061 
0062         template<typename T>
0063         std::string rawMemoryToString( const T& object ) {
0064           return rawMemoryToString( &object, sizeof(object) );
0065         }
0066 
0067         template<typename T>
0068         class IsStreamInsertable {
0069             template<typename Stream, typename U>
0070             static auto test(int)
0071                 -> decltype(std::declval<Stream&>() << std::declval<U>(), std::true_type());
0072 
0073             template<typename, typename>
0074             static auto test(...)->std::false_type;
0075 
0076         public:
0077             static const bool value = decltype(test<std::ostream, const T&>(0))::value;
0078         };
0079 
0080         template<typename E>
0081         std::string convertUnknownEnumToString( E e );
0082 
0083         template<typename T>
0084         std::enable_if_t<
0085             !std::is_enum<T>::value && !std::is_base_of<std::exception, T>::value,
0086         std::string> convertUnstreamable( T const& ) {
0087             return std::string(Detail::unprintableString);
0088         }
0089         template<typename T>
0090         std::enable_if_t<
0091             !std::is_enum<T>::value && std::is_base_of<std::exception, T>::value,
0092          std::string> convertUnstreamable(T const& ex) {
0093             return ex.what();
0094         }
0095 
0096 
0097         template<typename T>
0098         std::enable_if_t<
0099             std::is_enum<T>::value,
0100         std::string> convertUnstreamable( T const& value ) {
0101             return convertUnknownEnumToString( value );
0102         }
0103 
0104 #if defined(_MANAGED)
0105         //! Convert a CLR string to a utf8 std::string
0106         template<typename T>
0107         std::string clrReferenceToString( T^ ref ) {
0108             if (ref == nullptr)
0109                 return std::string("null");
0110             auto bytes = System::Text::Encoding::UTF8->GetBytes(ref->ToString());
0111             cli::pin_ptr<System::Byte> p = &bytes[0];
0112             return std::string(reinterpret_cast<char const *>(p), bytes->Length);
0113         }
0114 #endif
0115 
0116     } // namespace Detail
0117 
0118 
0119     template <typename T, typename = void>
0120     struct StringMaker {
0121         template <typename Fake = T>
0122         static
0123         std::enable_if_t<::Catch::Detail::IsStreamInsertable<Fake>::value, std::string>
0124             convert(const Fake& value) {
0125                 ReusableStringStream rss;
0126                 // NB: call using the function-like syntax to avoid ambiguity with
0127                 // user-defined templated operator<< under clang.
0128                 rss.operator<<(value);
0129                 return rss.str();
0130         }
0131 
0132         template <typename Fake = T>
0133         static
0134         std::enable_if_t<!::Catch::Detail::IsStreamInsertable<Fake>::value, std::string>
0135             convert( const Fake& value ) {
0136 #if !defined(CATCH_CONFIG_FALLBACK_STRINGIFIER)
0137             return Detail::convertUnstreamable(value);
0138 #else
0139             return CATCH_CONFIG_FALLBACK_STRINGIFIER(value);
0140 #endif
0141         }
0142     };
0143 
0144     namespace Detail {
0145 
0146         // This function dispatches all stringification requests inside of Catch.
0147         // Should be preferably called fully qualified, like ::Catch::Detail::stringify
0148         template <typename T>
0149         std::string stringify(const T& e) {
0150             return ::Catch::StringMaker<std::remove_cv_t<std::remove_reference_t<T>>>::convert(e);
0151         }
0152 
0153         template<typename E>
0154         std::string convertUnknownEnumToString( E e ) {
0155             return ::Catch::Detail::stringify(static_cast<std::underlying_type_t<E>>(e));
0156         }
0157 
0158 #if defined(_MANAGED)
0159         template <typename T>
0160         std::string stringify( T^ e ) {
0161             return ::Catch::StringMaker<T^>::convert(e);
0162         }
0163 #endif
0164 
0165     } // namespace Detail
0166 
0167     // Some predefined specializations
0168 
0169     template<>
0170     struct StringMaker<std::string> {
0171         static std::string convert(const std::string& str);
0172     };
0173 
0174 #ifdef CATCH_CONFIG_CPP17_STRING_VIEW
0175     template<>
0176     struct StringMaker<std::string_view> {
0177         static std::string convert(std::string_view str);
0178     };
0179 #endif
0180 
0181     template<>
0182     struct StringMaker<char const *> {
0183         static std::string convert(char const * str);
0184     };
0185     template<>
0186     struct StringMaker<char *> {
0187         static std::string convert(char * str);
0188     };
0189 
0190 #if defined(CATCH_CONFIG_WCHAR)
0191     template<>
0192     struct StringMaker<std::wstring> {
0193         static std::string convert(const std::wstring& wstr);
0194     };
0195 
0196 # ifdef CATCH_CONFIG_CPP17_STRING_VIEW
0197     template<>
0198     struct StringMaker<std::wstring_view> {
0199         static std::string convert(std::wstring_view str);
0200     };
0201 # endif
0202 
0203     template<>
0204     struct StringMaker<wchar_t const *> {
0205         static std::string convert(wchar_t const * str);
0206     };
0207     template<>
0208     struct StringMaker<wchar_t *> {
0209         static std::string convert(wchar_t * str);
0210     };
0211 #endif // CATCH_CONFIG_WCHAR
0212 
0213     template<size_t SZ>
0214     struct StringMaker<char[SZ]> {
0215         static std::string convert(char const* str) {
0216             return Detail::convertIntoString(
0217                 StringRef( str, Detail::catch_strnlen( str, SZ ) ) );
0218         }
0219     };
0220     template<size_t SZ>
0221     struct StringMaker<signed char[SZ]> {
0222         static std::string convert(signed char const* str) {
0223             auto reinterpreted = reinterpret_cast<char const*>(str);
0224             return Detail::convertIntoString(
0225                 StringRef(reinterpreted, Detail::catch_strnlen(reinterpreted, SZ)));
0226         }
0227     };
0228     template<size_t SZ>
0229     struct StringMaker<unsigned char[SZ]> {
0230         static std::string convert(unsigned char const* str) {
0231             auto reinterpreted = reinterpret_cast<char const*>(str);
0232             return Detail::convertIntoString(
0233                 StringRef(reinterpreted, Detail::catch_strnlen(reinterpreted, SZ)));
0234         }
0235     };
0236 
0237 #if defined(CATCH_CONFIG_CPP17_BYTE)
0238     template<>
0239     struct StringMaker<std::byte> {
0240         static std::string convert(std::byte value);
0241     };
0242 #endif // defined(CATCH_CONFIG_CPP17_BYTE)
0243     template<>
0244     struct StringMaker<int> {
0245         static std::string convert(int value);
0246     };
0247     template<>
0248     struct StringMaker<long> {
0249         static std::string convert(long value);
0250     };
0251     template<>
0252     struct StringMaker<long long> {
0253         static std::string convert(long long value);
0254     };
0255     template<>
0256     struct StringMaker<unsigned int> {
0257         static std::string convert(unsigned int value);
0258     };
0259     template<>
0260     struct StringMaker<unsigned long> {
0261         static std::string convert(unsigned long value);
0262     };
0263     template<>
0264     struct StringMaker<unsigned long long> {
0265         static std::string convert(unsigned long long value);
0266     };
0267 
0268     template<>
0269     struct StringMaker<bool> {
0270         static std::string convert(bool b) {
0271             using namespace std::string_literals;
0272             return b ? "true"s : "false"s;
0273         }
0274     };
0275 
0276     template<>
0277     struct StringMaker<char> {
0278         static std::string convert(char c);
0279     };
0280     template<>
0281     struct StringMaker<signed char> {
0282         static std::string convert(signed char value);
0283     };
0284     template<>
0285     struct StringMaker<unsigned char> {
0286         static std::string convert(unsigned char value);
0287     };
0288 
0289     template<>
0290     struct StringMaker<std::nullptr_t> {
0291         static std::string convert(std::nullptr_t) {
0292             using namespace std::string_literals;
0293             return "nullptr"s;
0294         }
0295     };
0296 
0297     template<>
0298     struct StringMaker<float> {
0299         static std::string convert(float value);
0300         CATCH_EXPORT static int precision;
0301     };
0302 
0303     template<>
0304     struct StringMaker<double> {
0305         static std::string convert(double value);
0306         CATCH_EXPORT static int precision;
0307     };
0308 
0309     template <typename T>
0310     struct StringMaker<T*> {
0311         template <typename U>
0312         static std::string convert(U* p) {
0313             if (p) {
0314                 return ::Catch::Detail::rawMemoryToString(p);
0315             } else {
0316                 return "nullptr";
0317             }
0318         }
0319     };
0320 
0321     template <typename R, typename C>
0322     struct StringMaker<R C::*> {
0323         static std::string convert(R C::* p) {
0324             if (p) {
0325                 return ::Catch::Detail::rawMemoryToString(p);
0326             } else {
0327                 return "nullptr";
0328             }
0329         }
0330     };
0331 
0332 #if defined(_MANAGED)
0333     template <typename T>
0334     struct StringMaker<T^> {
0335         static std::string convert( T^ ref ) {
0336             return ::Catch::Detail::clrReferenceToString(ref);
0337         }
0338     };
0339 #endif
0340 
0341     namespace Detail {
0342         template<typename InputIterator, typename Sentinel = InputIterator>
0343         std::string rangeToString(InputIterator first, Sentinel last) {
0344             ReusableStringStream rss;
0345             rss << "{ ";
0346             if (first != last) {
0347                 rss << ::Catch::Detail::stringify(*first);
0348                 for (++first; first != last; ++first)
0349                     rss << ", " << ::Catch::Detail::stringify(*first);
0350             }
0351             rss << " }";
0352             return rss.str();
0353         }
0354     }
0355 
0356 } // namespace Catch
0357 
0358 //////////////////////////////////////////////////////
0359 // Separate std-lib types stringification, so it can be selectively enabled
0360 // This means that we do not bring in their headers
0361 
0362 #if defined(CATCH_CONFIG_ENABLE_ALL_STRINGMAKERS)
0363 #  define CATCH_CONFIG_ENABLE_PAIR_STRINGMAKER
0364 #  define CATCH_CONFIG_ENABLE_TUPLE_STRINGMAKER
0365 #  define CATCH_CONFIG_ENABLE_VARIANT_STRINGMAKER
0366 #  define CATCH_CONFIG_ENABLE_OPTIONAL_STRINGMAKER
0367 #endif
0368 
0369 // Separate std::pair specialization
0370 #if defined(CATCH_CONFIG_ENABLE_PAIR_STRINGMAKER)
0371 #include <utility>
0372 namespace Catch {
0373     template<typename T1, typename T2>
0374     struct StringMaker<std::pair<T1, T2> > {
0375         static std::string convert(const std::pair<T1, T2>& pair) {
0376             ReusableStringStream rss;
0377             rss << "{ "
0378                 << ::Catch::Detail::stringify(pair.first)
0379                 << ", "
0380                 << ::Catch::Detail::stringify(pair.second)
0381                 << " }";
0382             return rss.str();
0383         }
0384     };
0385 }
0386 #endif // CATCH_CONFIG_ENABLE_PAIR_STRINGMAKER
0387 
0388 #if defined(CATCH_CONFIG_ENABLE_OPTIONAL_STRINGMAKER) && defined(CATCH_CONFIG_CPP17_OPTIONAL)
0389 #include <optional>
0390 namespace Catch {
0391     template<typename T>
0392     struct StringMaker<std::optional<T> > {
0393         static std::string convert(const std::optional<T>& optional) {
0394             if (optional.has_value()) {
0395                 return ::Catch::Detail::stringify(*optional);
0396             } else {
0397                 return "{ }";
0398             }
0399         }
0400     };
0401     template <>
0402     struct StringMaker<std::nullopt_t> {
0403         static std::string convert(const std::nullopt_t&) {
0404             return "{ }";
0405         }
0406     };
0407 }
0408 #endif // CATCH_CONFIG_ENABLE_OPTIONAL_STRINGMAKER
0409 
0410 // Separate std::tuple specialization
0411 #if defined(CATCH_CONFIG_ENABLE_TUPLE_STRINGMAKER)
0412 #include <tuple>
0413 namespace Catch {
0414     namespace Detail {
0415         template<
0416             typename Tuple,
0417             std::size_t N = 0,
0418             bool = (N < std::tuple_size<Tuple>::value)
0419             >
0420             struct TupleElementPrinter {
0421             static void print(const Tuple& tuple, std::ostream& os) {
0422                 os << (N ? ", " : " ")
0423                     << ::Catch::Detail::stringify(std::get<N>(tuple));
0424                 TupleElementPrinter<Tuple, N + 1>::print(tuple, os);
0425             }
0426         };
0427 
0428         template<
0429             typename Tuple,
0430             std::size_t N
0431         >
0432             struct TupleElementPrinter<Tuple, N, false> {
0433             static void print(const Tuple&, std::ostream&) {}
0434         };
0435 
0436     }
0437 
0438 
0439     template<typename ...Types>
0440     struct StringMaker<std::tuple<Types...>> {
0441         static std::string convert(const std::tuple<Types...>& tuple) {
0442             ReusableStringStream rss;
0443             rss << '{';
0444             Detail::TupleElementPrinter<std::tuple<Types...>>::print(tuple, rss.get());
0445             rss << " }";
0446             return rss.str();
0447         }
0448     };
0449 }
0450 #endif // CATCH_CONFIG_ENABLE_TUPLE_STRINGMAKER
0451 
0452 #if defined(CATCH_CONFIG_ENABLE_VARIANT_STRINGMAKER) && defined(CATCH_CONFIG_CPP17_VARIANT)
0453 #include <variant>
0454 namespace Catch {
0455     template<>
0456     struct StringMaker<std::monostate> {
0457         static std::string convert(const std::monostate&) {
0458             return "{ }";
0459         }
0460     };
0461 
0462     template<typename... Elements>
0463     struct StringMaker<std::variant<Elements...>> {
0464         static std::string convert(const std::variant<Elements...>& variant) {
0465             if (variant.valueless_by_exception()) {
0466                 return "{valueless variant}";
0467             } else {
0468                 return std::visit(
0469                     [](const auto& value) {
0470                         return ::Catch::Detail::stringify(value);
0471                     },
0472                     variant
0473                 );
0474             }
0475         }
0476     };
0477 }
0478 #endif // CATCH_CONFIG_ENABLE_VARIANT_STRINGMAKER
0479 
0480 namespace Catch {
0481     // Import begin/ end from std here
0482     using std::begin;
0483     using std::end;
0484 
0485     namespace Detail {
0486         template <typename T, typename = void>
0487         struct is_range_impl : std::false_type {};
0488 
0489         template <typename T>
0490         struct is_range_impl<T, void_t<decltype(begin(std::declval<T>()))>> : std::true_type {};
0491     } // namespace Detail
0492 
0493     template <typename T>
0494     struct is_range : Detail::is_range_impl<T> {};
0495 
0496 #if defined(_MANAGED) // Managed types are never ranges
0497     template <typename T>
0498     struct is_range<T^> {
0499         static const bool value = false;
0500     };
0501 #endif
0502 
0503     template<typename Range>
0504     std::string rangeToString( Range const& range ) {
0505         return ::Catch::Detail::rangeToString( begin( range ), end( range ) );
0506     }
0507 
0508     // Handle vector<bool> specially
0509     template<typename Allocator>
0510     std::string rangeToString( std::vector<bool, Allocator> const& v ) {
0511         ReusableStringStream rss;
0512         rss << "{ ";
0513         bool first = true;
0514         for( bool b : v ) {
0515             if( first )
0516                 first = false;
0517             else
0518                 rss << ", ";
0519             rss << ::Catch::Detail::stringify( b );
0520         }
0521         rss << " }";
0522         return rss.str();
0523     }
0524 
0525     template<typename R>
0526     struct StringMaker<R, std::enable_if_t<is_range<R>::value && !::Catch::Detail::IsStreamInsertable<R>::value>> {
0527         static std::string convert( R const& range ) {
0528             return rangeToString( range );
0529         }
0530     };
0531 
0532     template <typename T, size_t SZ>
0533     struct StringMaker<T[SZ]> {
0534         static std::string convert(T const(&arr)[SZ]) {
0535             return rangeToString(arr);
0536         }
0537     };
0538 
0539 
0540 } // namespace Catch
0541 
0542 // Separate std::chrono::duration specialization
0543 #include <ctime>
0544 #include <ratio>
0545 #include <chrono>
0546 
0547 
0548 namespace Catch {
0549 
0550 template <class Ratio>
0551 struct ratio_string {
0552     static std::string symbol() {
0553         Catch::ReusableStringStream rss;
0554         rss << '[' << Ratio::num << '/'
0555             << Ratio::den << ']';
0556         return rss.str();
0557     }
0558 };
0559 
0560 template <>
0561 struct ratio_string<std::atto> {
0562     static char symbol() { return 'a'; }
0563 };
0564 template <>
0565 struct ratio_string<std::femto> {
0566     static char symbol() { return 'f'; }
0567 };
0568 template <>
0569 struct ratio_string<std::pico> {
0570     static char symbol() { return 'p'; }
0571 };
0572 template <>
0573 struct ratio_string<std::nano> {
0574     static char symbol() { return 'n'; }
0575 };
0576 template <>
0577 struct ratio_string<std::micro> {
0578     static char symbol() { return 'u'; }
0579 };
0580 template <>
0581 struct ratio_string<std::milli> {
0582     static char symbol() { return 'm'; }
0583 };
0584 
0585     ////////////
0586     // std::chrono::duration specializations
0587     template<typename Value, typename Ratio>
0588     struct StringMaker<std::chrono::duration<Value, Ratio>> {
0589         static std::string convert(std::chrono::duration<Value, Ratio> const& duration) {
0590             ReusableStringStream rss;
0591             rss << duration.count() << ' ' << ratio_string<Ratio>::symbol() << 's';
0592             return rss.str();
0593         }
0594     };
0595     template<typename Value>
0596     struct StringMaker<std::chrono::duration<Value, std::ratio<1>>> {
0597         static std::string convert(std::chrono::duration<Value, std::ratio<1>> const& duration) {
0598             ReusableStringStream rss;
0599             rss << duration.count() << " s";
0600             return rss.str();
0601         }
0602     };
0603     template<typename Value>
0604     struct StringMaker<std::chrono::duration<Value, std::ratio<60>>> {
0605         static std::string convert(std::chrono::duration<Value, std::ratio<60>> const& duration) {
0606             ReusableStringStream rss;
0607             rss << duration.count() << " m";
0608             return rss.str();
0609         }
0610     };
0611     template<typename Value>
0612     struct StringMaker<std::chrono::duration<Value, std::ratio<3600>>> {
0613         static std::string convert(std::chrono::duration<Value, std::ratio<3600>> const& duration) {
0614             ReusableStringStream rss;
0615             rss << duration.count() << " h";
0616             return rss.str();
0617         }
0618     };
0619 
0620     ////////////
0621     // std::chrono::time_point specialization
0622     // Generic time_point cannot be specialized, only std::chrono::time_point<system_clock>
0623     template<typename Clock, typename Duration>
0624     struct StringMaker<std::chrono::time_point<Clock, Duration>> {
0625         static std::string convert(std::chrono::time_point<Clock, Duration> const& time_point) {
0626             return ::Catch::Detail::stringify(time_point.time_since_epoch()) + " since epoch";
0627         }
0628     };
0629     // std::chrono::time_point<system_clock> specialization
0630     template<typename Duration>
0631     struct StringMaker<std::chrono::time_point<std::chrono::system_clock, Duration>> {
0632         static std::string convert(std::chrono::time_point<std::chrono::system_clock, Duration> const& time_point) {
0633             auto converted = std::chrono::system_clock::to_time_t(time_point);
0634 
0635 #ifdef _MSC_VER
0636             std::tm timeInfo = {};
0637             const auto err = gmtime_s(&timeInfo, &converted);
0638             if ( err ) {
0639                 return "gmtime from provided timepoint has failed. This "
0640                        "happens e.g. with pre-1970 dates using Microsoft libc";
0641             }
0642 #else
0643             std::tm* timeInfo = std::gmtime(&converted);
0644 #endif
0645 
0646             auto const timeStampSize = sizeof("2017-01-16T17:06:45Z");
0647             char timeStamp[timeStampSize];
0648             const char * const fmt = "%Y-%m-%dT%H:%M:%SZ";
0649 
0650 #ifdef _MSC_VER
0651             std::strftime(timeStamp, timeStampSize, fmt, &timeInfo);
0652 #else
0653             std::strftime(timeStamp, timeStampSize, fmt, timeInfo);
0654 #endif
0655             return std::string(timeStamp, timeStampSize - 1);
0656         }
0657     };
0658 }
0659 
0660 #include <catch2/interfaces/catch_interfaces_registry_hub.hpp>
0661 
0662 #define INTERNAL_CATCH_REGISTER_ENUM( enumName, ... ) \
0663 namespace Catch { \
0664     template<> struct StringMaker<enumName> { \
0665         static std::string convert( enumName value ) { \
0666             static const auto& enumInfo = ::Catch::getMutableRegistryHub().getMutableEnumValuesRegistry().registerEnum( #enumName, #__VA_ARGS__, { __VA_ARGS__ } ); \
0667             return static_cast<std::string>(enumInfo.lookup( static_cast<int>( value ) )); \
0668         } \
0669     }; \
0670 }
0671 
0672 #define CATCH_REGISTER_ENUM( enumName, ... ) INTERNAL_CATCH_REGISTER_ENUM( enumName, __VA_ARGS__ )
0673 
0674 #ifdef _MSC_VER
0675 #pragma warning(pop)
0676 #endif
0677 
0678 #endif // CATCH_TOSTRING_HPP_INCLUDED