Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2025-01-18 09:54:08

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 c);
0283     };
0284     template<>
0285     struct StringMaker<unsigned char> {
0286         static std::string convert(unsigned char c);
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 }
0402 #endif // CATCH_CONFIG_ENABLE_OPTIONAL_STRINGMAKER
0403 
0404 // Separate std::tuple specialization
0405 #if defined(CATCH_CONFIG_ENABLE_TUPLE_STRINGMAKER)
0406 #include <tuple>
0407 namespace Catch {
0408     namespace Detail {
0409         template<
0410             typename Tuple,
0411             std::size_t N = 0,
0412             bool = (N < std::tuple_size<Tuple>::value)
0413             >
0414             struct TupleElementPrinter {
0415             static void print(const Tuple& tuple, std::ostream& os) {
0416                 os << (N ? ", " : " ")
0417                     << ::Catch::Detail::stringify(std::get<N>(tuple));
0418                 TupleElementPrinter<Tuple, N + 1>::print(tuple, os);
0419             }
0420         };
0421 
0422         template<
0423             typename Tuple,
0424             std::size_t N
0425         >
0426             struct TupleElementPrinter<Tuple, N, false> {
0427             static void print(const Tuple&, std::ostream&) {}
0428         };
0429 
0430     }
0431 
0432 
0433     template<typename ...Types>
0434     struct StringMaker<std::tuple<Types...>> {
0435         static std::string convert(const std::tuple<Types...>& tuple) {
0436             ReusableStringStream rss;
0437             rss << '{';
0438             Detail::TupleElementPrinter<std::tuple<Types...>>::print(tuple, rss.get());
0439             rss << " }";
0440             return rss.str();
0441         }
0442     };
0443 }
0444 #endif // CATCH_CONFIG_ENABLE_TUPLE_STRINGMAKER
0445 
0446 #if defined(CATCH_CONFIG_ENABLE_VARIANT_STRINGMAKER) && defined(CATCH_CONFIG_CPP17_VARIANT)
0447 #include <variant>
0448 namespace Catch {
0449     template<>
0450     struct StringMaker<std::monostate> {
0451         static std::string convert(const std::monostate&) {
0452             return "{ }";
0453         }
0454     };
0455 
0456     template<typename... Elements>
0457     struct StringMaker<std::variant<Elements...>> {
0458         static std::string convert(const std::variant<Elements...>& variant) {
0459             if (variant.valueless_by_exception()) {
0460                 return "{valueless variant}";
0461             } else {
0462                 return std::visit(
0463                     [](const auto& value) {
0464                         return ::Catch::Detail::stringify(value);
0465                     },
0466                     variant
0467                 );
0468             }
0469         }
0470     };
0471 }
0472 #endif // CATCH_CONFIG_ENABLE_VARIANT_STRINGMAKER
0473 
0474 namespace Catch {
0475     // Import begin/ end from std here
0476     using std::begin;
0477     using std::end;
0478 
0479     namespace Detail {
0480         template <typename T, typename = void>
0481         struct is_range_impl : std::false_type {};
0482 
0483         template <typename T>
0484         struct is_range_impl<T, void_t<decltype(begin(std::declval<T>()))>> : std::true_type {};
0485     } // namespace Detail
0486 
0487     template <typename T>
0488     struct is_range : Detail::is_range_impl<T> {};
0489 
0490 #if defined(_MANAGED) // Managed types are never ranges
0491     template <typename T>
0492     struct is_range<T^> {
0493         static const bool value = false;
0494     };
0495 #endif
0496 
0497     template<typename Range>
0498     std::string rangeToString( Range const& range ) {
0499         return ::Catch::Detail::rangeToString( begin( range ), end( range ) );
0500     }
0501 
0502     // Handle vector<bool> specially
0503     template<typename Allocator>
0504     std::string rangeToString( std::vector<bool, Allocator> const& v ) {
0505         ReusableStringStream rss;
0506         rss << "{ ";
0507         bool first = true;
0508         for( bool b : v ) {
0509             if( first )
0510                 first = false;
0511             else
0512                 rss << ", ";
0513             rss << ::Catch::Detail::stringify( b );
0514         }
0515         rss << " }";
0516         return rss.str();
0517     }
0518 
0519     template<typename R>
0520     struct StringMaker<R, std::enable_if_t<is_range<R>::value && !::Catch::Detail::IsStreamInsertable<R>::value>> {
0521         static std::string convert( R const& range ) {
0522             return rangeToString( range );
0523         }
0524     };
0525 
0526     template <typename T, size_t SZ>
0527     struct StringMaker<T[SZ]> {
0528         static std::string convert(T const(&arr)[SZ]) {
0529             return rangeToString(arr);
0530         }
0531     };
0532 
0533 
0534 } // namespace Catch
0535 
0536 // Separate std::chrono::duration specialization
0537 #include <ctime>
0538 #include <ratio>
0539 #include <chrono>
0540 
0541 
0542 namespace Catch {
0543 
0544 template <class Ratio>
0545 struct ratio_string {
0546     static std::string symbol() {
0547         Catch::ReusableStringStream rss;
0548         rss << '[' << Ratio::num << '/'
0549             << Ratio::den << ']';
0550         return rss.str();
0551     }
0552 };
0553 
0554 template <>
0555 struct ratio_string<std::atto> {
0556     static char symbol() { return 'a'; }
0557 };
0558 template <>
0559 struct ratio_string<std::femto> {
0560     static char symbol() { return 'f'; }
0561 };
0562 template <>
0563 struct ratio_string<std::pico> {
0564     static char symbol() { return 'p'; }
0565 };
0566 template <>
0567 struct ratio_string<std::nano> {
0568     static char symbol() { return 'n'; }
0569 };
0570 template <>
0571 struct ratio_string<std::micro> {
0572     static char symbol() { return 'u'; }
0573 };
0574 template <>
0575 struct ratio_string<std::milli> {
0576     static char symbol() { return 'm'; }
0577 };
0578 
0579     ////////////
0580     // std::chrono::duration specializations
0581     template<typename Value, typename Ratio>
0582     struct StringMaker<std::chrono::duration<Value, Ratio>> {
0583         static std::string convert(std::chrono::duration<Value, Ratio> const& duration) {
0584             ReusableStringStream rss;
0585             rss << duration.count() << ' ' << ratio_string<Ratio>::symbol() << 's';
0586             return rss.str();
0587         }
0588     };
0589     template<typename Value>
0590     struct StringMaker<std::chrono::duration<Value, std::ratio<1>>> {
0591         static std::string convert(std::chrono::duration<Value, std::ratio<1>> const& duration) {
0592             ReusableStringStream rss;
0593             rss << duration.count() << " s";
0594             return rss.str();
0595         }
0596     };
0597     template<typename Value>
0598     struct StringMaker<std::chrono::duration<Value, std::ratio<60>>> {
0599         static std::string convert(std::chrono::duration<Value, std::ratio<60>> const& duration) {
0600             ReusableStringStream rss;
0601             rss << duration.count() << " m";
0602             return rss.str();
0603         }
0604     };
0605     template<typename Value>
0606     struct StringMaker<std::chrono::duration<Value, std::ratio<3600>>> {
0607         static std::string convert(std::chrono::duration<Value, std::ratio<3600>> const& duration) {
0608             ReusableStringStream rss;
0609             rss << duration.count() << " h";
0610             return rss.str();
0611         }
0612     };
0613 
0614     ////////////
0615     // std::chrono::time_point specialization
0616     // Generic time_point cannot be specialized, only std::chrono::time_point<system_clock>
0617     template<typename Clock, typename Duration>
0618     struct StringMaker<std::chrono::time_point<Clock, Duration>> {
0619         static std::string convert(std::chrono::time_point<Clock, Duration> const& time_point) {
0620             return ::Catch::Detail::stringify(time_point.time_since_epoch()) + " since epoch";
0621         }
0622     };
0623     // std::chrono::time_point<system_clock> specialization
0624     template<typename Duration>
0625     struct StringMaker<std::chrono::time_point<std::chrono::system_clock, Duration>> {
0626         static std::string convert(std::chrono::time_point<std::chrono::system_clock, Duration> const& time_point) {
0627             auto converted = std::chrono::system_clock::to_time_t(time_point);
0628 
0629 #ifdef _MSC_VER
0630             std::tm timeInfo = {};
0631             gmtime_s(&timeInfo, &converted);
0632 #else
0633             std::tm* timeInfo = std::gmtime(&converted);
0634 #endif
0635 
0636             auto const timeStampSize = sizeof("2017-01-16T17:06:45Z");
0637             char timeStamp[timeStampSize];
0638             const char * const fmt = "%Y-%m-%dT%H:%M:%SZ";
0639 
0640 #ifdef _MSC_VER
0641             std::strftime(timeStamp, timeStampSize, fmt, &timeInfo);
0642 #else
0643             std::strftime(timeStamp, timeStampSize, fmt, timeInfo);
0644 #endif
0645             return std::string(timeStamp, timeStampSize - 1);
0646         }
0647     };
0648 }
0649 
0650 #include <catch2/interfaces/catch_interfaces_registry_hub.hpp>
0651 
0652 #define INTERNAL_CATCH_REGISTER_ENUM( enumName, ... ) \
0653 namespace Catch { \
0654     template<> struct StringMaker<enumName> { \
0655         static std::string convert( enumName value ) { \
0656             static const auto& enumInfo = ::Catch::getMutableRegistryHub().getMutableEnumValuesRegistry().registerEnum( #enumName, #__VA_ARGS__, { __VA_ARGS__ } ); \
0657             return static_cast<std::string>(enumInfo.lookup( static_cast<int>( value ) )); \
0658         } \
0659     }; \
0660 }
0661 
0662 #define CATCH_REGISTER_ENUM( enumName, ... ) INTERNAL_CATCH_REGISTER_ENUM( enumName, __VA_ARGS__ )
0663 
0664 #ifdef _MSC_VER
0665 #pragma warning(pop)
0666 #endif
0667 
0668 #endif // CATCH_TOSTRING_HPP_INCLUDED