Skip to content

Instantly share code, notes, and snippets.

@onqtam
Last active March 20, 2017 21:06
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save onqtam/2d07dac989eee2fd5ecc68ca37b14f03 to your computer and use it in GitHub Desktop.
Save onqtam/2d07dac989eee2fd5ecc68ca37b14f03 to your computer and use it in GitHub Desktop.
#pragma once
#ifdef __GNUC__
#pragma GCC system_header // to silence "ISO C++11 requires at least one argument for the "..." in a variadic macro"
#endif // __GNUC__
#include <utility> // std::forward
#include <memory> // placement new
#ifdef __clang__
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wpadded"
#endif // __clang__
namespace fast_static {
template<typename T>
struct storage {
typedef T type;
alignas(T) char buffer[sizeof(T)];
T* ptr = nullptr;
template<typename... Args>
void construct(Args&&... args) { ptr = new (buffer) type(std::forward<Args>(args)...); }
~storage() { if(ptr) ptr->~T(); }
};
}
#ifdef __clang__
#pragma clang diagnostic pop
#endif // __clang__
#ifdef __GNUC__
#define FAST_STATIC_UNLIKELY(x) __builtin_expect((x), 0)
#else // __GNUC__
#define FAST_STATIC_UNLIKELY(x) x
#endif // __GNUC__
#ifdef __clang__
#define FAST_STATIC_GLOBAL_NO_WARNINGS _Pragma("clang diagnostic push") \
_Pragma("clang diagnostic ignored \"-Wglobal-constructors\"") \
_Pragma("clang diagnostic ignored \"-Wexit-time-destructors\"")
#define FAST_STATIC_GLOBAL_NO_WARNINGS_END _Pragma("clang diagnostic pop")
#define FAST_STATIC_VA_ARGS_NO_WARNINGS _Pragma("clang diagnostic push") \
_Pragma("clang diagnostic ignored \"-Wgnu-zero-variadic-macro-arguments\"")
#define FAST_STATIC_VA_ARGS_NO_WARNINGS_END _Pragma("clang diagnostic pop")
#else // __clang__
#define FAST_STATIC_GLOBAL_NO_WARNINGS
#define FAST_STATIC_GLOBAL_NO_WARNINGS_END
#define FAST_STATIC_VA_ARGS_NO_WARNINGS
#define FAST_STATIC_VA_ARGS_NO_WARNINGS_END
#endif // __clang__
#define FAST_STATIC_CAT_IMPL(s1, s2) s1##s2
#define FAST_STATIC_CAT(s1, s2) FAST_STATIC_CAT_IMPL(s1, s2)
#define FAST_STATIC_STORAGE(name, ...) \
FAST_STATIC_GLOBAL_NO_WARNINGS \
namespace FAST_STATIC_CAT(fast_static, name) { \
static ::fast_static::storage<__VA_ARGS__> name; \
} \
FAST_STATIC_GLOBAL_NO_WARNINGS_END \
extern ::fast_static::storage<__VA_ARGS__> FAST_STATIC_CAT(fast_static, name)::name // to require a comma after the macro
#define FAST_STATIC_IMPL(name, ...) \
if(FAST_STATIC_UNLIKELY(!FAST_STATIC_CAT(fast_static, name)::name.ptr)) \
FAST_STATIC_CAT(fast_static, name)::name.construct(__VA_ARGS__); \
decltype(FAST_STATIC_CAT(fast_static, name)::name)::type& name = *FAST_STATIC_CAT(fast_static, name)::name.ptr
#define FAST_STATIC_EXPAND(x) x // workaround for MSVC
// indirection because this was the only way to silence a clang warning when there are no variable arguments for initialization
#define FAST_STATIC(...) \
FAST_STATIC_VA_ARGS_NO_WARNINGS \
FAST_STATIC_EXPAND(FAST_STATIC_IMPL(__VA_ARGS__)) \
FAST_STATIC_VA_ARGS_NO_WARNINGS_END
// ==================================================================
// C++11 changed the semantics of local static variables - their initialization is thread-safe.
// This may have a significant performance impact on code that uses statics in the hot path of the program.
// The synchronization can be disabled globally with -fno-threadsafe-statics for GCC if it is not required.
// The fast_static.h header can help if lazy initialization is required without disabling the thread safety globally.
// The only difference with real static objects in functions is that the destructor runs a bit
// later - with the globals and not with the other local statics and std::atexit() handlers.
// USAGE:
// FAST_STATIC_STORAGE(var, int); // outside of a function
// FAST_STATIC(var, 42); // inside a function - should be in the same namespace as the storage
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment