#ifndef VARIANT_H #define VARIANT_H #include #include #include #include #include /** * @brief Fixed data type is based on the double data type. Highest value is 31 digits positive and lowest 31 digits negative. The * best precision is 1/31. */ struct fixed { double dValue; ///< The value /** * @brief Constructor * @tparam TType Type of the value to create a fixed type of * @param[in] tValue The value to assign */ template constexpr fixed(TType tValue) : dValue(static_cast(tValue)) {} /** * @brief Assignment operator * @tparam TType Type of the value to assign * @param[in] tValue The value to assign * @return Reference to this class */ template constexpr fixed& operator=(TType tValue) { dValue = tValue; return *this; } /** * @{ * @brief Contained value as double * @return The value */ constexpr operator double() const {return dValue;} constexpr operator const double&() const {return dValue;} constexpr operator double&() {return dValue;} /** * @} */ /** * @brief Unary operator to return a fixed type. * @return the fixed type as a copy from this class. */ fixed constexpr operator+() const {return *this;} /** * @brief Unary operator to return a negative fixed type. * @return the fixed type as a copy from this class. */ fixed constexpr operator-() const {return fixed(-dValue);} }; /** * @brief Add two fixed types * @param[in] val1 Value 1 * @param[in] val2 Value 2 * @return The result */ inline fixed operator+(fixed val1, fixed val2) { return val1.dValue + val2.dValue; } /** * @brief Subtract two fixed types * @param[in] val1 Value 1 * @param[in] val2 Value 2 * @return The result */ inline fixed operator-(fixed val1, fixed val2) { return val1.dValue - val2.dValue; } /** * @brief Multiply two fixed types * @param[in] val1 Value 1 * @param[in] val2 Value 2 * @return The result */ inline fixed operator*(fixed val1, fixed val2) { return val1.dValue * val2.dValue; } /** * @brief Divide two fixed types * @param[in] val1 Value 1 * @param[in] val2 Value 2 * @return The result */ inline fixed operator/(fixed val1, fixed val2) { return val1.dValue / val2.dValue; } /** * @{ * @brief Compare two types * @param[in] val1 Value 1 * @param[in] val2 Value 2 * @return The result */ inline bool operator<(fixed val1, fixed val2) { return val1.dValue < val2.dValue; } inline bool operator<(fixed val1, double val2) { return val1.dValue < val2; } inline bool operator<(double val1, fixed val2) { return val1 < val2.dValue; } inline bool operator<=(fixed val1, fixed val2) { return val1.dValue <= val2.dValue; } inline bool operator<=(fixed val1, double val2) { return val1.dValue <= val2; } inline bool operator<=(double val1, fixed val2) { return val1 <= val2.dValue; } inline bool operator>(fixed val1, fixed val2) { return val1.dValue > val2.dValue; } inline bool operator>(fixed val1, double val2) { return val1.dValue > val2; } inline bool operator>(double val1, fixed val2) { return val1 > val2.dValue; } inline bool operator>=(fixed val1, fixed val2) { return val1.dValue >= val2.dValue; } inline bool operator>=(fixed val1, double val2) { return val1.dValue >= val2; } inline bool operator>=(double val1, fixed val2) { return val1 >= val2.dValue; } inline bool operator==(fixed val1, fixed val2) { return val1.dValue == val2.dValue; } inline bool operator==(fixed val1, double val2) { return val1.dValue == val2; } inline bool operator==(double val1, fixed val2) { return val1 == val2.dValue; } inline bool operator!=(fixed val1, fixed val2) { return val1.dValue != val2.dValue; } inline bool operator!=(fixed val1, double val2) { return val1.dValue != val2; } inline bool operator!=(double val1, fixed val2) { return val1 != val2.dValue; } /** * @} */ namespace std { #ifndef DOXYGEN_IGNORE /** * @brief Specialization of floating point variable for fixed data type. */ template <> inline constexpr bool is_floating_point_v<::fixed> = true; /** * @brief Specialization of signed number variable for fixed data type. */ template <> inline constexpr bool is_signed_v<::fixed> = true; /** * @brief Specialization of arithmic data type variable for fixed data type. */ template <> inline constexpr bool is_arithmetic_v<::fixed> = true; #endif /** * @brief Specialization of numeric limits class for fixed data type. */ template <> struct numeric_limits<::fixed> : numeric_limits { /** * @brief Returns the smallest finite value representable by fixed. * @return The smallest finite type. */ [[nodiscard]] static constexpr ::fixed(min)() noexcept { return 1.0 / static_cast(max()); } /** * @brief Returns the minimum largest value representable by fixed. * @return The largest finite type. */ [[nodiscard]] static constexpr ::fixed(max)() noexcept { // 31 bits available return 1ll << 31ll; } /** * @brief Returns the lowest finite value representable by fixed. * @return The lowest finite type. */ [[nodiscard]] static constexpr ::fixed lowest() noexcept { return -(max)(); } }; } /** * @brief Variant class to store const values. * @details This class is used for the storage of all possible data types as well as solving the expressions in const assignments. * Only 32-bit, 64-bit integers, largest floating point and several string data type variations are used. The conversion rules for * data types follow the C++11 conversion rules: * - Assignment of data type: * - Signed integers are assigned to int32_t when smaller than 64-bit; otherwise are assigned to int64_t. * - Unsigned integers are assigned to uint32_t when smaller than 64-bit; otherwise are assigned to uint64_t. * - Integral signed data types are: char, short, long, long long, int8_t, int16_t, int32_t int64_t. * - Integral unsigned data types are: bool, unsigned short, unsigned long, unsigned long long, uint8_t, uint16_t, uint32_t * uint64_t, wchar_t, char16_t, char32_t. * - String objects and string pointers are assigned to their corresponding counterparts. * - Arithmic operations with two operands: * - If either operand is long double, the other operand is converted to long double. * - If both integral operands are signed or both are unsigned, the operand with lesser conversion rank is converted to the * - operand with the greater conversion rank. * - Otherwise, if the unsigned integral operand's conversion rank is greater or equal to the conversion rank of the signed * operand, the signed operand is converted to the unsigned operand's type. * - Otherwise, if the signed integral operand's type can represent all values of the unsigned operand, the unsigned operand * is converted to the signed operand's type. * - Otherwise, both integral operands are converted to the unsigned counterpart of the signed operand's type. * - Conversions with and between string based operands are not supported. * - Cast to data type: * - If the stored data type and target data type are both signed or both unsigned, the target data type boundaries are * checked with the value before assignment takes place. * - If the destination type is unsigned, the resulting value is the smallest unsigned value equal to the source value modulo * 2^n where n is the number of bits used to represent the destination type. * - If the destination type is signed, the value does not change if the source integer can be represented in the destination * type. */ class CConstVariant { /** * The variant type containing the value in one of the data types supplied. The data type order provides the ranking of the * types. */ using TVariant = std::variant; public: /** * @brief Type to index mapping. * @remarks 'char' is represented by 'int8_t'. 'char16_t' is represented by 'uint16_t'. 'char32_t' is represented by * 'int32_t'. 'wchar_t' is either represented by 'uint16_t' or by 'uint32_t'. ASCII and UTF-8 strings are represented by * 'std::string'. */ enum ETypeMapping { type_bool = 0, type_int8_t, type_uint8_t, type_int16_t, type_uint16_t, type_int32_t, type_uint32_t, type_int64_t, type_uint64_t, type_fixed, type_float, type_double, type_long_double, type_string, type_u16string, type_u32string, type_wstring, }; /** * @brief Default constructor. */ CConstVariant() = default; /** * @brief Copy constructor. * @param[in] rvar Reference to the const variant to copy from. */ CConstVariant(const CConstVariant& rvar); /** * @brief Move constructor is not available. * @param[in] rvar Reference to the const variant to move from. */ CConstVariant(CConstVariant&& rvar) noexcept; /** * @brief Assignment constructor. * @param[in] bValue Value to assign. */ CConstVariant(bool bValue); /** * @brief Assignment constructor. * @param[in] iValue Value to assign. */ CConstVariant(int8_t iValue); /** * @brief Assignment constructor. * @param[in] uiValue Value to assign. */ CConstVariant(uint8_t uiValue); /** * @brief Assignment constructor. * @param[in] iValue Value to assign. */ CConstVariant(int16_t iValue); /** * @brief Assignment constructor. * @param[in] uiValue Value to assign. */ CConstVariant(uint16_t uiValue); #ifdef _WIN32 /** * @brief Assignment constructor. * @param[in] iValue Value to assign. */ CConstVariant(long int iValue); /** * @brief Assignment constructor. * @param[in] uiValue Value to assign. */ CConstVariant(unsigned long int uiValue); #endif /** * @brief Assignment constructor. * @param[in] iValue Value to assign. */ CConstVariant(int32_t iValue); /** * @brief Assignment constructor. * @param[in] uiValue Value to assign. */ CConstVariant(uint32_t uiValue); /** * @brief Assignment constructor. * @param[in] iValue Value to assign. */ CConstVariant(int64_t iValue); #if defined(__GNUC__) && !defined(_WIN32) /** * @brief Assignment constructor. * @param[in] iValue Value to assign. */ CConstVariant(long long int iValue); /** * @brief Assignment constructor. * @param[in] uiValue Value to assign. */ CConstVariant(unsigned long long int uiValue); #endif /** * @brief Assignment constructor. * @param[in] uiValue Value to assign. */ CConstVariant(uint64_t uiValue); /** * @brief Assignment constructor. * @param[in] fixValue Value to assign. */ CConstVariant(fixed fixValue); /** * @brief Assignment constructor. * @param[in] fValue Value to assign. */ CConstVariant(float fValue); /** * @brief Assignment constructor. * @param[in] dValue Value to assign. */ CConstVariant(double dValue); /** * @brief Assignment constructor. * @param[in] ldValue Value to assign. */ CConstVariant(long double ldValue); /** * @brief Assignment constructor. * @param[in] rssValue Reference to the string value to assign. */ CConstVariant(const std::string& rssValue); /** * @brief Assignment constructor. * @param[in] rssValue Reference to the string value to assign. */ CConstVariant(const std::u16string& rssValue); /** * @brief Assignment constructor. * @param[in] rssValue Reference to the string value to assign. */ CConstVariant(const std::u32string& rssValue); /** * @brief Assignment constructor. * @param[in] rssValue Reference to the string value to assign. */ CConstVariant(const std::wstring& rssValue); /** * @brief Copy assignment operator * @param[in] rvar Reference to the const variant to copy from. * @return Reference to the const variant instance. */ CConstVariant& operator=(const CConstVariant& rvar); /** * @brief Move operator is not available. * @param[in] rvar Reference to the const variant to move from. * @return Reference to the const variant instance. */ CConstVariant& operator=(CConstVariant&& rvar) noexcept; /** * @brief Assignment operator. * @tparam TType Type of the value to assign. * @param[in] rtValue Reference to the value to assign. * @return Reference to the const variant instance. */ template CConstVariant& operator=(const TType& rtValue); /** * @brief Get the value of the variant casted to the provided target type. * @throw Throws exception when the value exceeds the boundaries of the target value or the types are incompatible. * @tparam TTargetType The target type. * @return The value casted to the target type. */ template TTargetType Get() const; /** * @brief Get the value as string. * @return Get the value as a string. */ std::string GetAsString() const; /** * @{ * @brief Meta information * @remarks The ranking function provides a number indicating the ranking of the type. * @return Returns 'true' when the type stored in the variant has the specific attribute; 'false' otherwise. */ bool IsArithmetic() const; bool IsIntegral() const; bool IsFloatingPoint() const; bool IsBoolean() const; bool IsSigned() const; bool IsUnsigned() const; size_t Ranking() const; template bool IsSame() const; /** * @} */ /** * @brief Convert the variant to the type supplied. * @throw Throws exception when the value exceeds the boundaries of the target value or the types are incompatible. * @param[in] nRank The rank of the type to convert to. */ void Convert(size_t nRank); /** * @brief Convert the variant to the type supplied. * @throw Throws exception when the value exceeds the boundaries of the target value or the types are incompatible. * @tparam TType The type to convert to. */ template void Convert(); /** * @{ * @brief Unary arithmetic operators. * @remarks The bit operator '~' and the logical operator '!' work with integral operands only. * @result The result of the operation. */ CConstVariant operator!() const; CConstVariant operator~() const; CConstVariant operator+() const; CConstVariant operator-() const; /** * @} */ /** * @{ * @brief Do unary operation. The first version allows the operation on any arithmetic type. The second version works for * integral operation only. * @tparam TFunction The function type that should do the operation. The function uses the prototype: * @code CConstVariant(auto tOperand); @endcode * @param[in] rvarOperand The operand. * @param[in] tOperation The function that is used for the operation. * @return The const variant with the result. */ template static CConstVariant UnaryOperation(const CConstVariant& rvarOperand, TFunction tOperation); template static CConstVariant UnaryOperationIntegral(const CConstVariant& rvarOperand, TFunction tOperation); /** * @} */ /** * @{ * @brief Do binary operation. The first version allows the operation on any arithmetic type. The second version works for * integral operation only. * @tparam TFunction The function type that should do the operation. The function uses the prototype: * @code CConstVariant(auto tOperand1, auto tOperand2); @endcode * @param[in] rvarOperand1 The first operand. * @param[in] rvarOperand2 The second operand. * @param[in] tOperation The function that is used for the operation. * @return The const variant with the result. */ template static CConstVariant BinaryOperation(const CConstVariant& rvarOperand1, const CConstVariant& rvarOperand2, TFunction tOperation); template static CConstVariant BinaryOperationIntegral(const CConstVariant& rvarOperand1, const CConstVariant& rvarOperand2, TFunction tOperation); /** * @} */ private: /** * @brief Get the value of the variant. * @details Get the value of the variant by providing the variant type as well as the target type. Do conversion if necessary. * Follow the casting rules as defined at the class description. * @throw Throws exception when the value exceeds the boundaries of the target value or the types are incompatible. * @tparam TTargetType The target type. * @tparam TVariantType The variant type * @return The value casted to the target type. */ template TTargetType InternalGet() const; TVariant m_varValue; ///< The variant that stores the value. }; /** * @brief Equalize variants. * @details Convert the variant of lesser ranking to a higher ranking. This function is used for binary arithmetic operations. The * function implements the rules as described at the CConstVariant class description. * @param[in] rvar1 The first variant. * @param[in] rvar2 The second variant. */ void Equalize(CConstVariant& rvar1, CConstVariant& rvar2); /** * @{ * @brief Arithmetic operators. * @remarks The bit operators (~ & | ^ << >>) as well as the remainder (%) operator work with integral operands only. * @param[in] rvarOperand1 The first operand of a binary operation. * @param[in] rvarOperand2 The second operand of a binary operation. * @result The result of the operation. */ CConstVariant operator*(const CConstVariant& rvarOperand1, const CConstVariant& rvarOperand2); CConstVariant operator/(const CConstVariant& rvarOperand1, const CConstVariant& rvarOperand2); CConstVariant operator+(const CConstVariant& rvarOperand1, const CConstVariant& rvarOperand2); CConstVariant operator-(const CConstVariant& rvarOperand1, const CConstVariant& rvarOperand2); CConstVariant operator<(const CConstVariant& rvarOperand1, const CConstVariant& rvarOperand2); CConstVariant operator<=(const CConstVariant& rvarOperand1, const CConstVariant& rvarOperand2); CConstVariant operator>(const CConstVariant& rvarOperand1, const CConstVariant& rvarOperand2); CConstVariant operator>=(const CConstVariant& rvarOperand1, const CConstVariant& rvarOperand2); CConstVariant operator+(const CConstVariant& rvarOperand1, const CConstVariant& rvarOperand2); CConstVariant operator==(const CConstVariant& rvarOperand1, const CConstVariant& rvarOperand2); CConstVariant operator!=(const CConstVariant& rvarOperand1, const CConstVariant& rvarOperand2); CConstVariant operator%(const CConstVariant& rvarOperand1, const CConstVariant& rvarOperand2); CConstVariant operator<<(const CConstVariant& rvarOperand1, const CConstVariant& rvarOperand2); CConstVariant operator>>(const CConstVariant& rvarOperand1, const CConstVariant& rvarOperand2); CConstVariant operator&(const CConstVariant& rvarOperand1, const CConstVariant& rvarOperand2); CConstVariant operator^(const CConstVariant& rvarOperand1, const CConstVariant& rvarOperand2); CConstVariant operator|(const CConstVariant& rvarOperand1, const CConstVariant& rvarOperand2); CConstVariant operator&&(const CConstVariant& rvarOperand1, const CConstVariant& rvarOperand2); CConstVariant operator||(const CConstVariant& rvarOperand1, const CConstVariant& rvarOperand2); /** * @} */ #endif // !defined(VARIANT_H)