279 lines
11 KiB
C++
279 lines
11 KiB
C++
#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
|