Simple variant-value (case variant union) object, with (variant) basic types analogous to a value in any weakly typed language (like JavaScript, Lisp, etc) More...
#include <VariantValue.h>
Classes | |
struct | EqualsComparer |
Compares values as if first normalized with Normalize () method. More... | |
struct | ThreeWayComparer |
Compares values as if first normalized with Normalize () method. More... | |
Public Types | |
enum class | Type : uint8_t |
Enumeration of variant types. More... | |
Public Member Functions | |
VariantValue ()=default | |
construct a VariantValue from most any 'basic type' you would expect to find in a weakly typed language (e.g. lisp, javascript) | |
nonvirtual VariantValue & | operator= (VariantValue &&rhs) noexcept=default |
nonvirtual bool | empty () const |
nonvirtual String | ToString () const |
template<typename RETURNTYPE > requires (Private_::IVariantValueAsBasic_<RETURNTYPE> or (Common::IOptional<RETURNTYPE> and Private_::IVariantValueAsBasic_<Common::ExtractValueType_t<RETURNTYPE>>)) | |
nonvirtual RETURNTYPE | As () const |
nonvirtual | operator bool () const |
return true iff value GetType () != null; | |
nonvirtual bool | IsConvertibleTo (Type to) const |
nonvirtual VariantValue | ConvertTo (Type to) const |
Return this VariantValue converted to the given type (as if by As<T> for the T appropriate to 'Type to') | |
nonvirtual VariantValue | Normalize () const |
nonvirtual strong_ordering | operator<=> (const VariantValue &rhs) const |
compares as if first normalized with Normalize() | |
nonvirtual bool | operator== (const VariantValue &rhs) const |
compares as if first normalized with Normalize() | |
Simple variant-value (case variant union) object, with (variant) basic types analogous to a value in any weakly typed language (like JavaScript, Lisp, etc)
These objects are internally efficiently copied (shared_ptr), but have copy by value semantics (since they are never modifiable).
Design Notes: o This is similar to std::variant, but generally simpler to use because of brevity, and a well chosen (see COM, or JSON) but useful predefined set of things that are shared in the variant.
o Note that it is never possible to create circular references (e.g. with Array or Map) types because these are constructed from existing already constructed VariantValue objects, and can never be modified thereafter.
o Note that this VariantValue is analogous to, and inspired by, the Microsoft COM VARIANT object type.
o The reason we chose to interpret As<T>() as generating an exception, instead of an assertion, was based on experience using the VariantValue class. We found it was typically used extracting data from an unreliable external source (typically JSON - either from a web service or configuration data), and if that was mal-formed (since there is no JSON schema) - we would assert. At least for this usage (and that now seems the primary one) exceptions on type mismatches seemed most helpful.
From section from section 3.9.1 of http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2012/n3337.pdf There are five standard signed integer types : signed char, short int, int, long int, and long long int. In this list, each type provides at least as much storage as those preceding it in the list. For each of the standard signed integer types, there exists a corresponding (but different) standard unsigned integer type: unsigned char, unsigned short int, unsigned int, unsigned long int, and unsigned long long int, each of which occupies the same amount of storage and has the same alignment requirements.
So for now - we just store the largest signed and unsigned integer types and cast down to what the user users/requests.
EQUALSCOMPARER: If exactTypeMatchOnly is true, no type coercion takes place, but by default types are automatically coerced, if reasonable, so that they can be compared for equality. When comparing two items, at least one of which is a floating point number, the other type is coerced into a floating point number and @Math::NearlyEquals() is used (because often these values come from serialization/deserialization which loses a tiny bit of precision). Note that NANs compare as equal, and Equals ("NAN", Math::nan<double> ()) compares as true. When comparing any other types (except Map or Array) with a String, the to types are coerced into Strings, and compared as strings.
For example, dates written out as ISO-2022 strings, and read back as such. Numbers - like NAN written portably and read back. For structured types (like arrays) - but explicitly use JSON reader/writer ;-).
TODO:
@todo XPath / JPath / JSONPath feature in DataExchange::VariantValue - https://github.com/SophistSolutions/Stroika/issues/110 @todo POSSIBLY add support for Precision (see Characters::Float2String) - once that module has cleaned up notion of precision. Not sure how to add unobtrusively. - for As<String>()? optional param?... Maybe Float2StringOptions is optional param to As<String> ()??? @todo Add SINFAE CTOR template, so we can lose explicit map<> CTOR, and handle other cases automatically, like vector<wstring> CTOR. And/or fix KeyValuePair<> ctor so maps 'convertible' key and convertible 'value' types. @todo Use Debug::AssertExternallySynchronizedMutex<> to assure not used from multiple threads. @todo Re-review the signed/unsigned compare etc code. I think its all correct, but its tricky enough to warrant a careful review
Definition at line 170 of file Foundation/DataExchange/VariantValue.h.
|
strong |
Enumeration of variant types.
Definition at line 210 of file Foundation/DataExchange/VariantValue.h.
|
default |
construct a VariantValue from most any 'basic type' you would expect to find in a weakly typed language (e.g. lisp, javascript)
When constructing a 'stringish' VariantValue, any arguments used to construct a String object maybe used, with the same constraints (e.g. req basic_string_view<char> MUST contain only ascii text)
|
defaultnoexcept |
Assign anything to a VariantValue you can construct a VariantValue with.
bool VariantValue::empty | ( | ) | const |
Return if the given object is logically 'empty'. The meaning of this depends on the type of the variant. For example, eNull type is always empty. Most other types are empty iff their native type (e.g. basic_string) is 'empty'. A FLOAT is empty iff its std::isnan(). Booleans and integer types are never empty (even if zero/false). Maps and arrays are empty iff they contain no elements;
Definition at line 276 of file Foundation/DataExchange/VariantValue.cpp.
String VariantValue::ToString | ( | ) | const |
Definition at line 323 of file Foundation/DataExchange/VariantValue.cpp.
nonvirtual RETURNTYPE Stroika::Foundation::DataExchange::VariantValue::As | ( | ) | const |
Only (see requires) types supported; There is no generic As<T> implementation.
If the caller attempts a conversion that isn't supported, or doesn't make sense then DataExchange::BadFormatException will be thrown (not assertion error).
For the optional<OF_T> specialization, if this is empty (nullopt counts as empty) - then return nullopt, and only otherwise attempt to coerce (As<the optional type>).
|
explicit |
return true iff value GetType () != null;
And because of that - at least partly - we avoided support explicit operator (for each type in the basic variant like explicit operator String).
Definition at line 58 of file Foundation/DataExchange/VariantValue.inl.
bool VariantValue::IsConvertibleTo | ( | Type | to | ) | const |
Return true if this VariantValue would be convertible to the argument type. Note - not just potentially convertible, but the data is actually formatted to allow the conversion. So for example, if to is DateTime, then an ill formed string would NOT be IsConvertibleTo
Definition at line 358 of file Foundation/DataExchange/VariantValue.cpp.
VariantValue VariantValue::ConvertTo | ( | Type | to | ) | const |
Return this VariantValue converted to the given type (as if by As<T> for the T appropriate to 'Type to')
This will throw DataExchange::BadFormatException if the conversion doesn't make sense.
Definition at line 373 of file Foundation/DataExchange/VariantValue.cpp.
VariantValue VariantValue::Normalize | ( | ) | const |
Return a (possibly new, possibly same) object with certain 'features' standardized. Essentially this converts to basic JSON-writable types. So BLOB, and Date, etc, converted to string, integers converted to Float (number), nans converted to strings, etc.
This also produced 'sorted' mappings.
You generally don't need to use this, but its helpful for the definition of equality and comparison.
Definition at line 408 of file Foundation/DataExchange/VariantValue.cpp.