diff --git a/include/cjdb/contracts.hpp b/include/cjdb/contracts.hpp index b13c846..e0ca735 100644 --- a/include/cjdb/contracts.hpp +++ b/include/cjdb/contracts.hpp @@ -4,10 +4,48 @@ #ifndef CJDB_CONTRACTS_HPP #define CJDB_CONTRACTS_HPP -#include -#include +#include #include +#ifdef _MSC_VER + #define CJDB_PRETTY_FUNCTION __FUNCSIG__ + #define CJDB_FORCE_INLINE __forceinline +#else + #define CJDB_PRETTY_FUNCTION __PRETTY_FUNCTION__ + #define CJDB_FORCE_INLINE [[gnu::always_inline]] inline +#endif // _MSC_VER + +#ifndef CJDB_PRINT_ERROR + #ifdef CJDB_USE_STDIO + #include + + namespace cjdb::contracts_detail { + struct print_error_fn { + template // NOLINTNEXTLINE(modernize-avoid-c-arrays) + CJDB_FORCE_INLINE void operator()(char const(&message)[N]) const noexcept + { + std::fwrite(message, sizeof(char), N - 1, stderr); + } + }; + inline constexpr auto print_error = print_error_fn{}; + } // namespace cjdb::contracts_detail + #else + #include + + namespace cjdb::contracts_detail { + struct print_error_fn { + template // NOLINTNEXTLINE(modernize-avoid-c-arrays) + CJDB_FORCE_INLINE void operator()(char const(&message)[N]) const noexcept + try { + std::cerr.write(message, static_cast(N) - 1); + } catch(...) {} + }; + inline constexpr auto print_error = print_error_fn{}; + } // namespace cjdb::contracts_detail + #endif // CJDB_USE_STDIO + #define CJDB_PRINT_ERROR(MESSAGE) ::cjdb::contracts_detail::print_error(MESSAGE) +#endif // CJDB_PRINT_ERROR + // clang-tidy doesn't yet support this // // #ifndef __cpp_lib_is_constant_evaluated @@ -18,28 +56,32 @@ #define CJDB_ASSERT(...) CJDB_CONTRACT_IMPL("assertion", __VA_ARGS__) #define CJDB_ENSURES(...) CJDB_CONTRACT_IMPL("post-condition", __VA_ARGS__) -#ifdef _MSC_VER - #define CJDB_PRETTY_FUNCTION __FUNCSIG__ -#else - #define CJDB_PRETTY_FUNCTION __PRETTY_FUNCTION__ -#endif // _MSC_VER - namespace cjdb::contracts_detail { #ifdef NDEBUG - inline constexpr auto is_debug = false; + inline constexpr bool is_debug = false; #else - inline constexpr auto is_debug = true; + inline constexpr bool is_debug = true; #endif // NDEBUG struct contract_impl_fn { + template constexpr void operator()(bool const result, - std::string_view const message, - std::string_view const function) const noexcept + char const(&message)[N1], // NOLINT(modernize-avoid-c-arrays) + char const(&function)[N2]) const noexcept // NOLINT(modernize-avoid-c-arrays) { - if (not result) { + if (not result) [[unlikely]] { if (not std::is_constant_evaluated()) { - if constexpr (is_debug) { - std::fprintf(stderr, "%s in `%s`\n", message.data(), function.data()); + if constexpr (is_debug) { // NOLINT + constexpr auto& suffix = "`\n"; + constexpr auto message_size = N1 - 1; + constexpr auto function_size = N2 - 1; + // NOLINTNEXTLINE(modernize-avoid-c-arrays) + char full_message[message_size + function_size + sizeof suffix]{}; + auto p = full_message; + std::memcpy(p, message, message_size); + std::memcpy(p += message_size, function, function_size); + std::memcpy(p += function_size, suffix, sizeof suffix); + CJDB_PRINT_ERROR(full_message); } } #ifdef _MSC_VER @@ -69,10 +111,9 @@ namespace cjdb::contracts_detail { #define CJDB_CONTRACT_IMPL(CJDB_KIND, ...) \ ::cjdb::contracts_detail::contract_impl(::cjdb::contracts_detail::matches_bool(__VA_ARGS__), \ - __FILE__ ":" CJDB_TO_STRING(__LINE__) ": " CJDB_KIND " `" #__VA_ARGS__ "` failed", \ + __FILE__ ":" CJDB_TO_STRING(__LINE__) ": " CJDB_KIND " `" #__VA_ARGS__ "` failed in `", \ CJDB_PRETTY_FUNCTION) - #define CJDB_TO_STRING(CJDB_STRING) CJDB_TO_STRING_IMPL(CJDB_STRING) #define CJDB_TO_STRING_IMPL(CJDB_STRING) #CJDB_STRING