#pragma region CPL License /* Nuclex Native Framework Copyright (C) 2002-2023 Nuclex Development Labs This library is free software; you can redistribute it and/or modify it under the terms of the IBM Common Public License as published by the IBM Corporation; either version 1.0 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the IBM Common Public License for more details. You should have received a copy of the IBM Common Public License along with this library */ #pragma endregion // CPL License #ifndef NUCLEX_SUPPORT_VARIANT_H #define NUCLEX_SUPPORT_VARIANT_H #include "Nuclex/Support/Config.h" #include "Nuclex/Support/VariantType.h" #include // for std::int*_t and std::uint*_t #include // for std::string #include // for std::any namespace Nuclex { namespace Support { // ------------------------------------------------------------------------------------------- // /// Wraps a value of an arbitrary type /// /// Whatever you do, only use this to interface with scripting languages or where /// it really makes sense. C++ is not a dynamically typed language and a template-based /// design is always a better choice. If you need to design interfaces that pass /// platform-dependent types to internal implementations, consider /// the class instead. /// class NUCLEX_SUPPORT_TYPE Variant { /// Initializes a new, empty variant public: NUCLEX_SUPPORT_API Variant() : type(VariantType::Empty) {} /// Initializes a variant to a boolean value /// Boolean value the variant will hold public: NUCLEX_SUPPORT_API Variant(bool booleanValue) : type(VariantType::Boolean), booleanValue(booleanValue) {} /// Initializes a variant to an unsigned 8 bit integer value /// Unsigned 8 bit integer value the variant will hold public: NUCLEX_SUPPORT_API Variant(std::uint8_t uint8Value) : type(VariantType::Uint8), uint8Value(uint8Value) {} /// Initializes a variant to a signed 8 bit integer value /// Signed 8 bit integer value the variant will hold public: NUCLEX_SUPPORT_API Variant(std::int8_t int8Value) : type(VariantType::Int8), int8Value(int8Value) {} /// Initializes a variant to an unsigned 16 bit integer value /// Unsigned 16 bit integer value the variant will hold public: NUCLEX_SUPPORT_API Variant(std::uint16_t uint16Value) : type(VariantType::Uint16), uint16Value(uint16Value) {} /// Initializes a variant to a signed 16 bit integer value /// Signed 16 bit integer value the variant will hold public: NUCLEX_SUPPORT_API Variant(std::int16_t int16Value) : type(VariantType::Int16), int16Value(int16Value) {} /// Initializes a variant to an unsigned 32 bit integer value /// Unsigned 32 bit integer value the variant will hold public: NUCLEX_SUPPORT_API Variant(std::uint32_t uint32Value) : type(VariantType::Uint32), uint32Value(uint32Value) {} /// Initializes a variant to a signed 32 bit integer value /// Signed 32 bit integer value the variant will hold public: NUCLEX_SUPPORT_API Variant(std::int32_t int32Value) : type(VariantType::Int32), int32Value(int32Value) {} /// Initializes a variant to an unsigned 64 bit integer value /// Unsigned 64 bit integer value the variant will hold public: NUCLEX_SUPPORT_API Variant(std::uint64_t uint64Value) : type(VariantType::Uint64), uint64Value(uint64Value) {} /// Initializes a variant to a signed 64 bit integer value /// Signed 64 bit integer value the variant will hold public: NUCLEX_SUPPORT_API Variant(std::int64_t int64Value) : type(VariantType::Int64), int64Value(int64Value) {} /// Initializes a variant to a floating point value /// Floating point value the variant will hold public: NUCLEX_SUPPORT_API Variant(float floatValue) : type(VariantType::Float), floatValue(floatValue) {} /// Initializes a variant to a double precision floating point value /// /// Double precision floating point value the variant will hold /// public: NUCLEX_SUPPORT_API Variant(double doubleValue) : type(VariantType::Double), doubleValue(doubleValue) {} /// Initializes a variant to hold a string /// String that the variant will hold public: NUCLEX_SUPPORT_API Variant(const std::string &stringValue) : type(VariantType::String) { new(this->stringValueBytes) std::string(stringValue); } /// Initializes a variant to hold a wide string /// Wide string that the variant will hold public: NUCLEX_SUPPORT_API Variant(const std::wstring &wstringValue) : type(VariantType::WString) { new(this->wstringValueBytes) std::wstring(wstringValue); } // Disabled due to compiler issue. #if defined(NUCLEX_SUPPORT_VARIANT_WITH_STDANY_CONSTRUCTOR) /// Initializes a variant to hold an opaquely typed value /// Opaquely typed value the variant will hold public: NUCLEX_SUPPORT_API Variant(const std::any &anyValue) : type(VariantType::Any) { new(this->anyValueBytes) std::any(anyValue); } #endif /// Initializes a variant to hold a void pointer /// Pointer that the variant will hold public: NUCLEX_SUPPORT_API Variant(void *pointerValue) : type(VariantType::VoidPointer), pointerValue(pointerValue) {} /// Initializes the variant as a copy of another variant /// Other variant that will be copied public: NUCLEX_SUPPORT_API Variant(const Variant &other); /// Initializes the variant by taking over the contents of another /// Other variant that will be taken over public: NUCLEX_SUPPORT_API Variant(Variant &&other); /// Destroys the variant and frees any memory used public: NUCLEX_SUPPORT_API ~Variant() { free(); } /// Checks whether the variant is currently not holding a value /// True if the variant is empty public: NUCLEX_SUPPORT_API bool IsEmpty() const { return (this->type == VariantType::Empty); } /// Returns the value held by the variant as a boolean /// The variant's value as a boolean /// /// Integer or floating point values will be true if they are any value other than /// zero, strings will be lexically casted and objects will be true if they are /// not null pointers. /// public: NUCLEX_SUPPORT_API bool ToBoolean() const; /// Returns the value held by the variant as an unsigned 8 bit integer /// The variant's value as an unsigned 8 bit integer public: NUCLEX_SUPPORT_API std::uint8_t ToUint8() const; /// Returns the value held by the variant as a signed 8 bit integer /// The variasnt's value as a signed 8 bit integer public: NUCLEX_SUPPORT_API std::int8_t ToInt8() const; /// Returns the value held by the variant as an unsigned 16 bit integer /// The variant's value as an unsigned 16 bit integer public: NUCLEX_SUPPORT_API std::uint16_t ToUint16() const; /// Returns the value held by the variant as a signed 16 bit integer /// The variasnt's value as a signed 16 bit integer public: NUCLEX_SUPPORT_API std::int16_t ToInt16() const; /// Returns the value held by the variant as an unsigned 32 bit integer /// The variant's value as an unsigned 32 bit integer public: NUCLEX_SUPPORT_API std::uint32_t ToUint32() const; /// Returns the value held by the variant as a signed 32 bit integer /// The variasnt's value as a signed 32 bit integer public: NUCLEX_SUPPORT_API std::int32_t ToInt32() const; /// Returns the value held by the variant as an unsigned 64 bit integer /// The variant's value as an unsigned 64 bit integer public: NUCLEX_SUPPORT_API std::uint64_t ToUint64() const; /// Returns the value held by the variant as a signed 64 bit integer /// The variasnt's value as a signed 64 bit integer public: NUCLEX_SUPPORT_API std::int64_t ToInt64() const; /// Returns the value held by the variant as a floating point value /// The variasnt's value as a floating point value public: NUCLEX_SUPPORT_API float ToFloat() const; /// /// Returns the value held by the variant as a double precision floating point value /// /// The variasnt's value as a double precision floating point value public: NUCLEX_SUPPORT_API double ToDouble() const; /// Returns the value held by the variant as a string /// The variasnt's value as a string public: NUCLEX_SUPPORT_API std::string ToString() const; /// Returns the value held by the variant as a wide string /// The variasnt's value as a wide string public: NUCLEX_SUPPORT_API std::wstring ToWString() const; /// Returns the value held by the variant as an opaquely typed value /// The variasnt's value as an opaquely typed value public: NUCLEX_SUPPORT_API std::any ToAny() const; /// Returns the value held by the variant as a void pointer /// The variasnt's value as a void pointer public: NUCLEX_SUPPORT_API void *ToVoidPointer() const; /// Checks whether the variant is holding a numeric value /// True if the variant is holding a numeric value public: NUCLEX_SUPPORT_API bool IsNumber() const { switch(this->type) { case VariantType::Uint8: case VariantType::Int8: case VariantType::Uint16: case VariantType::Int16: case VariantType::Uint32: case VariantType::Int32: case VariantType::Uint64: case VariantType::Int64: case VariantType::Float: case VariantType::Double: { return true; } default: { return false; } } } /// Checks whether the variant is holding a string /// True if the variant is holding a string public: NUCLEX_SUPPORT_API bool IsString() const { switch(this->type) { case VariantType::String: case VariantType::WString: { return true; } default: { return false; } } } /// Retrieves the type that is currently stored by the variant /// The type stored by the variant public: NUCLEX_SUPPORT_API VariantType GetType() const { return this->type; } /// Assigns a boolean value to the variant /// Boolean that will be assigned /// The variant itself public: NUCLEX_SUPPORT_API Variant &operator =(bool newValue) { free(); this->booleanValue = newValue; this->type = VariantType::Boolean; return *this; } /// Assigns an unsigned 8 bit integer value to the variant /// Unsigned 8 bit integer value that will be assigned /// The variant itself public: NUCLEX_SUPPORT_API Variant &operator =(std::uint8_t newValue) { free(); this->uint8Value = newValue; this->type = VariantType::Uint8; return *this; } /// Assigns a signed 8 bit integer value to the variant /// Signed 8 bit integer value that will be assigned /// The variant itself public: NUCLEX_SUPPORT_API Variant &operator =(std::int8_t newValue) { free(); this->int8Value = newValue; this->type = VariantType::Int8; return *this; } /// Assigns an unsigned 16 bit integer value to the variant /// Unsigned 16 bit integer value that will be assigned /// The variant itself public: NUCLEX_SUPPORT_API Variant &operator =(std::uint16_t newValue) { free(); this->uint16Value = newValue; this->type = VariantType::Uint16; return *this; } /// Assigns a signed 16 bit integer value to the variant /// Signed 16 bit integer value that will be assigned /// The variant itself public: NUCLEX_SUPPORT_API Variant &operator =(std::int16_t newValue) { free(); this->int16Value = newValue; this->type = VariantType::Int16; return *this; } /// Assigns an unsigned 32 bit integer value to the variant /// Unsigned 32 bit integer value that will be assigned /// The variant itself public: NUCLEX_SUPPORT_API Variant &operator =(std::uint32_t newValue) { free(); this->uint32Value = newValue; this->type = VariantType::Uint32; return *this; } /// Assigns a signed 32 bit integer value to the variant /// Signed 32 bit integer value that will be assigned /// The variant itself public: NUCLEX_SUPPORT_API Variant &operator =(std::int32_t newValue) { free(); this->int32Value = newValue; this->type = VariantType::Int32; return *this; } /// Assigns an unsigned 64 bit integer value to the variant /// Unsigned 64 bit integer value that will be assigned /// The variant itself public: NUCLEX_SUPPORT_API Variant &operator =(std::uint64_t newValue) { free(); this->uint64Value = newValue; this->type = VariantType::Uint64; return *this; } /// Assigns a signed 64 bit integer value to the variant /// Signed 64 bit integer value that will be assigned /// The variant itself public: NUCLEX_SUPPORT_API Variant &operator =(std::int64_t newValue) { free(); this->int64Value = newValue; this->type = VariantType::Int64; return *this; } /// Assigns a floating point value to the variant /// Floating point value that will be assigned /// The variant itself public: NUCLEX_SUPPORT_API Variant &operator =(float newValue) { free(); this->floatValue = newValue; this->type = VariantType::Float; return *this; } /// Assigns a double precision floating point value to the variant /// /// Double precision floating point value that will be assigned /// /// The variant itself public: NUCLEX_SUPPORT_API Variant &operator =(double newValue) { free(); this->doubleValue = newValue; this->type = VariantType::Double; return *this; } /// Assigns a string to the variant /// String that will be assigned /// The variant itself public: NUCLEX_SUPPORT_API Variant &operator =(const std::string &newValue) { free(); new(this->stringValueBytes) std::string(newValue); this->type = VariantType::String; return *this; } /// Assigns a wide string to the variant /// Wide string that will be assigned /// The variant itself public: NUCLEX_SUPPORT_API Variant &operator =(const std::wstring &newValue) { free(); new(this->wstringValueBytes) std::wstring(newValue); this->type = VariantType::WString; return *this; } /// Assigns an opaquely typed value to the variant /// Opaquely types value that will be assigned /// The variant itself public: NUCLEX_SUPPORT_API Variant &operator =(const std::any &newValue) { free(); new(this->anyValueBytes) std::any(newValue); this->type = VariantType::Any; return *this; } /// Assigns a pointer to the variant /// Pointer that will be assigned /// The variant itself public: NUCLEX_SUPPORT_API Variant &operator =(void *newValue) { free(); this->pointerValue = newValue; this->type = VariantType::VoidPointer; return *this; } /// Assigns a variant to hold the same value as another variant /// Other variant whose value will be copied /// The variant itself public: NUCLEX_SUPPORT_API Variant &operator =(const Variant &other); /// Assigns a variant to hold the same value as another variant /// Other variant whose value will be copied /// The variant itself public: NUCLEX_SUPPORT_API Variant &operator =(Variant &&other); /// Frees all memory used by the variant private: void free() { switch(this->type) { case VariantType::String: { reinterpret_cast(this->stringValueBytes)->~basic_string(); break; } case VariantType::WString: { reinterpret_cast(this->wstringValueBytes)->~basic_string(); break; } case VariantType::Any: { reinterpret_cast(this->anyValueBytes)->~any(); break; } default: {} // Avoids compiler warnings about unhandled enum members } } /// Type of value that the variant is holding private: VariantType type; /// Value held by the variant private: union { /// Boolean value, if the variant is holding that type bool booleanValue; /// Unsigned 8 bit integer value, if the variant is holding that type std::uint8_t uint8Value; /// Signed 8 bit integer value, if the variant is holding that type std::int8_t int8Value; /// Unsigned 16 bit integer value, if the variant is holding that type std::uint16_t uint16Value; /// Signed 16 bit integer value, if the variant is holding that type std::int16_t int16Value; /// Unsigned 32 bit integer value, if the variant is holding that type std::uint32_t uint32Value; /// Signed 32 bit integer value, if the variant is holding that type std::int32_t int32Value; /// Unsigned 64 bit integer value, if the variant is holding that type std::uint64_t uint64Value; /// Signed 64 bit integer value, if the variant is holding that type std::int64_t int64Value; /// Floating point value, if the variant is holding that type float floatValue; /// /// Double precision floating point value, if the variant is holding that type /// double doubleValue; /// String value, if the variant is holding that type std::uint8_t stringValueBytes[sizeof(std::string)]; /// Wide string value, if the variant is holding that type std::uint8_t wstringValueBytes[sizeof(std::wstring)]; /// Opaque value of an arbitrary type, if the variant is holding that std::uint8_t anyValueBytes[sizeof(std::any)]; /// Void pointer value, if the variant is holding that type void *pointerValue; }; }; // ------------------------------------------------------------------------------------------- // }} // namespace Nuclex::Support #endif // NUCLEX_SUPPORT_VARIANT_H