//! Deals with heap allocating (`boxing') types and values. //! //! Nameapace(s): //! API functions/macros start with (or are) `box`. `unbox`, `BOX`, and `UNBOX`. //! Internal impl. functions/macros start with (or are) `_box`, `_BOX`, `_unbox`, and `_UNBOX`. //! //! Provides macros for C and C++: //! * BOX(T) - Allocate a properly aligned, uninitliased `T*` pointer on the heap. //! * BOX_EX(T) - Allocate a properly aligned, zero-initialised `T*` pointer on the heap. //! * BOX_WITH(T, val) - Allocate a properly aligned `T*` pointer on the heap with the value of `val`. //! * UNBOX(val) - Free the memory of boxed pointer `val`, then return the value itself. //! * UNBOX_EX(val) - Explicitly zero and then free the memory of boxed pointer `val`, then return the value itself. //! //! Provides templates for C++: //! * T* box() - Same as `BOX(T)` //! * T* box() - Same as `BOX_EX(T)` //! * T* box(T value) - Same as `BOX_WITH(T, value)` //! * T unbox(T* value) - Same as `UNBOX(value)` //! * T unbox(T* value) - Same as `UNBOX_EX(value)` //! //! # Notes //! `UNBOX_EX` and `unbox` use `explicit_bzero` to blank the memory before free. //! Whereas `BOX_EX` and `box` just use `bzero`. //! //! This API is **not** designed to work with C++ classes, constructors, or destructors. It is designed for C types and structs. Constructors and destructors are not called or supported. Only use with C-like (so-called ``POD'') types without con/destructors. No default initialisation is done (besides zero-initialisation when requested). //! To override this behaviour and use C++ new/delete instead of C malloc/free, you can define the _BOX_CPP preprocessor directive. However, C++ move semantics are not used here at all so using this API with non-POD types will cause lots of useless copy-constructors being called. Do not use this with non-trivially constructable types. Ideally, only ever use C ("POD") types with this. #ifndef _BOX_H #define _BOX_H #ifdef __cplusplus #ifdef _BOX_CPP #include #error "Using BOX with non-POD types causes memory leaks" #endif #include #include //#include template inline T* box() { void* ptr = #ifdef _BOX_CPP new T(); #else aligned_alloc(alignof(T), sizeof(T)); #endif if constexpr(Zero) bzero(ptr, sizeof(T)); return (T*)ptr; } #ifdef _BOX_CPP template inline T* box(T&& val) { return new T(std::move(val)); } #endif template inline T* box(T val) { #ifdef _BOX_CPP T* ptr = new T(std::move(val)); #else T* ptr = box(); *ptr = val; #endif return ptr; } template inline T unbox(T* boxed) { T val = *boxed; if constexpr(Zero) explicit_bzero((void*)boxed, sizeof(T)); #ifdef _BOX_CPP delete boxed; #else free((void*)boxed); #endif return val; } template inline static T _unbox_explicit(T* boxed) { return unbox(boxed); } #define BOX(T) box() #define BOX_EX(T) box() #define BOX_WITH(T, val) box(val) #define UNBOX_EX(val) _unbox_explicit(val) #define UNBOX(val) unbox(val) #else #include #include #define BOX(T) ({ void* _bx__ptr = aligned_alloc(_Alignof(T), sizeof(T)); \ (T*)_bx__ptr; }) #define BOX_EX(T) ({ void* _bx__ptr = aligned_alloc(_Alignof(T), sizeof(T)); \ bzero(_bx__ptr, sizeof(T)); \ (T*)_bx__ptr; }) #define BOX_WITH(T, v) ({ T* _bx__ptr = aligned_alloc(_Alignof(T), sizeof(T)); \ *_bx__ptr = (v); \ _bx__ptr; }) #define UNBOX_EX(_val) ({ __auto_type _bx__ptr = (_val); \ __auto_type _bx__val = *_bx__ptr; \ explicit_bzero((void*)_bx__ptr, sizeof(_bx__val)); \ free((void*)_bx__ptr); \ _bx__val; }) #define UNBOX(val) ({ __auto_type _bx__val = *val; \ free((void*)val); \ _bx__val; }) #endif #endif /* _BOX_H */