diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 1e3af147a8..554346f49a 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -98,6 +98,7 @@ set(IMPL_HEADERS ${SOURCES_DIR}/internal/catch_jsonwriter.hpp ${SOURCES_DIR}/internal/catch_lazy_expr.hpp ${SOURCES_DIR}/internal/catch_leak_detector.hpp + ${SOURCES_DIR}/internal/catch_lifetimebound.hpp ${SOURCES_DIR}/internal/catch_list.hpp ${SOURCES_DIR}/internal/catch_logical_traits.hpp ${SOURCES_DIR}/internal/catch_message_info.hpp diff --git a/src/catch2/catch_all.hpp b/src/catch2/catch_all.hpp index 2417d8567e..3dc4609e66 100644 --- a/src/catch2/catch_all.hpp +++ b/src/catch2/catch_all.hpp @@ -79,6 +79,7 @@ #include #include #include +#include #include #include #include diff --git a/src/catch2/catch_tostring.cpp b/src/catch2/catch_tostring.cpp index cc6865674b..dad6241071 100644 --- a/src/catch2/catch_tostring.cpp +++ b/src/catch2/catch_tostring.cpp @@ -57,6 +57,36 @@ namespace Detail { } } // end unnamed namespace + std::size_t catch_strnlen( const char* str, std::size_t n ) { + auto ret = std::char_traits::find( str, n, '\0' ); + if ( ret != nullptr ) { return static_cast( ret - str ); } + return n; + } + + std::string formatTimeT(std::time_t time) { +#ifdef _MSC_VER + std::tm timeInfo = {}; + const auto err = gmtime_s( &timeInfo, &time ); + if ( err ) { + return "gmtime from provided timepoint has failed. This " + "happens e.g. with pre-1970 dates using Microsoft libc"; + } +#else + std::tm* timeInfo = std::gmtime( &time ); +#endif + + auto const timeStampSize = sizeof( "2017-01-16T17:06:45Z" ); + char timeStamp[timeStampSize]; + const char* const fmt = "%Y-%m-%dT%H:%M:%SZ"; + +#ifdef _MSC_VER + std::strftime( timeStamp, timeStampSize, fmt, &timeInfo ); +#else + std::strftime( timeStamp, timeStampSize, fmt, timeInfo ); +#endif + return std::string( timeStamp, timeStampSize - 1 ); + } + std::string convertIntoString(StringRef string, bool escapeInvisibles) { std::string ret; // This is enough for the "don't escape invisibles" case, and a good diff --git a/src/catch2/catch_tostring.hpp b/src/catch2/catch_tostring.hpp index 41f89413c4..b005212f83 100644 --- a/src/catch2/catch_tostring.hpp +++ b/src/catch2/catch_tostring.hpp @@ -8,7 +8,7 @@ #ifndef CATCH_TOSTRING_HPP_INCLUDED #define CATCH_TOSTRING_HPP_INCLUDED - +#include #include #include #include @@ -40,13 +40,9 @@ namespace Catch { namespace Detail { - inline std::size_t catch_strnlen(const char *str, std::size_t n) { - auto ret = std::char_traits::find(str, n, '\0'); - if (ret != nullptr) { - return static_cast(ret - str); - } - return n; - } + std::size_t catch_strnlen(const char *str, std::size_t n); + + std::string formatTimeT( std::time_t time ); constexpr StringRef unprintableString = "{?}"_sr; @@ -411,44 +407,38 @@ namespace Catch { // Separate std::tuple specialization #if defined(CATCH_CONFIG_ENABLE_TUPLE_STRINGMAKER) -#include +# include +# include namespace Catch { namespace Detail { - template< - typename Tuple, - std::size_t N = 0, - bool = (N < std::tuple_size::value) - > - struct TupleElementPrinter { - static void print(const Tuple& tuple, std::ostream& os) { - os << (N ? ", " : " ") - << ::Catch::Detail::stringify(std::get(tuple)); - TupleElementPrinter::print(tuple, os); - } - }; - - template< - typename Tuple, - std::size_t N - > - struct TupleElementPrinter { - static void print(const Tuple&, std::ostream&) {} - }; - - } + template + void PrintTuple( const Tuple& tuple, + std::ostream& os, + std::index_sequence ) { + // 1 + Account for when the tuple is empty + char a[1 + sizeof...( Is )] = { + ( ( os << ( Is ? ", " : " " ) + << ::Catch::Detail::stringify( std::get( tuple ) ) ), + '\0' )... }; + (void)a; + } + } // namespace Detail - template + template struct StringMaker> { - static std::string convert(const std::tuple& tuple) { + static std::string convert( const std::tuple& tuple ) { ReusableStringStream rss; rss << '{'; - Detail::TupleElementPrinter>::print(tuple, rss.get()); + Detail::PrintTuple( + tuple, + rss.get(), + std::make_index_sequence{} ); rss << " }"; return rss.str(); } }; -} +} // namespace Catch #endif // CATCH_CONFIG_ENABLE_TUPLE_STRINGMAKER #if defined(CATCH_CONFIG_ENABLE_VARIANT_STRINGMAKER) && defined(CATCH_CONFIG_CPP17_VARIANT) @@ -635,28 +625,7 @@ struct ratio_string { const auto systemish = std::chrono::time_point_cast< std::chrono::system_clock::duration>( time_point ); const auto as_time_t = std::chrono::system_clock::to_time_t( systemish ); - -#ifdef _MSC_VER - std::tm timeInfo = {}; - const auto err = gmtime_s( &timeInfo, &as_time_t ); - if ( err ) { - return "gmtime from provided timepoint has failed. This " - "happens e.g. with pre-1970 dates using Microsoft libc"; - } -#else - std::tm* timeInfo = std::gmtime( &as_time_t ); -#endif - - auto const timeStampSize = sizeof("2017-01-16T17:06:45Z"); - char timeStamp[timeStampSize]; - const char * const fmt = "%Y-%m-%dT%H:%M:%SZ"; - -#ifdef _MSC_VER - std::strftime(timeStamp, timeStampSize, fmt, &timeInfo); -#else - std::strftime(timeStamp, timeStampSize, fmt, timeInfo); -#endif - return std::string(timeStamp, timeStampSize - 1); + return ::Catch::Detail::formatTimeT( as_time_t ); } }; } diff --git a/src/catch2/internal/catch_lifetimebound.hpp b/src/catch2/internal/catch_lifetimebound.hpp new file mode 100644 index 0000000000..8114dc70eb --- /dev/null +++ b/src/catch2/internal/catch_lifetimebound.hpp @@ -0,0 +1,24 @@ + +// Copyright Catch2 Authors +// Distributed under the Boost Software License, Version 1.0. +// (See accompanying file LICENSE.txt or copy at +// https://www.boost.org/LICENSE_1_0.txt) + +// SPDX-License-Identifier: BSL-1.0 + +#ifndef CATCH_LIFETIMEBOUND_HPP_INCLUDED +#define CATCH_LIFETIMEBOUND_HPP_INCLUDED + +#if !defined( __has_cpp_attribute ) +# define CATCH_ATTR_LIFETIMEBOUND +#elif __has_cpp_attribute( msvc::lifetimebound ) +# define CATCH_ATTR_LIFETIMEBOUND [[msvc::lifetimebound]] +#elif __has_cpp_attribute( clang::lifetimebound ) +# define CATCH_ATTR_LIFETIMEBOUND [[clang::lifetimebound]] +#elif __has_cpp_attribute( lifetimebound ) +# define CATCH_ATTR_LIFETIMEBOUND [[lifetimebound]] +#else +# define CATCH_ATTR_LIFETIMEBOUND +#endif + +#endif // CATCH_LIFETIMEBOUND_HPP_INCLUDED diff --git a/src/catch2/internal/catch_string_manip.hpp b/src/catch2/internal/catch_string_manip.hpp index dc0c552c4a..4251f83d85 100644 --- a/src/catch2/internal/catch_string_manip.hpp +++ b/src/catch2/internal/catch_string_manip.hpp @@ -8,6 +8,7 @@ #ifndef CATCH_STRING_MANIP_HPP_INCLUDED #define CATCH_STRING_MANIP_HPP_INCLUDED +#include #include #include @@ -28,10 +29,10 @@ namespace Catch { //! Returns a new string without whitespace at the start/end std::string trim( std::string const& str ); //! Returns a substring of the original ref without whitespace. Beware lifetimes! - StringRef trim(StringRef ref); + StringRef trim( StringRef ref CATCH_ATTR_LIFETIMEBOUND ); // !!! Be aware, returns refs into original string - make sure original string outlives them - std::vector splitStringRef( StringRef str, char delimiter ); + std::vector splitStringRef( StringRef str CATCH_ATTR_LIFETIMEBOUND, char delimiter ); bool replaceInPlace( std::string& str, std::string const& replaceThis, std::string const& withThis ); /** @@ -49,7 +50,7 @@ namespace Catch { StringRef m_label; public: - constexpr pluralise(std::uint64_t count, StringRef label): + constexpr pluralise(std::uint64_t count, StringRef label CATCH_ATTR_LIFETIMEBOUND): m_count(count), m_label(label) {} diff --git a/src/catch2/internal/catch_stringref.hpp b/src/catch2/internal/catch_stringref.hpp index 421ce71292..e70925bcd0 100644 --- a/src/catch2/internal/catch_stringref.hpp +++ b/src/catch2/internal/catch_stringref.hpp @@ -8,11 +8,12 @@ #ifndef CATCH_STRINGREF_HPP_INCLUDED #define CATCH_STRINGREF_HPP_INCLUDED +#include + #include #include #include #include - #include namespace Catch { @@ -36,14 +37,16 @@ namespace Catch { public: // construction constexpr StringRef() noexcept = default; - StringRef( char const* rawChars ) noexcept; + StringRef( char const* rawChars CATCH_ATTR_LIFETIMEBOUND ) noexcept; - constexpr StringRef( char const* rawChars, size_type size ) noexcept + constexpr StringRef( char const* rawChars CATCH_ATTR_LIFETIMEBOUND, + size_type size ) noexcept : m_start( rawChars ), m_size( size ) {} - StringRef( std::string const& stdString ) noexcept + StringRef( + std::string const& stdString CATCH_ATTR_LIFETIMEBOUND ) noexcept : m_start( stdString.c_str() ), m_size( stdString.size() ) {} @@ -89,7 +92,7 @@ namespace Catch { } // Returns the current start pointer. May not be null-terminated. - constexpr char const* data() const noexcept { + constexpr char const* data() const noexcept CATCH_ATTR_LIFETIMEBOUND { return m_start; } diff --git a/src/catch2/internal/catch_test_case_tracker.hpp b/src/catch2/internal/catch_test_case_tracker.hpp index 50278c910f..6bd749ffab 100644 --- a/src/catch2/internal/catch_test_case_tracker.hpp +++ b/src/catch2/internal/catch_test_case_tracker.hpp @@ -8,6 +8,7 @@ #ifndef CATCH_TEST_CASE_TRACKER_HPP_INCLUDED #define CATCH_TEST_CASE_TRACKER_HPP_INCLUDED +#include #include #include #include @@ -48,7 +49,7 @@ namespace TestCaseTracking { StringRef name; SourceLineInfo location; - constexpr NameAndLocationRef( StringRef name_, + constexpr NameAndLocationRef( StringRef name_ CATCH_ATTR_LIFETIMEBOUND, SourceLineInfo location_ ): name( name_ ), location( location_ ) {} diff --git a/src/catch2/matchers/catch_matchers.hpp b/src/catch2/matchers/catch_matchers.hpp index 3d996c39f2..90ed333854 100644 --- a/src/catch2/matchers/catch_matchers.hpp +++ b/src/catch2/matchers/catch_matchers.hpp @@ -10,6 +10,7 @@ #include #include +#include #include #include @@ -79,11 +80,15 @@ namespace Matchers { return description; } - friend MatchAllOf operator&& (MatchAllOf&& lhs, MatcherBase const& rhs) { + friend MatchAllOf operator&&( MatchAllOf&& lhs, + MatcherBase const& rhs + CATCH_ATTR_LIFETIMEBOUND ) { lhs.m_matchers.push_back(&rhs); return CATCH_MOVE(lhs); } - friend MatchAllOf operator&& (MatcherBase const& lhs, MatchAllOf&& rhs) { + friend MatchAllOf + operator&&( MatcherBase const& lhs CATCH_ATTR_LIFETIMEBOUND, + MatchAllOf&& rhs ) { rhs.m_matchers.insert(rhs.m_matchers.begin(), &lhs); return CATCH_MOVE(rhs); } @@ -131,11 +136,15 @@ namespace Matchers { return description; } - friend MatchAnyOf operator|| (MatchAnyOf&& lhs, MatcherBase const& rhs) { + friend MatchAnyOf operator||( MatchAnyOf&& lhs, + MatcherBase const& rhs + CATCH_ATTR_LIFETIMEBOUND ) { lhs.m_matchers.push_back(&rhs); return CATCH_MOVE(lhs); } - friend MatchAnyOf operator|| (MatcherBase const& lhs, MatchAnyOf&& rhs) { + friend MatchAnyOf + operator||( MatcherBase const& lhs CATCH_ATTR_LIFETIMEBOUND, + MatchAnyOf&& rhs ) { rhs.m_matchers.insert(rhs.m_matchers.begin(), &lhs); return CATCH_MOVE(rhs); } @@ -155,7 +164,8 @@ namespace Matchers { MatcherBase const& m_underlyingMatcher; public: - explicit MatchNotOf( MatcherBase const& underlyingMatcher ): + explicit MatchNotOf( MatcherBase const& underlyingMatcher + CATCH_ATTR_LIFETIMEBOUND ): m_underlyingMatcher( underlyingMatcher ) {} @@ -171,16 +181,22 @@ namespace Matchers { } // namespace Detail template - Detail::MatchAllOf operator&& (MatcherBase const& lhs, MatcherBase const& rhs) { + Detail::MatchAllOf + operator&&( MatcherBase const& lhs CATCH_ATTR_LIFETIMEBOUND, + MatcherBase const& rhs CATCH_ATTR_LIFETIMEBOUND ) { return Detail::MatchAllOf{} && lhs && rhs; } + template - Detail::MatchAnyOf operator|| (MatcherBase const& lhs, MatcherBase const& rhs) { + Detail::MatchAnyOf + operator||( MatcherBase const& lhs CATCH_ATTR_LIFETIMEBOUND, + MatcherBase const& rhs CATCH_ATTR_LIFETIMEBOUND ) { return Detail::MatchAnyOf{} || lhs || rhs; } template - Detail::MatchNotOf operator! (MatcherBase const& matcher) { + Detail::MatchNotOf + operator!( MatcherBase const& matcher CATCH_ATTR_LIFETIMEBOUND ) { return Detail::MatchNotOf{ matcher }; } diff --git a/src/catch2/matchers/catch_matchers_templated.hpp b/src/catch2/matchers/catch_matchers_templated.hpp index fc9fcb2bd5..0cd4016365 100644 --- a/src/catch2/matchers/catch_matchers_templated.hpp +++ b/src/catch2/matchers/catch_matchers_templated.hpp @@ -11,6 +11,7 @@ #include #include #include +#include #include #include @@ -114,7 +115,8 @@ namespace Matchers { MatchAllOfGeneric(MatchAllOfGeneric&&) = default; MatchAllOfGeneric& operator=(MatchAllOfGeneric&&) = default; - MatchAllOfGeneric(MatcherTs const&... matchers) : m_matchers{ {std::addressof(matchers)...} } {} + MatchAllOfGeneric(MatcherTs const&... matchers CATCH_ATTR_LIFETIMEBOUND) + : m_matchers{ {std::addressof(matchers)...} } {} explicit MatchAllOfGeneric(std::array matchers) : m_matchers{matchers} {} template @@ -136,8 +138,8 @@ namespace Matchers { template friend MatchAllOfGeneric operator && ( - MatchAllOfGeneric&& lhs, - MatchAllOfGeneric&& rhs) { + MatchAllOfGeneric&& lhs CATCH_ATTR_LIFETIMEBOUND, + MatchAllOfGeneric&& rhs CATCH_ATTR_LIFETIMEBOUND ) { return MatchAllOfGeneric{array_cat(CATCH_MOVE(lhs.m_matchers), CATCH_MOVE(rhs.m_matchers))}; } @@ -145,8 +147,8 @@ namespace Matchers { template friend std::enable_if_t, MatchAllOfGeneric> operator && ( - MatchAllOfGeneric&& lhs, - MatcherRHS const& rhs) { + MatchAllOfGeneric&& lhs CATCH_ATTR_LIFETIMEBOUND, + MatcherRHS const& rhs CATCH_ATTR_LIFETIMEBOUND ) { return MatchAllOfGeneric{array_cat(CATCH_MOVE(lhs.m_matchers), static_cast(&rhs))}; } @@ -154,8 +156,8 @@ namespace Matchers { template friend std::enable_if_t, MatchAllOfGeneric> operator && ( - MatcherLHS const& lhs, - MatchAllOfGeneric&& rhs) { + MatcherLHS const& lhs CATCH_ATTR_LIFETIMEBOUND, + MatchAllOfGeneric&& rhs CATCH_ATTR_LIFETIMEBOUND ) { return MatchAllOfGeneric{array_cat(static_cast(std::addressof(lhs)), CATCH_MOVE(rhs.m_matchers))}; } }; @@ -169,7 +171,8 @@ namespace Matchers { MatchAnyOfGeneric(MatchAnyOfGeneric&&) = default; MatchAnyOfGeneric& operator=(MatchAnyOfGeneric&&) = default; - MatchAnyOfGeneric(MatcherTs const&... matchers) : m_matchers{ {std::addressof(matchers)...} } {} + MatchAnyOfGeneric(MatcherTs const&... matchers CATCH_ATTR_LIFETIMEBOUND) + : m_matchers{ {std::addressof(matchers)...} } {} explicit MatchAnyOfGeneric(std::array matchers) : m_matchers{matchers} {} template @@ -190,8 +193,8 @@ namespace Matchers { //! Avoids type nesting for `GenericAnyOf || GenericAnyOf` case template friend MatchAnyOfGeneric operator || ( - MatchAnyOfGeneric&& lhs, - MatchAnyOfGeneric&& rhs) { + MatchAnyOfGeneric&& lhs CATCH_ATTR_LIFETIMEBOUND, + MatchAnyOfGeneric&& rhs CATCH_ATTR_LIFETIMEBOUND ) { return MatchAnyOfGeneric{array_cat(CATCH_MOVE(lhs.m_matchers), CATCH_MOVE(rhs.m_matchers))}; } @@ -199,8 +202,8 @@ namespace Matchers { template friend std::enable_if_t, MatchAnyOfGeneric> operator || ( - MatchAnyOfGeneric&& lhs, - MatcherRHS const& rhs) { + MatchAnyOfGeneric&& lhs CATCH_ATTR_LIFETIMEBOUND, + MatcherRHS const& rhs CATCH_ATTR_LIFETIMEBOUND ) { return MatchAnyOfGeneric{array_cat(CATCH_MOVE(lhs.m_matchers), static_cast(std::addressof(rhs)))}; } @@ -208,8 +211,8 @@ namespace Matchers { template friend std::enable_if_t, MatchAnyOfGeneric> operator || ( - MatcherLHS const& lhs, - MatchAnyOfGeneric&& rhs) { + MatcherLHS const& lhs CATCH_ATTR_LIFETIMEBOUND, + MatchAnyOfGeneric&& rhs CATCH_ATTR_LIFETIMEBOUND) { return MatchAnyOfGeneric{array_cat(static_cast(std::addressof(lhs)), CATCH_MOVE(rhs.m_matchers))}; } }; @@ -225,7 +228,8 @@ namespace Matchers { MatchNotOfGeneric(MatchNotOfGeneric&&) = default; MatchNotOfGeneric& operator=(MatchNotOfGeneric&&) = default; - explicit MatchNotOfGeneric(MatcherT const& matcher) : m_matcher{matcher} {} + explicit MatchNotOfGeneric(MatcherT const& matcher CATCH_ATTR_LIFETIMEBOUND) + : m_matcher{matcher} {} template bool match(Arg&& arg) const { @@ -237,7 +241,9 @@ namespace Matchers { } //! Negating negation can just unwrap and return underlying matcher - friend MatcherT const& operator ! (MatchNotOfGeneric const& matcher) { + friend MatcherT const& + operator!( MatchNotOfGeneric const& matcher + CATCH_ATTR_LIFETIMEBOUND ) { return matcher.m_matcher; } }; @@ -247,20 +253,22 @@ namespace Matchers { // compose only generic matchers template std::enable_if_t, Detail::MatchAllOfGeneric> - operator && (MatcherLHS const& lhs, MatcherRHS const& rhs) { + operator&&( MatcherLHS const& lhs CATCH_ATTR_LIFETIMEBOUND, + MatcherRHS const& rhs CATCH_ATTR_LIFETIMEBOUND ) { return { lhs, rhs }; } template std::enable_if_t, Detail::MatchAnyOfGeneric> - operator || (MatcherLHS const& lhs, MatcherRHS const& rhs) { + operator||( MatcherLHS const& lhs CATCH_ATTR_LIFETIMEBOUND, + MatcherRHS const& rhs CATCH_ATTR_LIFETIMEBOUND ) { return { lhs, rhs }; } //! Wrap provided generic matcher in generic negator template std::enable_if_t, Detail::MatchNotOfGeneric> - operator ! (MatcherT const& matcher) { + operator!( MatcherT const& matcher CATCH_ATTR_LIFETIMEBOUND ) { return Detail::MatchNotOfGeneric{matcher}; } @@ -268,25 +276,29 @@ namespace Matchers { // compose mixed generic and non-generic matchers template std::enable_if_t, Detail::MatchAllOfGeneric>> - operator && (MatcherLHS const& lhs, MatcherBase const& rhs) { + operator&&( MatcherLHS const& lhs CATCH_ATTR_LIFETIMEBOUND, + MatcherBase const& rhs CATCH_ATTR_LIFETIMEBOUND ) { return { lhs, rhs }; } template std::enable_if_t, Detail::MatchAllOfGeneric, MatcherRHS>> - operator && (MatcherBase const& lhs, MatcherRHS const& rhs) { + operator&&( MatcherBase const& lhs CATCH_ATTR_LIFETIMEBOUND, + MatcherRHS const& rhs CATCH_ATTR_LIFETIMEBOUND ) { return { lhs, rhs }; } template std::enable_if_t, Detail::MatchAnyOfGeneric>> - operator || (MatcherLHS const& lhs, MatcherBase const& rhs) { + operator||( MatcherLHS const& lhs CATCH_ATTR_LIFETIMEBOUND, + MatcherBase const& rhs CATCH_ATTR_LIFETIMEBOUND ) { return { lhs, rhs }; } template std::enable_if_t, Detail::MatchAnyOfGeneric, MatcherRHS>> - operator || (MatcherBase const& lhs, MatcherRHS const& rhs) { + operator||( MatcherBase const& lhs CATCH_ATTR_LIFETIMEBOUND, + MatcherRHS const& rhs CATCH_ATTR_LIFETIMEBOUND ) { return { lhs, rhs }; } diff --git a/src/catch2/meson.build b/src/catch2/meson.build index 60f7777a95..273e330fd1 100644 --- a/src/catch2/meson.build +++ b/src/catch2/meson.build @@ -105,6 +105,7 @@ internal_headers = [ 'internal/catch_jsonwriter.hpp', 'internal/catch_lazy_expr.hpp', 'internal/catch_leak_detector.hpp', + 'internal/catch_lifetimebound.hpp', 'internal/catch_list.hpp', 'internal/catch_logical_traits.hpp', 'internal/catch_message_info.hpp',