// -*- c++ -*-

//-------------------------------------------------------------------------
// Author: Daniel Käps, created 2004. Extended 2005, 2006.
// This file is public domain.
//-------------------------------------------------------------------------

#ifndef _INCLUDED_safecast_h  
#define _INCLUDED_safecast_h  

//-------------------------------------------------------------------------
// typesafer casts
//
// Idea:
// - centralize cast operators in functions, avoid
//   using C casts (or reinterpret_cast<>) directly 
//   in the rest of the code
// - provide overloads if necessary for required parameter types
//   (the right overloaded function should be selected automatically
//   by the compiler if available, if not, the compiler should give
//   an error) - a change of a parameter by the calling code to a greater 
//   value range should be detected
// - note that since the C++ overload mechanism only considers parameters
//   (not the function's return value), a (polymorph) return value must 
//   be returned using a non-const parameter reference (or maybe pointer) 
//   (cf. e.g. cast_approx_assign(), cast_ptr_from_voidptr())
// - the purpose of using the cast is made explicit by the cast-function
//   name (e.g. cast_round_assign(), cast_approx_assign()), which
//   may improve readability of code using casts
//
// Notes:
// - use of overloading mechanism makes these functions different from
//   static_cast<>, const_cast<> (which also provide more safety than
//   C-style casts) in that the right cast is choosen by overload 
//   resolution, instead of having to specify desired output type 
//   explicitly
// - these functions are supposed to work on older compilers which have
//   no support for reinterpret_cast<>, static_cast<>
// - this is experimental
//
//-------------------------------------------------------------------------

// WARN maybe should return equality==false if signed value is lower than zero
inline bool cast_is_equal(unsigned int LeftUInt, int RightInt)
{
    return LeftUInt == (unsigned int) RightInt;
}

inline bool cast_is_equal(int LeftInt, unsigned int RightUInt)
{
    return ((unsigned int) LeftInt) == RightUInt;
}

inline bool cast_is_not_equal(unsigned int LeftUInt, int RightInt)
{
    return LeftUInt != (unsigned int) RightInt;
}

inline bool cast_is_not_equal(int LeftInt, unsigned int RightUInt)
{
    return ((unsigned int) LeftInt) != RightUInt;
}

inline bool cast_is_smaller(int LeftInt, unsigned int RightUInt)
{
    return ((unsigned int) LeftInt) < RightUInt;
}

inline bool cast_is_equal_smaller(int LeftInt, unsigned int RightUInt)
{
    return ((unsigned int) LeftInt) <= RightUInt;
}

inline bool cast_is_greater(unsigned int LeftUInt, int RightInt)
{
    return LeftUInt > (unsigned int) RightInt;
}

inline bool cast_is_equal_greater(unsigned int LeftUInt, int RightInt)
{
    return LeftUInt >= (unsigned int) RightInt;
}

//-------------------------------------------------------------------------

// casts with purpose of rounding to some integer value
// - actually, this is more truncation/clip (e.g. 4.6 is 
//   rounded down to 4)
inline void  cast_round_assign (int & Int, double Double)
{
    Int = (int) Double;
}

// cast with purpose to extract some character contained in an
// 'int'-type value (as returned by fgetc(), toupper() etc.) to 
// a variable of type 'char'
inline void  cast_char_assign (char & Char, int Int)
{
    // WARN: correct conversion for e.g. character contained in
    //   integer value?
    // TODO: check if conversion works with different settings
    //   for signedness of char-type, different char-conversion
    //   options of compilers
    // - Char = (char) Int;
    // - Char = (unsigned char) Int;
    //
    // TODO: handle errors (e.g. Int==-1 which indicates an error)
    //   condition
    //
    Char = (unsigned char) (((unsigned int) Int) & 0xff);
}

//-------------------------------------------------------------------------

// safe, loseless conversion from types to int:
inline int cast_extend_to_int(bool Bool)
{
    return (int) Bool;
}

inline int cast_extend_to_int(signed char Char)
{
    return (int) Char;
}

inline int cast_extend_to_int(unsigned char Char)
{
    return (int) Char;
}

//-------------------------------------------------------------------------

#ifdef M_SafeCast_IsUseStxInt64

// purpose: type conversions which may lead to inaccuracies
// caused by representation/resolution limitations
inline void  cast_approx_assign (double & Double, stxInt64 Int64)
{
    Double = (double) Int64;
}

#endif

//-------------------------------------------------------------------------

// cast_enum() is used to avoid overload ambiguity error related to 'enum'
// type with some compilers
inline int cast_enum(int Int)
{
    return Int;
}

//-------------------------------------------------------------------------

// declare and implement signedness casts for (char)
    #define M_Type char
    #define M_SafecastSignedness_IsAddConstOverloads 0
    #define M_SafecastSignedness_IsUseDefaultSignedness 1
    #include "safecast_signedness.h"
    #undef M_SafecastSignedness_IsAddConstOverloads
    #undef M_SafecastSignedness_IsUseDefaultSignedness
    #undef M_Type

