First
This commit is contained in:
@@ -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
|
||||
Reference in New Issue
Block a user