/******************************************************************************/ /* VARIOUS UTILITIES **********************************************************/ /******************************************************************************/ #ifndef _H_EBCL_UTILITIES #define _H_EBCL_UTILITIES #include namespace ebcl { // IsPowerOf2( v ) - Is some integer a power of 2 ? template< typename T > static constexpr bool IsPowerOf2( T value ); /*= TEMPLATED HORRORS ========================================================*/ // MetaOr< ... > - Logical OR for templates template< typename... > struct MetaOr : std::false_type { }; template< typename T > struct MetaOr< T > : T { }; template< typename T , typename... Rest > struct MetaOr< T , Rest... > : std::conditional_t< bool( T::value ) , T , MetaOr< Rest... > > { }; /*----------------------------------------------------------------------------*/ // MetaAnd< ... > - Logical AND for templates template< typename... > struct MetaAnd : std::true_type { }; template< typename T > struct MetaAnd< T > : T { }; template< typename T , typename... Rest > struct MetaAnd< T , Rest... > : std::conditional_t< bool( T::value ) , MetaAnd< Rest... > , T > { }; /*----------------------------------------------------------------------------*/ // MetaNot< ... > - Logical NOT template< typename T > struct MetaNot : std::integral_constant< bool , !bool( T::value ) > { }; /*----------------------------------------------------------------------------*/ // MetaGet< N , List... > - Get the Nth element in a list template< unsigned , typename... > struct MetaGet { }; template< typename T , typename... Rest > struct MetaGet< 0 , T , Rest... > { using type = T; }; template< unsigned N , typename First , typename... Rest > struct MetaGet< N , First , Rest... > : MetaGet< N - 1 , Rest... > { }; template< unsigned N , typename... List > using T_MetaGet = typename MetaGet< N , List... >::type; /*----------------------------------------------------------------------------*/ // MetaLength< List... > - Get the length of a list template< typename... > struct MetaLength : std::integral_constant< unsigned , 0 > { }; template< typename T > struct MetaLength< T > : std::integral_constant< unsigned , 1 > { }; template< typename First , typename... Rest > struct MetaLength< First , Rest... > : std::integral_constant< unsigned , MetaLength< Rest... >::value + 1 > { }; /*----------------------------------------------------------------------------*/ // MetaContains< Type , List... > - Checks whether Type is in List template< typename Type , typename... List > using MetaContains = MetaOr< std::is_same< Type , List >... >; /*----------------------------------------------------------------------------*/ // MetaIndexOf< Type , List... > - Gets the index of Type in List. If Type is // not in the list, the length of the list is returned. template< typename Type , typename... List > struct MetaIndexOf { }; template< typename Type > struct MetaIndexOf< Type > : std::integral_constant< unsigned , 0 > { }; template< typename Type , typename First , typename... Rest > struct MetaIndexOf< Type , First , Rest... > : std::conditional_t< std::is_same< Type , First >::value , std::integral_constant< unsigned , 0 > , std::integral_constant< unsigned , MetaIndexOf< Type , Rest... >::value + 1 > > { }; /*----------------------------------------------------------------------------*/ // CanCopyOrMove< Type > - Check whether a type can be copy- or move-constructed. template< typename T > using CanCopyOrMove = MetaOr< std::is_copy_constructible< T > , std::is_move_constructible< T > >; /*----------------------------------------------------------------------------*/ // CanCopyConsAll< List... > - Checks that all listed types are copy-constructible template< typename... List > using CanCopyConsAll = MetaOr< std::is_copy_constructible< List >... >; // CanMoveConsAll< List... > - Checks that all listed types are move-constructible template< typename... List > using CanMoveConsAll = MetaOr< std::is_move_constructible< List >... >; // CanCopyAssAll< List... > - Checks that all listed types are copy-constructible template< typename... List > using CanCopyAssAll = MetaOr< std::is_copy_assignable< List >... >; // CanMoveAssAll< List... > - Checks that all listed types are move-constructible template< typename... List > using CanMoveAssAll = MetaOr< std::is_move_assignable< List >... >; /*----------------------------------------------------------------------------*/ // EnableForChild< P , C > - Enable if P is a parent class of C, or if P and C // are the same. template< typename Parent , typename Child > using EnableForChild = std::enable_if< std::is_base_of< Parent , Child >::value , bool >; template< typename C , typename P > using T_EnableForChild = typename EnableForChild::type; // EnableForParent< C , P > - Enable if P is a parent class of C, and if P and // C are distinct. template< typename Child , typename Parent > using EnableForParent = std::enable_if< std::is_base_of< Parent , Child >::value && !std::is_same< Parent , Child >::value , bool >; template< typename C , typename P > using T_EnableForParent = typename EnableForParent::type; /*----------------------------------------------------------------------------*/ // Remove const and topmost reference template< typename T > using RemoveConstRef = std::remove_const< std::remove_reference_t< T > >; template< typename T > using T_RemoveConstRef = typename RemoveConstRef< T >::type; /*----------------------------------------------------------------------------*/ // T_EnableIfTypesMatch - Make sure that the A type actually matches the V type. template< typename V , typename A , typename RA = T_RemoveConstRef< A > > using EnableIfTypesMatch = std::enable_if< std::is_same< RA , V >::value , bool >; template< typename V , typename A > using T_EnableIfTypesMatch = typename EnableIfTypesMatch< V , A >::type; /*----------------------------------------------------------------------------*/ /* In-place construction tag */ template< typename Type > struct Construct { explicit constexpr Construct( ) = default; static constexpr Construct< Type > v = Construct< Type >( ); }; using InPlace = Construct< void >; /*----------------------------------------------------------------------------*/ template< typename T > struct T_CFunc { }; template< typename RT , typename... AT > struct T_CFunc< RT( AT... ) > { typedef RT (* type )( AT... ); }; template< typename T > using F_CFunc = typename T_CFunc< T >::type; /*= MACRO HORRORS ============================================================*/ // M_LSHIFT_OP( Type , Target ) - Declare a left-shift operator using the // specified types #define M_LSHIFT_OP( Type , Target ) \ Type & operator<< ( Type& obj , Target value ) // M_WITH_INT( Name ) - Declare a template with a type that needs to be some // sort of integer. #define M_WITH_INT( Name ) \ template< \ typename Name , \ typename = std::enable_if_t< std::is_integral< Name >::value > \ > // M_DECLARE_SWAP( Type ) - Declare the swap function for a given type #define M_DECLARE_SWAP( Type ) \ void swap( Type& lhs , Type& rhs ) noexcept // M_DEFINE_SWAP( Type ) - Define the swap function for a given type #define M_DEFINE_SWAP( Type ) \ void lw::swap( Type& lhs , Type& rhs ) noexcept /*= ENDIAN DETECTION =========================================================*/ /* * Unfortunately there's no standard way to do this so we depend on some * GCC-specific stuff. */ #ifndef __BYTE_ORDER__ # error "We need the __BYTE_ORDER__ macro to exist :(" #endif enum class E_Endian { LITTLE = __ORDER_LITTLE_ENDIAN__ , BIG = __ORDER_BIG_ENDIAN__ , NATIVE = __BYTE_ORDER__ }; static constexpr bool IsBigEndian( ); static constexpr bool IsLittleEndian( ); /*= ENDIAN CONVERSION ========================================================*/ template< typename T , E_Endian E = E_Endian::NATIVE , typename = std::enable_if_t< std::is_integral< T >::value || std::is_floating_point< T >::value > > struct T_LittleEndian { static T convert( T value ); }; template< typename T , E_Endian E = E_Endian::NATIVE , typename = std::enable_if_t< std::is_integral< T >::value || std::is_floating_point< T >::value > > struct T_BigEndian { static T convert( T value ); }; /*= COMPARATORS AND SORTING ==================================================*/ // F_Comparator< T > - T_Comparator function type template< typename T > using F_Comparator = std::function< int( T const& a , T const& b ) >; // T_Comparator< T > - Default comparison function implementation template< typename T > struct T_Comparator { static int compare( T const& a , T const& b ); }; // Helper macros to declare and define specialised hash functions. #define M_DECLARE_COMPARATOR( Type ) \ template< > \ struct T_Comparator< Type > \ { \ static int compare( Type const& , Type const& ); \ } #define M_DEFINE_COMPARATOR( Type ) \ int ::ebcl::T_Comparator< Type >::compare( Type const & a , Type const & b ) // Instantiate some commonly used comparators extern template struct T_Comparator< uint32_t >; // Sort - Sort an array template< typename T > void Sort( T* array , uint32_t items , F_Comparator< T > comparator = T_Comparator< T >::compare ); /*= HASHING ==================================================================*/ // HashData( data , size ) - General hash function (Jenkin's one-at-a-time) uint32_t HashData( uint8_t const* data , uint32_t size ); // Hash function template struct. Meant to be specialized. By default it uses // the function above. template< typename T , int S = sizeof( T ) > struct T_HashFunction { static uint32_t hash( T const& item ); }; // ComputeHash( T ) - The actual hash function. template< typename T > uint32_t ComputeHash( T const& item ); // Helper macros to declare and define specialised hash functions. #define M_DECLARE_HASH( Type ) \ template< > \ struct T_HashFunction< Type > \ { \ static uint32_t hash( Type const& ); \ } #define M_DEFINE_HASH( Type ) \ uint32_t T_HashFunction< Type >::hash( Type const & item ) /*= PRIVATE IMPLEMENTATION BASE ==============================================*/ /* This class should be used as a base class for any non-performance-critical * class that contains a lot of private stuff. The idea is to limit the amount * of stuff exposed through the API, which in turns makes binary compatibility * somewhat easier. */ class A_PrivateImplementation { private: using T_Destructor_ = std::function< void( void* ) >; void* p_; T_Destructor_ piDestructor_; protected: A_PrivateImplementation( ) = delete; A_PrivateImplementation( A_PrivateImplementation const& ) = delete; A_PrivateImplementation& operator=( A_PrivateImplementation const& ) = delete; A_PrivateImplementation( void* p , T_Destructor_ destructor ); template< typename T > explicit A_PrivateImplementation( T* p ); A_PrivateImplementation( A_PrivateImplementation&& ) noexcept; A_PrivateImplementation& operator=( A_PrivateImplementation&& ) noexcept; public: virtual ~A_PrivateImplementation( ); protected: template< typename T > T& p( ) const; private: void callPrivateDestructor( ) noexcept; }; } // namespace #endif // _H_EBCL_UTILITIES #include