This commit is contained in:
jeanlemotan
2024-07-02 18:06:33 +02:00
commit 8297b0b45f
157 changed files with 24865 additions and 0 deletions
+278
View File
@@ -0,0 +1,278 @@
#pragma once
#include "tl/detail/prologue.h"
#include <type_traits>
///////////////////////////////////////////////////////////////////////////////
namespace tl
{
namespace assert
{
//The assert handler is called whenever an assert fails and should present a message to the user.
//If it returns BREAK, the code will dbgbreak and allow you to connect with the debugger. Returning CONTINUE will continue the code normally
// condition contains a stringified condition that failed
// msg is any user supplied message. Can be NULL
// file is the name of the file the assert failed
// line is the line where the assert failed
enum class response
{
BREAK,
CONTINUE
};
using handler = response (*)(const char *, const char *, int, const char *);
TL_API handler set_handler(handler) noexcept;
TL_API response default_handler(const char* condition, const char* file, int line, const char* msg) noexcept;
inline response null_handler(const char*, const char*, int, const char*) noexcept
{
return response::CONTINUE;
}
//Use this to temporarily change the assert handler for the current scope
class handler_guard
{
public:
explicit handler_guard(handler new_handler) noexcept
{
m_prev_handler = set_handler(new_handler);
}
~handler_guard() noexcept
{
set_handler(m_prev_handler);
}
private:
handler m_prev_handler;
};
///////////////////////////////////////////////////////////////////////
//for internal use only
namespace detail
{
//helper for checking if a description is used instead of an assert condition
template<typename Cond> struct _description_in_condition { static const bool value = false; };
template<> struct _description_in_condition<char> { static const bool value = true; };
template<int N> struct _description_in_condition<char[N]> { static const bool value = true; };
template<typename T>
struct description_in_condition
{
static const bool value = _description_in_condition<std::remove_cv_t<std::remove_reference_t<T>>>::value;
};
//this is responsible of formatting the messages and calling the handler
TL_API response do_assert(const char* _condition, const char* _file, int _line, const char* msg) noexcept;
TL_API response do_assert(const char* _condition, const char* _file, int _line) noexcept;
//used to mark/test once asserts as being taken or not
TL_API bool is_assert_enabled(const char* _file, int _line) noexcept;
TL_API void set_assert_enabled(const char* _file, int _line, bool _enabled) noexcept;
inline const char* get_plain_message() noexcept { return ""; }
inline const char* get_plain_message(const char* msg) noexcept { return msg; }
TL_API handler& get_static_handler() noexcept;
}
}
}
#define __TL_ASSERT_TEST_DESCRIPTION_IN_CONDITION__(condition) \
const auto& __cond_result__ = (condition); \
(void)(__cond_result__); \
static_assert(tl::assert::detail::description_in_condition<decltype(__cond_result__)>::value == false, \
"It seems you used the assert description instead of the assert condition.")
//ASSERT
#define __TL_ASSERT_ENABLED_IMPL__(condition, ...) \
do \
{ \
__TL_ASSERT_TEST_DESCRIPTION_IN_CONDITION__(condition); \
if (!(__cond_result__)) [[unlikely]] \
{ \
eastl::string __msg__ = tl::format<eastl::string>(__VA_ARGS__); \
if (tl::assert::detail::do_assert(#condition, __FILE__, __LINE__, __msg__.data()) == tl::assert::response::BREAK) \
TL_BREAK(); \
} \
} while(false)
#define __TL_ASSERT_ENABLED_IMPL_ONCE__(condition, ...) \
do \
{ \
__TL_ASSERT_TEST_DESCRIPTION_IN_CONDITION__(condition); \
/* checking the condition first and then the GetAssertOnce, as chances are that the condition is cheaper */ \
if (!(__cond_result__) && tl::assert::detail::is_assert_enabled(__FILE__, __LINE__)) [[unlikely]] \
{ \
tl::assert::detail::set_assert_enabled(__FILE__, __LINE__, false); \
eastl::string __msg__ = tl::format<eastl::string>(__VA_ARGS__); \
if (tl::assert::detail::do_assert(#condition, __FILE__, __LINE__, __msg__.data()) == tl::assert::response::BREAK) \
TL_BREAK(); \
} \
} while(false)
//PLAIN ASSERT
#define __TL_PLAIN_ASSERT_ENABLED_IMPL__(condition, ...) \
do \
{ \
__TL_ASSERT_TEST_DESCRIPTION_IN_CONDITION__(condition); \
if (!(__cond_result__)) [[unlikely]] \
{ \
if (tl::assert::detail::do_assert(#condition, __FILE__, __LINE__, ##__VA_ARGS__) == tl::assert::response::BREAK) \
TL_BREAK(); \
} \
} while(false)
#define __TL_PLAIN_ASSERT_ENABLED_IMPL_ONCE__(condition, ...) \
do \
{ \
__TL_ASSERT_TEST_DESCRIPTION_IN_CONDITION__(condition); \
/* checking the condition first and then the GetAssertOnce, as chances are that the condition is cheaper */ \
if (!(__cond_result__) && tl::assert::detail::is_assert_enabled(__FILE__, __LINE__)) [[unlikely]] \
{ \
tl::assert::detail::set_assert_enabled(__FILE__, __LINE__, false); \
if (tl::assert::detail::do_assert(#condition, __FILE__, __LINE__, ##__VA_ARGS__) == tl::assert::response::BREAK) \
TL_BREAK(); \
} \
} while(false)
//FAIL
#define __TL_FAIL_ENABLED_IMPL__(...) \
do \
{ \
eastl::string __msg__ = tl::format<eastl::string>(__VA_ARGS__); \
if (tl::assert::detail::do_assert("fail", __FILE__, __LINE__, __msg__.data()) == tl::assert::response::BREAK) \
TL_BREAK(); \
} while(false)
#define __TL_FAIL_ENABLED_IMPL_ONCE__(...) \
do \
{ \
if (tl::assert::detail::is_assert_enabled(__FILE__, __LINE__)) [[likely]] \
{ \
tl::assert::detail::set_assert_enabled(__FILE__, __LINE__, false); \
eastl::string __msg__ = tl::format<eastl::string>(__VA_ARGS__); \
if (tl::assert::detail::do_assert("fail", __FILE__, __LINE__, __msg__.data()) == tl::assert::response::BREAK) \
TL_BREAK(); \
} \
} while(false)
//PLAIN FAIL
#define __TL_PLAIN_FAIL_ENABLED_IMPL__(...) \
do \
{ \
const char* __msg__ = tl::assert::detail::get_plain_message(__VA_ARGS__); \
if (tl::assert::detail::do_assert("fail", __FILE__, __LINE__, __msg__) == tl::assert::response::BREAK) \
TL_BREAK(); \
} while(false)
#define __TL_PLAIN_FAIL_ENABLED_IMPL_ONCE__(...) \
do \
{ \
if (tl::assert::detail::is_assert_enabled(__FILE__, __LINE__)) [[likely]] \
{ \
tl::assert::detail::set_assert_enabled(__FILE__, __LINE__, false); \
const char* __msg__ = tl::assert::detail::get_plain_message(__VA_ARGS__); \
if (tl::assert::detail::do_assert("fail", __FILE__, __LINE__, __msg__) == tl::assert::response::BREAK) \
TL_BREAK(); \
} \
} while(false)
#define __TL_ASSERT_DISABLED_IMPL__(condition, ...) do {} while (false)
#define __TL_ASSERT_DISABLED_IMPL_ONCE__(condition, ...) do {} while (false)
#define __TL_PLAIN_ASSERT_DISABLED_IMPL__(condition, ...) do {} while (false)
#define __TL_PLAIN_ASSERT_DISABLED_IMPL_ONCE__(condition, ...) do {} while (false)
#define __TL_FAIL_DISABLED_IMPL__(...) do {} while (false)
#define __TL_FAIL_DISABLED_IMPL_ONCE__(...) do {} while (false)
#define __TL_PLAIN_FAIL_DISABLED_IMPL__(...) do {} while (false)
#define __TL_PLAIN_FAIL_DISABLED_IMPL_ONCE__(...) do {} while (false)
#ifdef TL_DEBUG
//assert
#define TL_ASSERT __TL_ASSERT_ENABLED_IMPL__
#define TL_ASSERT_ONCE __TL_ASSERT_ENABLED_IMPL_ONCE__
#define TL_ASSERT_RELEASE __TL_ASSERT_ENABLED_IMPL__
#define TL_ASSERT_RELEASE_ONCE __TL_ASSERT_ENABLED_IMPL_ONCE__
//plain assert
#define TL_PLAIN_ASSERT __TL_PLAIN_ASSERT_ENABLED_IMPL__
#define TL_PLAIN_ASSERT_ONCE __TL_PLAIN_ASSERT_ENABLED_IMPL_ONCE__
#define TL_PLAIN_ASSERT_RELEASE __TL_PLAIN_ASSERT_ENABLED_IMPL__
#define TL_PLAIN_ASSERT_RELEASE_ONCE __TL_PLAIN_ASSERT_ENABLED_IMPL_ONCE__
//fail
#define TL_FAIL __TL_FAIL_ENABLED_IMPL__
#define TL_FAIL_ONCE __TL_FAIL_ENABLED_IMPL_ONCE__
#define TL_FAIL_RELEASE __TL_FAIL_ENABLED_IMPL__
#define TL_FAIL_RELEASE_ONCE __TL_FAIL_ENABLED_IMPL_ONCE__
//plain fail
#define TL_PLAIN_FAIL __TL_PLAIN_FAIL_ENABLED_IMPL__
#define TL_PLAIN_FAIL_ONCE __TL_PLAIN_FAIL_ENABLED_IMPL_ONCE__
#define TL_PLAIN_FAIL_RELEASE __TL_PLAIN_FAIL_ENABLED_IMPL__
#define TL_PLAIN_FAIL_RELEASE_ONCE __TL_PLAIN_FAIL_ENABLED_IMPL_ONCE__
#else
//assert
#define TL_ASSERT __TL_ASSERT_DISABLED_IMPL__
#define TL_ASSERT_ONCE __TL_ASSERT_DISABLED_IMPL_ONCE__
#ifdef TL_RETAIL
#define TL_ASSERT_RELEASE __TL_ASSERT_DISABLED_IMPL__
#define TL_ASSERT_RELEASE_ONCE __TL_ASSERT_DISABLED_IMPL_ONCE__
#define TL_PLAIN_ASSERT_RELEASE __TL_ASSERT_DISABLED_IMPL__
#define TL_PLAIN_ASSERT_RELEASE_ONCE __TL_ASSERT_DISABLED_IMPL_ONCE__
#define TL_FAIL_RELEASE __TL_ASSERT_DISABLED_IMPL__
#define TL_FAIL_RELEASE_ONCE __TL_ASSERT_DISABLED_IMPL_ONCE__
#define TL_PLAIN_FAIL_RELEASE __TL_ASSERT_DISABLED_IMPL__
#define TL_PLAIN_FAIL_RELEASE_ONCE __TL_ASSERT_DISABLED_IMPL_ONCE__
#else
#define TL_ASSERT_RELEASE __TL_ASSERT_ENABLED_IMPL__
#define TL_ASSERT_RELEASE_ONCE __TL_ASSERT_ENABLED_IMPL_ONCE__
#define TL_PLAIN_ASSERT_RELEASE __TL_PLAIN_ASSERT_ENABLED_IMPL__
#define TL_PLAIN_ASSERT_RELEASE_ONCE __TL_PLAIN_ASSERT_ENABLED_IMPL_ONCE__
#define TL_FAIL_RELEASE __TL_FAIL_ENABLED_IMPL__
#define TL_FAIL_RELEASE_ONCE __TL_FAIL_ENABLED_IMPL_ONCE__
#define TL_PLAIN_FAIL_RELEASE __TL_PLAIN_FAIL_ENABLED_IMPL__
#define TL_PLAIN_FAIL_RELEASE_ONCE __TL_PLAIN_FAIL_ENABLED_IMPL_ONCE__
#endif
//plain assert
#define TL_PLAIN_ASSERT __TL_PLAIN_ASSERT_DISABLED_IMPL__
#define TL_PLAIN_ASSERT_ONCE __TL_PLAIN_ASSERT_DISABLED_IMPL_ONCE__
//fail
#define TL_FAIL __TL_FAIL_DISABLED_IMPL__
#define TL_FAIL_ONCE __TL_FAIL_DISABLED_IMPL_ONCE__
//plain fail
#define TL_PLAIN_FAIL __TL_PLAIN_FAIL_DISABLED_IMPL__
#define TL_PLAIN_FAIL_ONCE __TL_PLAIN_FAIL_DISABLED_IMPL_ONCE__
#endif