// declare and implement signedness casts for (char *)
    #define M_Type char *
    #define M_SafecastSignedness_IsAddConstOverloads 1
    #define M_SafecastSignedness_IsUseDefaultSignedness 1
    #include "safecast_signedness.h"
    #undef M_SafecastSignedness_IsAddConstOverloads
    #undef M_SafecastSignedness_IsUseDefaultSignedness
    #undef M_Type

// declare and implement signedness casts for (int)
    #define M_Type int
    #define M_SafecastSignedness_IsAddConstOverloads 0
    #define M_SafecastSignedness_IsUseDefaultSignedness 0
    #include "safecast_signedness.h"
    #undef M_SafecastSignedness_IsAddConstOverloads
    #undef M_SafecastSignedness_IsUseDefaultSignedness
    #undef M_Type

// declare and implement signedness casts for (int *)
    #define M_Type int *
    #define M_SafecastSignedness_IsAddConstOverloads 1
    #define M_SafecastSignedness_IsUseDefaultSignedness 0
    #include "safecast_signedness.h"
    #undef M_SafecastSignedness_IsAddConstOverloads
    #undef M_SafecastSignedness_IsUseDefaultSignedness
    #undef M_Type

//=========================================================================

// (void)-pointer to pointer-to-type conversions
//
// - this group is similar to static_cast<>(), but uses 
//   overloading mechanism to automatically choose the right
//   conversion (unlike static_cast<>(), where this needs to
//   be specified as "template" argument additionally)
// - it is somewhat less safe than static_cast<> because the
//   the VoidPtr argument will convert from any pointer
// - unlike static_cast, however, it is restricted to the
//   builtin-types here (but additional overloads may be added
//   anywhere as required)

inline void cast_ptr_assign(bool * & BoolPtr, void * VoidPtr)
{
    BoolPtr = (bool *) VoidPtr;
}
inline void cast_ptr_assign(char * & CharPtr, void * VoidPtr)
{
    CharPtr = (char *) VoidPtr;
}
inline void cast_ptr_assign(signed char * & UCharPtr, void * VoidPtr)
{
    UCharPtr = (signed char *) VoidPtr;
}
inline void cast_ptr_assign(unsigned char * & UCharPtr, void * VoidPtr)
{
    UCharPtr = (unsigned char *) VoidPtr;
}
inline void cast_ptr_assign(short * & ShortPtr, void * VoidPtr)
{
    ShortPtr = (short *) VoidPtr;
}
inline void cast_ptr_assign(unsigned short * & UShortPtr, void * VoidPtr)
{
    UShortPtr = (unsigned short *) VoidPtr;
}
inline void cast_ptr_assign(int * & IntPtr, void * VoidPtr)
{
    IntPtr = (int *) VoidPtr;
}
inline void cast_ptr_assign(unsigned int * & UIntPtr, void * VoidPtr)
{
    UIntPtr = (unsigned int *) VoidPtr;
}
inline void cast_ptr_assign(long * & LongPtr, void * VoidPtr)
{
    LongPtr = (long *) VoidPtr;
}
inline void cast_ptr_assign(unsigned long * & ULongPtr, void * VoidPtr)
{
    ULongPtr = (unsigned long *) VoidPtr;
}
inline void cast_ptr_assign(float * & FloatPtr, void * VoidPtr)
{
    FloatPtr = (float *) VoidPtr;
}
inline void cast_ptr_assign(double * & DoublePtr, void * VoidPtr)
{
    DoublePtr = (double *) VoidPtr;
}

//-------------------------------------------------------------------------

inline void cast_ptr_assign(const bool * & BoolPtr, const void * VoidPtr)
{
    BoolPtr = (const bool *) VoidPtr;
}
inline void cast_ptr_assign(const char * & CharPtr, const void * VoidPtr)
{
    CharPtr = (const char *) VoidPtr;
}
inline void cast_ptr_assign(const signed char * & UCharPtr, const void * VoidPtr)
{
    UCharPtr = (const signed char *) VoidPtr;
}
inline void cast_ptr_assign(const unsigned char * & UCharPtr, const void * VoidPtr)
{
    UCharPtr = (const unsigned char *) VoidPtr;
}
inline void cast_ptr_assign(const short * & ShortPtr, const void * VoidPtr)
{
    ShortPtr = (const short *) VoidPtr;
}
inline void cast_ptr_assign(const unsigned short * & UShortPtr, const void * VoidPtr)
{
    UShortPtr = (const unsigned short *) VoidPtr;
}
inline void cast_ptr_assign(const int * & IntPtr, const void * VoidPtr)
{
    IntPtr = (const int *) VoidPtr;
}
inline void cast_ptr_assign(const unsigned int * & UIntPtr, const void * VoidPtr)
{
    UIntPtr = (const unsigned int *) VoidPtr;
}
inline void cast_ptr_assign(const long * & LongPtr, const void * VoidPtr)
{
    LongPtr = (const long *) VoidPtr;
}
inline void cast_ptr_assign(const unsigned long * & ULongPtr, const void * VoidPtr)
{
    ULongPtr = (const unsigned long *) VoidPtr;
}
inline void cast_ptr_assign(const float * & FloatPtr, const void * VoidPtr)
{
    FloatPtr = (const float *) VoidPtr;
}
inline void cast_ptr_assign(const double * & DoublePtr, const void * VoidPtr)
{
    DoublePtr = (const double *) VoidPtr;
}

