1. 4FIPS
  2. PHOTOS
  3. VIDEOS
  4. APPS
  5. CODE
  6. FORUMS
  7. ABOUT
/*
(c) 2004-14 Filip Stoklas, aka FipS, www.4FipS.com
THIS CODE IS FREE - LICENSED UNDER THE MIT LICENSE
*/

#ifndef FS_HEADER__fs_common_h__GUARD
#define FS_HEADER__fs_common_h__GUARD

#include "fs_config.h"

#include <string> // TODO: Get rid of this.
#include <exception> // TODO: Get rid of this.

/*
Usage:
- FS_ASSERT(cond && "Optional message...");
- FS_TRACE(("[x] File '%s' not found!\n", fname)); // [x], [!], [o], [.]
- FS_PROBE("x", 0.0);

Notes:
- {} scope is used to protect from the 'dangling else'.
- do-while is used to enforce a semicolon.
- The double-parentheses let the compiler always check for syntactic validity.
- (0,0) & volatile to suppress MSVC warning C4127.
- Custom handler setters and getters are not thread-safe (use carefully).

Links:
- http://stackoverflow.com/q/1644868
*/

#if defined(FS_PLATFORM_WIN)
#   define FS_DEBUG_BREAK do { __debugbreak(); } while(0,0)
#elif defined(FS_PLATFORM_OSX)
#   define FS_DEBUG_BREAK do { __asm__("int $3"); } while(0,0)
#else
#   define FS_DEBUG_BREAK do { } while(0,0)
#endif

#define FS_UNUSED(x) do { (void)sizeof(x); } while(0,0)

// --- ASSERT ---

#if defined(FS_ENABLE_ASSERT)
#   define FS_ASSERT(cond)                                                              \
    do {                                                                                \
        const volatile bool result = (cond) ? true : false;                             \
        if(!result && fs::assert::dispatch(#cond, __FUNCTION__, __FILE__, __LINE__))    \
        { FS_DEBUG_BREAK; }                                                             \
    } while(0,0)
#else
#   define FS_ASSERT(cond) do { FS_UNUSED(cond); } while(0,0)
#endif

#if defined(FS_ENABLE_ASSERT)
#   define FS_VERIFY(cond) FS_ASSERT(cond)
#else
#   define FS_VERIFY(cond) (void(cond))
#endif

namespace fs { namespace assert {

bool dispatch(const char *cond, const char *func, const char *file, int line) FS_NOEXCEPT;

using handler_type = bool (*)(const char *cond, const char *func, const char *file, int line);
handler_type set_handler(handler_type h) FS_NOEXCEPT;
handler_type handler() FS_NOEXCEPT;
bool default_handler(const char *cond, const char *func, const char *file, int line) FS_NOEXCEPT;

}} // namespace fs::asset

// --- TRACE ---

#if defined(FS_ENABLE_TRACE)
#   define FS_TRACE(params) fs::trace::dispatch params
#else
#   define FS_TRACE(params) do { if(0,0) fs::trace::dispatch params; } while(0,0)
#endif

namespace fs { namespace trace {

void dispatch(const char *fmt, ...) FS_NOEXCEPT;

using handler_type = void (*)(const char *text);
handler_type set_handler(handler_type h) FS_NOEXCEPT;
handler_type handler() FS_NOEXCEPT;
void default_handler(const char *text) FS_NOEXCEPT;

}} // namespace fs::trace

// --- PROBE ---

#if defined(FS_ENABLE_PROBE)
#   define FS_PROBE(key, val) fs::probe::dispatch(key, val)
#else
#   define FS_PROBE(key, val) do { if(0,0) fs::probe::dispatch(key, val); } while(0,0)
#endif

namespace fs { namespace probe {

void dispatch(const char *key, double val) FS_NOEXCEPT;

using handler_cb = void (*)(void *ctx, const char *key, double val);
struct handler_type { handler_cb cb; void *ctx; };
handler_type set_handler(handler_type h) FS_NOEXCEPT;
handler_type handler() FS_NOEXCEPT;
void default_handler(void *ctx, const char *key, double val) FS_NOEXCEPT;

}} // namespace fs::probe

// --- ERROR ---

#define FS_ERROR(err)                                               \
    do {                                                            \
        if(fs::error::debug_break_enabled()) { FS_DEBUG_BREAK; }    \
        fs::error::dispatch(err);                                   \
    } while(0,0)

namespace fs { namespace error {

class error : public std::runtime_error
{
 public:

    error(const std::string &what) FS_NOEXCEPT : std::runtime_error(what) {}
};

void dispatch(const error &err) FS_NOEXCEPT;

using handler_type = void(*)(const error &err);
handler_type set_handler(handler_type h) FS_NOEXCEPT;
handler_type handler() FS_NOEXCEPT;
void default_handler(const error &err) FS_NOEXCEPT;

bool enable_debug_break(bool on) FS_NOEXCEPT;
bool debug_break_enabled() FS_NOEXCEPT;

class not_implemented : public error
{
 public:

    not_implemented() FS_NOEXCEPT : error("Not implemented!") {}
};

}} // namespace fs::error

namespace fs {

// --- NARROW CAST ---

template <typename T, typename F> // TO/FROM (unsigned/unsigned)
static typename std::enable_if<std::is_unsigned<T>::value && std::is_unsigned<F>::value, T>::type narrow_cast(F value) FS_NOEXCEPT
{
    FS_ASSERT(value <= std::numeric_limits<T>::max());
    return static_cast<T>(value);
}

template <typename T, typename F> // TO/FROM (signed/signed)
static typename std::enable_if<std::is_signed<T>::value && std::is_signed<F>::value, T>::type narrow_cast(F value) FS_NOEXCEPT
{
    FS_ASSERT(value >= std::numeric_limits<T>::min() && value <= std::numeric_limits<T>::max());
    return static_cast<T>(value);
}

template <typename T, typename F> // TO/FROM (unsigned/signed)
static typename std::enable_if<std::is_unsigned<T>::value && std::is_signed<F>::value, T>::type narrow_cast(F value) FS_NOEXCEPT
{
    FS_ASSERT(value >= 0 && static_cast<typename std::make_unsigned<F>::type>(value) <= std::numeric_limits<T>::max());
    return static_cast<T>(value);
}

template <typename T, typename F> // TO/FROM (signed/unsigned)
static typename std::enable_if<std::is_signed<T>::value && std::is_unsigned<F>::value, T>::type narrow_cast(F value) FS_NOEXCEPT
{
    FS_ASSERT(value <= static_cast<typename std::make_unsigned<T>::type>(std::numeric_limits<T>::max()));
    return static_cast<T>(value);
}

// --- MISC ---

double sys_time() FS_NOEXCEPT;

} // namespace fs

#endif // FS_HEADER__fs_common_h__GUARD