diff --git a/README.md b/README.md index ebed9d3..f3bdef4 100644 --- a/README.md +++ b/README.md @@ -6,7 +6,7 @@ library | latest version | category | languages | description ----------------|----------------|----------|-----------|------------- **gb_string.h** | 0.93 | strings | C, C++ | A better string library for C & C++ **gb_ini.h** | 0.91 | misc | C, C++ | A simple ini file loader library for C & C++ -**gb.hpp** | 0.20 | misc | C++11 | (Experimental) A C++11 helper library without STL geared towards game development +**gb.hpp** | 0.20a | misc | C++11 | (Experimental) A C++11 helper library without STL geared towards game development ## FAQ diff --git a/gb.hpp b/gb.hpp index 3d2b772..49a4d7b 100644 --- a/gb.hpp +++ b/gb.hpp @@ -1,8 +1,9 @@ -// gb.hpp - v0.20 - public domain C++11 helper library - no warranty implied; use at your own risk +// gb.hpp - v0.20a - public domain C++11 helper library - no warranty implied; use at your own risk // (Experimental) A C++11 helper library without STL geared towards game development /* Version History: + 0.20a - #ifndef for many macros 0.20 - Angle 0.19 - Cache friendly Transform and String fixes 0.18 - Hash_Table bug fixes @@ -65,6 +66,7 @@ Context: - Complex - Quaternion - Matrix(2,3,4) + - Angle - Euler_Angles - Transform - Aabb @@ -74,12 +76,6 @@ Context: - Functions & Constants - Type Functions - Random - - Generator_Type - - Geneartor Definition (Template/Concept) - - Mt19937_32 - - Mt19937_64 - - Random_Device - - Functions */ #ifndef GB_INCLUDE_GB_HPP @@ -91,17 +87,20 @@ Context: // NOTE(bill): Because static means three different things in C/C++ // Great Design(!) +#ifndef global #define global static #define internal static #define local_persist static +#endif #if defined(_MSC_VER) #define _ALLOW_KEYWORD_MACROS + + #ifndef alignof // Needed for MSVC 2013 'cause Microsoft "loves" standards + #define alignof(x) __alignof(x) + #endif #endif -#if !defined(alignof) // Needed for MSVC 2013 'cause Microsoft "loves" standards - #define alignof(x) __alignof(x) -#endif //////////////////////////////// /// /// @@ -109,16 +108,26 @@ Context: /// /// //////////////////////////////// #if defined(_WIN32) || defined(_WIN64) + #ifndef GB_SYSTEM_WINDOWS #define GB_SYSTEM_WINDOWS 1 + #endif #elif defined(__APPLE__) && defined(__MACH__) + #ifndef GB_SYSTEM_OSX #define GB_SYSTEM_OSX 1 + #endif #elif defined(__unix__) + #ifndef GB_SYSTEM_UNIX #define GB_SYSTEM_UNIX 1 + #endif #if defined(__linux__) + #ifndef GB_SYSTEM_LINUX #define GB_SYSTEM_LINUX 1 + #endif #elif defined(__FreeBSD__) || defined(__FreeBSD_kernel__) + #ifndef GB_SYSTEM_FREEBSD #define GB_SYSTEM_FREEBSD 1 + #endif #else #error This UNIX operating system is not supported by gb.hpp #endif @@ -133,21 +142,30 @@ Context: //////////////////////////////// #if defined(_WIN32) || defined(_WIN64) #if defined(_WIN64) + #ifndef GB_ARCH_64_BIT #define GB_ARCH_64_BIT 1 + #endif #else + #ifndef GB_ARCH_32_BIT #define GB_ARCH_32_BIT 1 + #endif #endif #endif // TODO(bill): Check if this KEPLER_ENVIRONMENT works on clang #if defined(__GNUC__) #if defined(__x86_64__) || defined(__ppc64__) + #ifndef GB_ARCH_64_BIT #define GB_ARCH_64_BIT 1 + #endif #else + #ifndef GB_ARCH_32_BIT #define GB_ARCH_32_BIT 1 + #endif #endif #endif +// TODO(bill): Get this to work // #if !defined(GB_LITTLE_EDIAN) && !defined(GB_BIG_EDIAN) // // Source: http://sourceforge.net/p/predef/wiki/Endianness/ @@ -225,45 +243,50 @@ Context: #include #endif -#if !defined(NDEBUG) - #define GB_ASSERT(x, ...) ((void)(::gb__assert_handler((x), #x, __FILE__, __LINE__, ##__VA_ARGS__))) -#else - #define GB_ASSERT(x, ...) ((void)sizeof(x)) -#endif - -#if !defined(GB_ARRAY_BOUND_CHECKING) +#ifndef GB_ARRAY_BOUND_CHECKING #define GB_ARRAY_BOUND_CHECKING 1 #endif +#ifndef GB_DISABLE_COPY #define GB_DISABLE_COPY(Type) \ Type(const Type&) = delete; \ Type& operator=(const Type&) = delete +#endif -/// Helper function used as a better alternative to assert which allows for -/// optional printf style error messages -extern "C" inline void -gb__assert_handler(bool condition, const char* condition_str, - const char* filename, size_t line, - const char* error_text = nullptr, ...) -{ - if (condition) - return; +#if !defined(GB_ASSERT) + #if !defined(NDEBUG) + #define GB_ASSERT(x, ...) ((void)(::gb__assert_handler((x), #x, __FILE__, __LINE__, ##__VA_ARGS__))) - fprintf(stderr, "ASSERT! %s(%lu): %s", filename, line, condition_str); - if (error_text) - { - fprintf(stderr, " - "); + /// Helper function used as a better alternative to assert which allows for + /// optional printf style error messages + extern "C" inline void + gb__assert_handler(bool condition, const char* condition_str, + const char* filename, size_t line, + const char* error_text = nullptr, ...) + { + if (condition) + return; - va_list args; - va_start(args, error_text); - vfprintf(stderr, error_text, args); - va_end(args); - } - fprintf(stderr, "\n"); - // TODO(bill): Get a better way to abort - *(int*)0 = 0; -} + fprintf(stderr, "ASSERT! %s(%lu): %s", filename, line, condition_str); + if (error_text) + { + fprintf(stderr, " - "); + + va_list args; + va_start(args, error_text); + vfprintf(stderr, error_text, args); + va_end(args); + } + fprintf(stderr, "\n"); + // TODO(bill): Get a better way to abort + *(int*)0 = 0; + } + + #else + #define GB_ASSERT(x, ...) ((void)sizeof(x)) + #endif +#endif //////////////////////////////// /// /// @@ -271,34 +294,32 @@ gb__assert_handler(bool condition, const char* condition_str, /// /// //////////////////////////////// #if defined(_MSC_VER) + extern "C" inline int + gb__vsnprintf_compatible(char* buffer, size_t size, const char* format, va_list args) + { + int result = -1; + if (size > 0) + result = _vsnprintf_s(buffer, size, _TRUNCATE, format, args); + if (result == -1) + return _vscprintf(format, args); -extern "C" inline int -gb__vsnprintf_compatible(char* buffer, size_t size, const char* format, va_list args) -{ - int result = -1; - if (size > 0) - result = _vsnprintf_s(buffer, size, _TRUNCATE, format, args); - if (result == -1) - return _vscprintf(format, args); + return result; + } - return result; -} - -extern "C" inline int -gb__snprintf_compatible(char* buffer, size_t size, const char* format, ...) -{ - va_list args; - va_start(args, format); - int result = gb__vsnprintf_compatible(buffer, size, format, args); - va_end(args); - return result; -} - -#if !defined(GB_DO_NOT_USE_MSVC_SPRINTF_FIX) - #define snprintf gb__snprintf_compatible - #define vsnprintf gb__vsnprintf_compatible -#endif // GB_DO_NOT_USE_MSVC_SPRINTF_FIX + extern "C" inline int + gb__snprintf_compatible(char* buffer, size_t size, const char* format, ...) + { + va_list args; + va_start(args, format); + int result = gb__vsnprintf_compatible(buffer, size, format, args); + va_end(args); + return result; + } + #if !defined(GB_DO_NOT_USE_MSVC_SPRINTF_FIX) + #define snprintf gb__snprintf_compatible + #define vsnprintf gb__vsnprintf_compatible + #endif // GB_DO_NOT_USE_MSVC_SPRINTF_FIX #endif #if !defined(__GB_NAMESPACE_PREFIX) && !defined(GB_NO_GB_NAMESPACE) @@ -327,96 +348,101 @@ __GB_NAMESPACE_START //////////////////////////////// +#ifndef GB_BASIC_TYPES +#define GB_BASIC_TYPES + #if defined(_MSC_VER) + using u8 = unsigned __int8; + using s8 = signed __int8; + using u16 = unsigned __int16; + using s16 = signed __int16; + using u32 = unsigned __int32; + using s32 = signed __int32; + using u64 = unsigned __int64; + using s64 = signed __int64; + #else + using u8 = unsigned char; + using s8 = signed char; + using u16 = unsigned short; + using s16 = signed short; + using u32 = unsigned int; + using s32 = signed int; + using u64 = unsigned long long; + using s64 = signed long long; + #endif + + static_assert( sizeof(u8) == 1, "u8 is not 8 bits"); + static_assert(sizeof(u16) == 2, "u16 is not 16 bits"); + static_assert(sizeof(u32) == 4, "u32 is not 32 bits"); + static_assert(sizeof(u64) == 8, "u64 is not 64 bits"); + + using f32 = float; + using f64 = double; + + #if defined(GB_B8_AS_BOOL) + using b8 = bool; + #else + using b8 = s8; + #endif + using b32 = s32; + + // NOTE(bill): (std::)size_t is not used not because it's a bad concept but on + // the platforms that I will be using: + // sizeof(size_t) == sizeof(usize) == sizeof(s64) + // NOTE(bill): This also allows for a signed version of size_t which is similar + // to ptrdiff_t + // NOTE(bill): If (u)intptr is a better fit, please use that. + // NOTE(bill): Also, I hate the `_t` suffix + #if defined(GB_ARCH_64_BIT) + using ssize = s64; + using usize = u64; + #elif defined(GB_ARCH_32_BIT) + using usize = s32; + using usize = u32; + #else + #error Unknown architecture bit size + #endif + + static_assert(sizeof(usize) == sizeof(size_t), + "`usize` is not the same size as `size_t`"); + static_assert(sizeof(ssize) == sizeof(usize), + "`ssize` is not the same size as `usize`"); + + using intptr = intptr_t; + using uintptr = uintptr_t; + + using ptrdiff = ptrdiff_t; -#if defined(_MSC_VER) - using u8 = unsigned __int8; - using s8 = signed __int8; - using u16 = unsigned __int16; - using s16 = signed __int16; - using u32 = unsigned __int32; - using s32 = signed __int32; - using u64 = unsigned __int64; - using s64 = signed __int64; -#else - using u8 = unsigned char; - using s8 = signed char; - using u16 = unsigned short; - using s16 = signed short; - using u32 = unsigned int; - using s32 = signed int; - using u64 = unsigned long long; - using s64 = signed long long; #endif -static_assert( sizeof(u8) == 1, "u8 is not 8 bits"); -static_assert(sizeof(u16) == 2, "u16 is not 16 bits"); -static_assert(sizeof(u32) == 4, "u32 is not 32 bits"); -static_assert(sizeof(u64) == 8, "u64 is not 64 bits"); +#if !defined(GB_U8_MIN) + #define GB_U8_MIN 0u + #define GB_U8_MAX 0xffu + #define GB_S8_MIN (-0x7f - 1) + #define GB_S8_MAX 0x7f -using f32 = float; -using f64 = double; + #define GB_U16_MIN 0u + #define GB_U16_MAX 0xffffu + #define GB_S16_MIN (-0x7fff - 1) + #define GB_S16_MAX 0x7fff -#if defined(GB_B8_AS_BOOL) - using b8 = bool; -#else - using b8 = s8; -#endif -using b32 = s32; + #define GB_U32_MIN 0u + #define GB_U32_MAX 0xffffffffu + #define GB_S32_MIN (-0x7fffffff - 1) + #define GB_S32_MAX 0x7fffffff -// NOTE(bill): (std::)size_t is not used not because it's a bad concept but on -// the platforms that I will be using: -// sizeof(size_t) == sizeof(usize) == sizeof(s64) -// NOTE(bill): This also allows for a signed version of size_t which is similar -// to ptrdiff_t -// NOTE(bill): If (u)intptr is a better fit, please use that. -// NOTE(bill): Also, I hate the `_t` suffix -#if defined(GB_ARCH_64_BIT) - using ssize = s64; - using usize = u64; -#elif defined(GB_ARCH_32_BIT) - using usize = s32; - using usize = u32; -#else - #error Unknown architecture bit size + #define GB_U64_MIN 0ull + #define GB_U64_MAX 0xffffffffffffffffull + #define GB_S64_MIN (-0x7fffffffffffffffll - 1) + #define GB_S64_MAX 0x7fffffffffffffffll #endif -static_assert(sizeof(usize) == sizeof(size_t), - "`usize` is not the same size as `size_t`"); -static_assert(sizeof(ssize) == sizeof(usize), - "`ssize` is not the same size as `usize`"); - -using intptr = intptr_t; -using uintptr = uintptr_t; - -using ptrdiff = ptrdiff_t; - -#define GB_U8_MIN 0u -#define GB_U8_MAX 0xffu -#define GB_S8_MIN (-0x7f - 1) -#define GB_S8_MAX 0x7f - -#define GB_U16_MIN 0u -#define GB_U16_MAX 0xffffu -#define GB_S16_MIN (-0x7fff - 1) -#define GB_S16_MAX 0x7fff - -#define GB_U32_MIN 0u -#define GB_U32_MAX 0xffffffffu -#define GB_S32_MIN (-0x7fffffff - 1) -#define GB_S32_MAX 0x7fffffff - -#define GB_U64_MIN 0ull -#define GB_U64_MAX 0xffffffffffffffffull -#define GB_S64_MIN (-0x7fffffffffffffffll - 1) -#define GB_S64_MAX 0x7fffffffffffffffll - -#if defined(GB_ARCH_64_BIT) +#if defined(GB_ARCH_64_BIT) && !defined(GB_USIZE_MIX) #define GB_USIZE_MIX GB_U64_MIN #define GB_USIZE_MAX GB_U64_MAX #define GB_SSIZE_MIX GB_S64_MIN #define GB_SSIZE_MAX GB_S64_MAX -#elif defined(GB_ARCH_32_BIT) +#elif defined(GB_ARCH_32_BIT) && !defined(GB_USIZE_MIX) #define GB_USIZE_MIX GB_U32_MIN #define GB_USIZE_MAX GB_U32_MAX @@ -424,7 +450,7 @@ using ptrdiff = ptrdiff_t; #define GB_SSIZE_MAX GB_S32_MAX #endif -#if defined(GB_BASIC_WITHOUT_NAMESPACE) +#if defined(GB_BASIC_WITHOUT_NAMESPACE) && !defined(U8_MIN) #define U8_MIN 0u #define U8_MAX 0xffu #define S8_MIN (-0x7f - 1) @@ -445,13 +471,13 @@ using ptrdiff = ptrdiff_t; #define S64_MIN (-0x7fffffffffffffffll - 1) #define S64_MAX 0x7fffffffffffffffll - #if defined(GB_ARCH_64_BIT) + #if defined(GB_ARCH_64_BIT) && !defined(GB_USIZE_MIX) #define USIZE_MIX U64_MIN #define USIZE_MAX U64_MAX #define SSIZE_MIX S64_MIN #define SSIZE_MAX S64_MAX - #elif defined(GB_ARCH_32_BIT) + #elif defined(GB_ARCH_32_BIT) && !defined(GB_USIZE_MIX) #define USIZE_MIX U32_MIN #define USIZE_MAX U32_MAX @@ -564,60 +590,68 @@ move(T&& t) { return static_cast&&>(t); } +__GB_NAMESPACE_END //////////////////////////////// /// /// /// Defer /// /// /// //////////////////////////////// -namespace impl -{ -template -struct Defer -{ - Func f; +#ifndef GB_DEFER +#define GB_DEFER + __GB_NAMESPACE_START + namespace impl + { + template + struct Defer + { + Func f; - Defer(Func&& f) : f{forward(f)} {} - ~Defer() { f(); }; -}; + Defer(Func&& f) : f{forward(f)} {} + ~Defer() { f(); }; + }; -template -inline Defer -defer_func(Func&& f) { return Defer(forward(f)); } -} // namespace impl -__GB_NAMESPACE_END + template + inline Defer + defer_func(Func&& f) { return Defer(forward(f)); } + } // namespace impl + __GB_NAMESPACE_END -// NOTE(bill): These macros are in the global namespace thus, defer can be treated without a __GB_NAMESPACE_PREFIX:: prefix -#define GB_DEFER_1(x, y) x##y -#define GB_DEFER_2(x, y) GB_DEFER_1(x, y) -#define GB_DEFER_3(x) GB_DEFER_2(GB_DEFER_2(GB_DEFER_2(x, __COUNTER__), _), __LINE__) -#define defer(code) auto GB_DEFER_3(_defer_) = __GB_NAMESPACE_PREFIX::impl::defer_func([&](){code;}) + // NOTE(bill): These macros are in the global namespace thus, defer can be treated without a __GB_NAMESPACE_PREFIX:: prefix + #define GB_DEFER_1(x, y) x##y + #define GB_DEFER_2(x, y) GB_DEFER_1(x, y) + #define GB_DEFER_3(x) GB_DEFER_2(GB_DEFER_2(GB_DEFER_2(x, __COUNTER__), _), __LINE__) + #define defer(code) auto GB_DEFER_3(_defer_) = __GB_NAMESPACE_PREFIX::impl::defer_func([&](){code;}) +#endif #if !defined(GB_CASTS_WITHOUT_NAMESPACE) __GB_NAMESPACE_START #endif // GB_CASTS_WITHOUT_NAMESPACE -// IMPORTANT NOTE(bill): Very similar to doing `*(T*)(&u)` but easier/clearer to write -// however, it can be dangerous if sizeof(T) > sizeof(U) e.g. unintialized memory, undefined behavior -// *(T*)(&u) ~~ pseudo_cast(u) -template -inline T -pseudo_cast(const U& u) -{ - return reinterpret_cast(u); -} +#ifndef GB_SPECIAL_CASTS +#define GB_SPECIAL_CASTS + // IMPORTANT NOTE(bill): Very similar to doing `*(T*)(&u)` but easier/clearer to write + // however, it can be dangerous if sizeof(T) > sizeof(U) e.g. unintialized memory, undefined behavior + // *(T*)(&u) ~~ pseudo_cast(u) + template + inline T + pseudo_cast(const U& u) + { + return reinterpret_cast(u); + } -// NOTE(bill): Very similar to doing `*(T*)(&u)` -template -inline Dest -bit_cast(const Source& source) -{ - static_assert(sizeof(Dest) <= sizeof(Source), - "bit_cast(const Source&) - sizeof(Dest) <= sizeof(Source)"); - Dest dest; - ::memcpy(&dest, &source, sizeof(Dest)); - return dest; -} + // NOTE(bill): Very similar to doing `*(T*)(&u)` + template + inline Dest + bit_cast(const Source& source) + { + static_assert(sizeof(Dest) <= sizeof(Source), + "bit_cast(const Source&) - sizeof(Dest) <= sizeof(Source)"); + Dest dest; + ::memcpy(&dest, &source, sizeof(Dest)); + return dest; + } +#endif // FORENOTE(bill): There used to be a magic_cast that was equivalent to // a C-style cast but I removed it as I could not get it work as intented