//-------------------------------------------------------------------------

// memory related functions like to work on bytes, not on void-pointer
// - note: cast_byteptr() is generic for all pointer types
// - TODO check that sizeof(char) == 1 (8 Bit)

inline unsigned char * cast_byteptr(void * VoidPtr)
{
    return (unsigned char *) VoidPtr;
}

inline const unsigned char * cast_byteptr(const void * VoidPtr)
{
    return (const unsigned char *) VoidPtr;
}

//-------------------------------------------------------------------------

// one of the few functions which will accept only void-pointer
// (instead of any pointer type) is static_cast<>(), so
// there may be some use for cast_voidptr()

inline void * cast_voidptr(void * VoidPtr)
{
    return (void *) VoidPtr;
}

inline const void * cast_voidptr(const void * VoidPtr)
{
    return (const void *) VoidPtr;
}


//=========================================================================

// packing of void-pointer into integer representation conversions
//
// - reinterpret_cast is not as strict as we would want:
//     VoidPtrFromChar = reinterpret_cast<void *> (Char);
//   seems to be valid statement (scope: g++ 3.3.3, maybe others)
// - TODO add checks that (sizeof(void *) <= sizeof(long))
//   and provide overloads for (unsigned) int if 
//     sizeof(long) >= sizeof(int) >= sizeof(void *)

inline void cast_pack_ptr(long & Long, void * VoidPtr)
{
    Long = (long) VoidPtr;
}

inline void cast_pack_ptr(unsigned long & ULong, void * VoidPtr)
{
    ULong = (unsigned long) VoidPtr;
}

//inline long cast_pack_ptr(void * VoidPtr);
//inline unsigned long cast_pack_ptr(void * VoidPtr);

//-------------------------------------------------------------------------

inline void cast_unpack_ptr(void * & VoidPtr, long Long)
{
    VoidPtr = (void *) Long;
}

inline void cast_unpack_ptr(void * & VoidPtr, unsigned long ULong)
{
    VoidPtr = (void *) ULong;
}

inline void * cast_unpack_ptr(unsigned long ULong)
{
    return (void *) ULong;
}

//=========================================================================

// packing of long into void-pointer representation conversions
//
// - reinterpret_cast is not as strict as we would want:
//     (...)
// - TODO add checks that (sizeof(void *) >= sizeof(long))
//   and provide overloads for (unsigned) int if 
//     sizeof(void *) >= sizeof(long) >= sizeof(int)

inline void cast_pack_int(void * & VoidPtr, long Long)
{
    VoidPtr = (void *) Long;
}

inline void cast_pack_int(void * VoidPtr, unsigned long ULong)
{
    VoidPtr = (void *) ULong;
}

inline void * cast_pack_int(unsigned long ULong)
{
    return (void *) ULong;
}

inline void * cast_pack_int(long Long)
{
    return (void *) Long;
}

//-------------------------------------------------------------------------

inline void cast_unpack_ptr(long & Long, void * VoidPtr)
{
    Long = (long) VoidPtr;
}

inline void cast_unpack_ptr(unsigned long & ULong, void * VoidPtr)
{
    ULong = (long) VoidPtr;
}

//=========================================================================

#ifndef M_SafeCast_IsNoCxxTemplates

// cast_nonconst()
// - similar to const_cast<>, but doesn't require explicit
//   specification of desired output type (template function 
//   auto-instantiation does this job well)
template <class Type>
Type & cast_nonconst (const Type & TypeVar)
{
    return const_cast <Type &> (TypeVar);
    // return (Type &) TypeVar;
}

template <class Type>
Type & cast_nonconst (const Type * TypeVarPtr)
{
    return const_cast <Type *> (TypeVarPtr);
    // return (Type *) TypeVar;
}

// cast_const(): explicit cast from non-const (or const) to const
// - this may be useful in cases where the compiler insists in
//   using a non-const overload when we would like to having
//   the const overload choosen
// - note that the argument is const as well which means both,
//   const and non-const argument can be specified
template <class Type>
const Type & cast_const (const Type & TypeVar)
{
    return TypeVar;
}

template <class Type>
const Type * cast_const (const Type * TypeVarPtr)
{
    return TypeVarPtr;
}

#endif // M_SafeCast_IsNoCxxTemplates

//-------------------------------------------------------------------------

#endif
