{ +-------------------------------------------------------------+ }
{ |                                                             | }
{ |   GM-Software                                               | }
{ |   ===========                                               | }
{ |                                                             | }
{ |   Project: All Projects                                     | }
{ |                                                             | }
{ |   Description: Union value for multiple types like variant  | }
{ |                but smaller and faster.                      | }
{ |                                                             | }
{ |   Copyright (C) - 2020 - Gerrit Moeller.                    | }
{ |                                                             | }
{ |   Source code distributed under MIT license.                | }
{ |                                                             | }
{ |   See: https://www.gm-software.de                           | }
{ |                                                             | }
{ +-------------------------------------------------------------+ }

{$INCLUDE GMCompilerSettings.inc}

unit GMUnionValue;

interface

uses GMStrDef, Variants;

type

  {$if FPC_FULLVERSION >= 30101}
    // SelfManaged Record size will be smaller because no additional pointers in the managed part of the reord are needed
    // SizeOf(RGMUnionValue) = 9 Byte on 32-Bit and 64-Bit platforms!
    {$define UseSelfManaged}
    {$define WithIntfType}
  {$endif}


  EGMUnionValueType = (uvtUnassigned, uvtNull, uvtString, uvtBoolean, uvtInt16, uvtInt32, uvtInt64, uvtDouble, uvtDateTime,
                       uvtPointer{$ifdef WithIntfType},uvtInterface{$endif});

  //
  // TStrType is meant to be a managed string data type like: UnicodeString, AnsiString, RawByteString, Utf8String or AnsiString(CP_XXXX)
  // WideString only works when UseSelfManaged is NOT defined
  //
  RGMGenericUnionValue<TStrType> = packed record
   private
    {$ifndef UseSelfManaged}
     StringValue: TStrType;
     {$ifdef WithIntfType}
      IntfValue: IUnknown;
     {$endif}
    {$endif}

    class operator Implicit(const AUnionValue: RGMGenericUnionValue<TStrType>): Boolean;
    class operator Implicit(const AUnionValue: RGMGenericUnionValue<TStrType>): Byte;
    class operator Implicit(const AUnionValue: RGMGenericUnionValue<TStrType>): SmallInt;
    class operator Implicit(const AUnionValue: RGMGenericUnionValue<TStrType>): Word;
    class operator Implicit(const AUnionValue: RGMGenericUnionValue<TStrType>): LongInt;
    class operator Implicit(const AUnionValue: RGMGenericUnionValue<TStrType>): LongWord;
    class operator Implicit(const AUnionValue: RGMGenericUnionValue<TStrType>): Int64;
    class operator Implicit(const AUnionValue: RGMGenericUnionValue<TStrType>): QWord;
    class operator Implicit(const AUnionValue: RGMGenericUnionValue<TStrType>): Pointer;
    class operator Implicit(const AUnionValue: RGMGenericUnionValue<TStrType>): Single;
    class operator Implicit(const AUnionValue: RGMGenericUnionValue<TStrType>): Double;
    class operator Implicit(const AUnionValue: RGMGenericUnionValue<TStrType>): TDateTime;
    class operator Implicit(const AUnionValue: RGMGenericUnionValue<TStrType>): UnicodeString;
    class operator Implicit(const AUnionValue: RGMGenericUnionValue<TStrType>): WideString;
    class operator Implicit(const AUnionValue: RGMGenericUnionValue<TStrType>): AnsiString;
    class operator Implicit(const AUnionValue: RGMGenericUnionValue<TStrType>): Utf8String;
    class operator Implicit(const AUnionValue: RGMGenericUnionValue<TStrType>): Variant;
    class operator Implicit(const AUnionValue: RGMGenericUnionValue<TStrType>): OleVariant;

    class operator Implicit(AValue: EGMUnionValueType): RGMGenericUnionValue<TStrType>;
    class operator Implicit(AValue: Boolean): RGMGenericUnionValue<TStrType>;
    class operator Implicit(AValue: SmallInt): RGMGenericUnionValue<TStrType>;
    class operator Implicit(AValue: LongInt): RGMGenericUnionValue<TStrType>;
    class operator Implicit(AValue: Int64): RGMGenericUnionValue<TStrType>;
    class operator Implicit(AValue: Double): RGMGenericUnionValue<TStrType>;
    class operator Implicit(AValue: TDateTime): RGMGenericUnionValue<TStrType>;
    class operator Implicit(AValue: Pointer): RGMGenericUnionValue<TStrType>;
    class operator Implicit(AValue: UnicodeString): RGMGenericUnionValue<TStrType>;
    class operator Implicit(AValue: WideString): RGMGenericUnionValue<TStrType>;
    class operator Implicit(AValue: AnsiString): RGMGenericUnionValue<TStrType>;
    class operator Implicit(AValue: Utf8String): RGMGenericUnionValue<TStrType>;
    class operator Implicit(AValue: Variant): RGMGenericUnionValue<TStrType>;
    class operator Implicit(AValue: OleVariant): RGMGenericUnionValue<TStrType>;

    procedure InternalAssignString(const AStrVal: TStrType); inline;

    {$ifdef WithIntfType}
    procedure InternalAssignIntf(const AIntfVal: IUnknown); inline;
    class operator Implicit(AValue: IUnknown): RGMGenericUnionValue<TStrType>;
    class operator Implicit(const AUnionValue: RGMGenericUnionValue<TStrType>): IUnknown;
    {$endif}

   public
    class operator Initialize(var AUnionValue: RGMGenericUnionValue<TStrType>);
    {$ifdef UseSelfManaged}
    class operator Finalize(var AUnionValue: RGMGenericUnionValue<TStrType>);
    class operator Copy(constref ASrc: RGMGenericUnionValue<TStrType>; var ADst: RGMGenericUnionValue<TStrType>);
    class operator AddRef(var ADst: RGMGenericUnionValue<TStrType>);
    {$endif}

    class operator Equal(const AValue1, AValue2: RGMGenericUnionValue<TStrType>): Boolean;
    class operator Equal(const AUnionValue: RGMGenericUnionValue<TStrType>; AValue: Int64): Boolean;
    class operator Equal(const AUnionValue: RGMGenericUnionValue<TStrType>; AValue: LongInt): Boolean;
    class operator Equal(const AUnionValue: RGMGenericUnionValue<TStrType>; AValue: Pointer): Boolean;
    {$ifdef WithIntfType}
    class operator Equal(const AUnionValue: RGMGenericUnionValue<TStrType>; AValue: IUnknown): Boolean;
    {$endif}
    class operator Equal(const AUnionValue: RGMGenericUnionValue<TStrType>; AValue: Double): Boolean;
    //class operator Equal(const AUnionValue: RGMGenericUnionValue<TStrType>; AValue: TDateTime): Boolean;
    class operator Equal(const AUnionValue: RGMGenericUnionValue<TStrType>; AValue: UnicodeString): Boolean;
    class operator Equal(const AUnionValue: RGMGenericUnionValue<TStrType>; AValue: WideString): Boolean;
    class operator Equal(const AUnionValue: RGMGenericUnionValue<TStrType>; AValue: AnsiString): Boolean;
    class operator Equal(const AUnionValue: RGMGenericUnionValue<TStrType>; AValue: Utf8String): Boolean;

    class operator NotEqual(const AValue1, AValue2: RGMGenericUnionValue<TStrType>): Boolean;

    class operator Add(const AValue1, AValue2: RGMGenericUnionValue<TStrType>): RGMGenericUnionValue<TStrType>;
    class operator Multiply(const AValue1, AValue2: RGMGenericUnionValue<TStrType>): RGMGenericUnionValue<TStrType>;
    class operator Divide(const AValue1, AValue2: RGMGenericUnionValue<TStrType>): RGMGenericUnionValue<TStrType>;
    class operator IntDivide(const AValue1, AValue2: RGMGenericUnionValue<TStrType>): RGMGenericUnionValue<TStrType>;
    class operator Modulus(const AValue1, AValue2: RGMGenericUnionValue<TStrType>): RGMGenericUnionValue<TStrType>;

    class operator LessThan(const AValue1, AValue2: RGMGenericUnionValue<TStrType>): Boolean;
    class operator GreaterThan(const AValue1, AValue2: RGMGenericUnionValue<TStrType>): Boolean;
    class operator LessThanOrEqual(const AValue1, AValue2: RGMGenericUnionValue<TStrType>): Boolean;
    class operator GreaterThanOrEqual(const AValue1, AValue2: RGMGenericUnionValue<TStrType>): Boolean;

    function IsNull: Boolean;
    function IsUnassigned: Boolean;
    function IsNullOrUnassigned: Boolean;

    function AsBoolean: Boolean;
    function AsInt16: SmallInt;
    function AsInt32: LongInt;
    function AsInt64: Int64;
    function AsDouble: Double;
    function AsDateTime: TDateTime;
    function AsString: TGMString;
    //function AsDisplayString: TGMString;
    function AsPointer: Pointer;
    {$ifdef WithIntfType}
    function AsInterface: IUnknown;
    {$endif}

    function AsBooleanDflt(const ADefaultValue: Boolean = False): Boolean;
    function AsInt16Dflt(const ADefaultValue: SmallInt = 0): SmallInt;
    function AsInt32Dflt(const ADefaultValue: LongInt = 0): LongInt;
    function AsInt64Dflt(const ADefaultValue: Int64 = 0): Int64;
    function AsDoubleDflt(const ADefaultValue: Double = 0.0): Double;
    function AsDateTimeDflt(const ADefaultValue: TDateTime = 0): TDateTime;
    function AsPointerDflt(const ADefaultValue: Pointer = nil): Pointer;
    function AsStringDflt(const ADefaultValue: TGMString = ''): TGMString;
    {$ifdef WithIntfType}
    function AsInterfaceDflt(const ADefaultValue: IUnknown = nil): IUnknown;
    {$endif}

    function ValueTypeName: TGMSTring;

    case ValueType: EGMUnionValueType of
     {$ifdef UseSelfManaged}
      uvtString: (StringValue: Pointer);
      {$ifdef WithIntfType}
       uvtInterface: (IntfValue: Pointer);
      {$endif}
     {$endif}
     uvtBoolean: (BoolValue: Boolean);
     uvtInt16: (Int16Value: SmallInt);
     uvtInt32: (Int32Value: LongInt);
     uvtInt64: (Int64Value: Int64);
     uvtDouble: (DoubleValue: Double);
     uvtDateTime: (DateTimeValue: TDateTime);
     uvtPointer: (PointerValue: Pointer);
  end;


  PGMUnionValue = ^RGMUnionValue;
  RGMUnionValue = RGMGenericUnionValue<TGMString>;


  IGMGetUnionValue = interface(IUnknown)
    ['{9BB4E638-DB7C-4583-9EE3-410E4FB0F5CB}']
    function GetUnionValue: RGMUnionValue;
    property Value: RGMUnionValue read GetUnionValue;
  end;


  IGMGetSetUnionValue = interface(IGMGetUnionValue)
    ['{3A3129DF-850E-40DA-AEF0-945A0BF2539C}']
    procedure SetUnionValue(const AValue: RGMUnionValue);
    property Value: RGMUnionValue read GetUnionValue write SetUnionValue;
  end;


  TGMUnionValToStrFunc = function (const AValue: RGMUnionValue): TGMString;


//
// To be used with GMNamesAndValuesAsString and GMSeparatedValues
//
function GMUnionValueAsString(const AValue: RGMUnionValue): TGMString;
function GMUnionValueAsQuotedString(const AValue: RGMUnionValue): TGMString;

function GMUnionValueTypeName(const AValueType: EGMUnionValueType): TGMString;

function GMUnionValueAsType(const AValue: RGMUnionValue; const ADestType: EGMUnionValueType): RGMUnionValue;
function GMUnionValueAsTypeDflt(const AValue: RGMUnionValue; const ADestType: EGMUnionValueType): RGMUnionValue;

procedure RaiseExceptionFmt(const AMsg: TGMString = '';
                            const AObj: TObject = nil;
                            const ARoutineName: TGMString = '');


resourcestring

  srValueConvertToErrFmt = 'Cannot convert union value of type %s to type %s';
  srValueConvertFromErrFmt = 'Cannot convert from %s to union value';


implementation

{$macro on}

uses SysUtils, TypInfo, GMIntf, GMCommon;

type

  XGMUnionValueConvertError = class(EGMException);


resourcestring

  srValueTypeOutofBounds = 'UnionValue ValueType %d out of bounds, must be in [%d .. %d]';


function GMUnionValueTypeName(const AValueType: EGMUnionValueType): TGMString;
begin
  // AValueType enum value out of bounds may happen if not proper initialized
  if GMIsInRange(Ord(AValueType), Ord(Low(AValueType)), Ord(High(AValueType))) then
    Result := GetEnumName(TypeInfo(EGMUnionValueType), Ord(AValueType))
  else
    Result := GMFormat(srValueTypeOutofBounds, [Ord(AValueType), Low(AValueType), High(AValueType)]);
end;

function GMUnionValueAsString(const AValue: RGMUnionValue): TGMString;
begin
  Result := AValue.AsStringDflt;
end;

function GMUnionValueAsQuotedString(const AValue: RGMUnionValue): TGMString;
begin
  Result := GMQuote(GMUnionValueAsString(AValue), '"', '"');
end;

function GMUnionValueAsType(const AValue: RGMUnionValue; const ADestType: EGMUnionValueType): RGMUnionValue;
begin
  //Result := Default(RGMUnionValue);
  case ADestType of
   uvtUnassigned: Result := uvtUnassigned;
   uvtNull: Result := uvtNull;
   uvtString: Result := AValue.AsString;
   uvtBoolean: Result := AValue.AsBoolean;
   uvtInt16: Result := AValue.AsInt16;
   uvtInt32: Result := AValue.AsInt32;
   uvtInt64: Result := AValue.AsInt64;
   uvtDouble: Result := AValue.AsDouble;
   uvtDateTime: Result := AValue.AsDateTime;
   uvtPointer: Result := AValue.AsPointer;
  end;
end;

function GMUnionValueAsTypeDflt(const AValue: RGMUnionValue; const ADestType: EGMUnionValueType): RGMUnionValue;
begin
  //Result := Default(RGMUnionValue);
  case ADestType of
   uvtUnassigned: Result := uvtUnassigned;
   uvtNull: Result := uvtNull;
   uvtString: Result := AValue.AsStringDflt;
   uvtBoolean: Result := AValue.AsBooleanDflt;
   uvtInt16: Result := AValue.AsInt16Dflt;
   uvtInt32: Result := AValue.AsInt32Dflt;
   uvtInt64: Result := AValue.AsInt64Dflt;
   uvtDouble: Result := AValue.AsDoubleDflt;
   uvtDateTime: Result := AValue.AsDateTimeDflt;
   uvtPointer: Result := AValue.AsPointerDflt;
  end;
end;

procedure RaiseExceptionFmt(const AMsg: TGMString = cDfltExceptionMsg;
                            const AObj: TObject = nil;
                            const ARoutineName: TGMString = cDfltRoutineName);
                            //const ASeverityLevel: TGMSeverityLevel = svError;
                            //const AHelpCtx: LongInt = cDfltHelpCtx);
begin
  raise XGMUnionValueConvertError.ObjError(AMsg, AObj, ARoutineName);
end;


{ --------------------------------------------------- }
{ ---- RGMGenericUnionValue<TStrType, TIntfType> ---- }
{ --------------------------------------------------- }

//const
//
//  cBoolInt: array [Boolean] of Int64 = (0, 1);   moved to GMCommon unit
//  cBoolStr: array [Boolean] of TGMString = ('False', 'True');   moved to GMCommon unit

{$ifdef UseSelfManaged}
  {$define StrTypeCasted:=TStrType}
{$else}
  {$define StrTypeCasted:=}
{$endif}


class operator RGMGenericUnionValue<TStrType>.Initialize(var AUnionValue: RGMGenericUnionValue<TStrType>);
begin
  FillByte(AUnionValue, SizeOf(AUnionValue), 0);
  //AUnionValue := Default(RGMGenericUnionValue<TStrType>); <- crashes in some cases because it calls finalize
  //AUnionValue.ValueType := uvtUnassigned;
  //AUnionValue.Int64Value := 0;
  //AUnionValue.DoubleValue := 0;
end;

procedure RGMGenericUnionValue<TStrType>.InternalAssignString(const AStrVal: TStrType);
begin
  // Assumes Finalize() has been called before
  if ValueType <> uvtString then
   begin
    {$ifdef UseSelfManaged} StringValue := nil; {$endif}
    ValueType := uvtString;
   end;

   StrTypeCasted(StringValue) := AStrVal;
   //vfGMTrace('String RefCount: '+GMIntToStr(StringRefCount(AStrVal)), 'REFCOUNT');
end;

{$ifdef WithIntfType}
procedure RGMGenericUnionValue<TStrType>.InternalAssignIntf(const AIntfVal: IUnknown);
begin
  // Assumes Finalize() has been called before
  if ValueType <> uvtInterface then
   begin
    {$ifdef UseSelfManaged} IntfValue := nil; {$endif}
    ValueType := uvtInterface;
   end;

  IUnknown(IntfValue) := AIntfVal;
end;
{$endif}

{$ifdef UseSelfManaged}
{$if FPC_FULLVERSION < 30301}
  {$ERROR Self managed strings need FPC version >= 3.3.1}
{$endif}

class operator RGMGenericUnionValue<TStrType>.Finalize(var AUnionValue: RGMGenericUnionValue<TStrType>);
begin
  case AUnionValue.ValueType of
   uvtString: if (AUnionValue.StringValue <> nil) then StrTypeCasted(AUnionValue.StringValue) := '';
   {$ifdef WithIntfType}
   uvtInterface: if (AUnionValue.IntfValue <> nil) then IUnknown(AUnionValue.IntfValue) := nil;
   {$endif}
  end;
end;

class operator RGMGenericUnionValue<TStrType>.Copy(constref ASrc: RGMGenericUnionValue<TStrType>; var ADst: RGMGenericUnionValue<TStrType>);
begin
  Finalize(ADst);

  case ASrc.ValueType of
   uvtString: ADst.InternalAssignString(StrTypeCasted(ASrc.StringValue));
   {$ifdef WithIntfType}
   uvtInterface: ADst.InternalAssignIntf(IUnknown(ASrc.IntfValue));
   {$endif}
   else System.Move(Asrc, ADst, SizeOf(ADst));
  end;
end;

class operator RGMGenericUnionValue<TStrType>.AddRef(var ADst: RGMGenericUnionValue<TStrType>);
//
// Desperately missing something like the following here:   function TStringHelper.AddRef: Integer;
//
type PStrRec = ^TStrRec;  // <- These types are the same for unicode strings and ansi strings
     TStrRec = Record
       CodePage    : TSystemCodePage;
       ElementSize : Word;
   {$if not defined(VER3_2)}
     {$ifdef CPU64}
       Ref         : Longint;
     {$else}
       Ref         : SizeInt;
     {$endif}
   {$else}
     {$ifdef CPU64}
       { align fields  }
	   Dummy       : DWord;
     {$endif CPU64}
       Ref         : SizeInt;
   {$endif}
       Len         : SizeInt;
     end;

const UnicodeFirstOff = SizeOf(TStrRec);

begin
  case ADst.ValueType of
   uvtString: if ADst.StringValue <> nil then
                 InterlockedIncrement(PStrRec(ADst.StringValue - UnicodeFirstOff)^.Ref); // <- This hack is so bad ..
                 // StrTypeCasted(ADst.StringValue).AddRef; <- this would be so much better ...
   {$ifdef WithIntfType}
   uvtInterface: if (ADst.IntfValue <> nil) then IUnknown(ADst.IntfValue)._AddRef;
   {$endif}
  end;
end;
{$endif}

function RGMGenericUnionValue<TStrType>.IsNull: Boolean;
begin
  Result := ValueType = uvtNull;
end;

function RGMGenericUnionValue<TStrType>.IsUnassigned: Boolean;
begin
  Result := ValueType = uvtUnassigned;
end;

function RGMGenericUnionValue<TStrType>.IsNullOrUnassigned: Boolean;
begin
  Result := ValueType in [uvtUnassigned, uvtNull];
end;

function RGMGenericUnionValue<TStrType>.ValueTypeName: TGMSTring;
begin
  Result := GMUnionValueTypeName(ValueType);
end;

{$IFDEF FPC}{$push}{$WARN 5059 off : Function result variable does not seem to be initialized}{$ENDIF}
class operator RGMGenericUnionValue<TStrType>.Implicit(const AUnionValue: RGMGenericUnionValue<TStrType>): Boolean;
begin
  case AUnionValue.ValueType of
   //uvtUnassigned, uvtNull,
   uvtBoolean: Result := AUnionValue.BoolValue;
   uvtString: Result := GMStrToBool(StrTypeCasted(AUnionValue.StringValue));
   uvtInt16: Result := AUnionValue.Int16Value <> 0;
   uvtInt32: Result := AUnionValue.Int32Value <> 0;
   uvtInt64: Result := AUnionValue.Int64Value <> 0;
   uvtDouble: Result := AUnionValue.DoubleValue <> 0.0;
   uvtDateTime: Result := AUnionValue.DateTimeValue <> 0.0;
   uvtPointer: Result := AUnionValue.PointerValue <> nil;
//   else raise XGMUnionValueConvertError.ObjError(GMFormat(srValueConvertToErrFmt, [AUnionValue.ValueTypeName, 'Boolean']), nil, 'RGMGenericUnionValue<TStrType>.Implicit');
   else RaiseExceptionFmt(GMFormat(srValueConvertToErrFmt, [AUnionValue.ValueTypeName, 'Boolean']), nil, 'RGMGenericUnionValue<TStrType>.Implicit');
  end;
end;

{$define InsertUnionIntCase:=//jvkUnassigned, jvkNull
  uvtBoolean: Result := cBoolInt[AUnionValue.BoolValue];
  uvtString: Result := GMStrToInt(StrTypeCasted(AUnionValue.StringValue));
  uvtInt16: Result := AUnionValue.Int16Value;
  uvtInt32: Result := AUnionValue.Int32Value;
  uvtInt64: Result := AUnionValue.Int64Value;
  uvtDouble: Result := Round(AUnionValue.DoubleValue);
  uvtDateTime: Result := Round(AUnionValue.DateTimeValue);
//  else raise XGMUnionValueConvertError.ObjError(GMFormat(srValueConvertToErrFmt, [AUnionValue.ValueTypeName, cResultTypeName]), nil, 'RGMGenericUnionValue<TStrType>.Implicit');
  else RaiseExceptionFmt(GMFormat(srValueConvertToErrFmt, [AUnionValue.ValueTypeName, cResultTypeName]), nil, 'RGMGenericUnionValue<TStrType>.Implicit');
  end;}


class operator RGMGenericUnionValue<TStrType>.Implicit(const AUnionValue: RGMGenericUnionValue<TStrType>): Byte;
const cResultTypeName = 'Byte';
begin
  case AUnionValue.ValueType of
  InsertUnionIntCase
end;

class operator RGMGenericUnionValue<TStrType>.Implicit(const AUnionValue: RGMGenericUnionValue<TStrType>): SmallInt;
const cResultTypeName = 'SmallInt';
begin
  case AUnionValue.ValueType of
  InsertUnionIntCase
end;

class operator RGMGenericUnionValue<TStrType>.Implicit(const AUnionValue: RGMGenericUnionValue<TStrType>): Word;
const cResultTypeName = 'Word';
begin
  case AUnionValue.ValueType of
  InsertUnionIntCase
end;

class operator RGMGenericUnionValue<TStrType>.Implicit(const AUnionValue: RGMGenericUnionValue<TStrType>): LongInt;
const cResultTypeName = 'LongInt';
begin
  case AUnionValue.ValueType of
  {$IFDEF CPU32}
   uvtPointer: Result := LongInt(AUnionValue.PointerValue);
  {$ENDIF}
  InsertUnionIntCase
end;

class operator RGMGenericUnionValue<TStrType>.Implicit(const AUnionValue: RGMGenericUnionValue<TStrType>): LongWord;
const cResultTypeName = 'LongWord';
begin
  case AUnionValue.ValueType of
   {$IFDEF CPU32}
    uvtPointer: Result := LongWord(AUnionValue.PointerValue);
   {$ENDIF}
   InsertUnionIntCase
end;

class operator RGMGenericUnionValue<TStrType>.Implicit(const AUnionValue: RGMGenericUnionValue<TStrType>): Int64;
const cResultTypeName = 'Int64';
begin
  case AUnionValue.ValueType of
   {$IFDEF CPU64}
    uvtPointer: Result := Int64(AUnionValue.PointerValue);
   {$ELSE}
    uvtPointer: Result := PtrUInt(AUnionValue.PointerValue);
   {$ENDIF}
   InsertUnionIntCase
end;

class operator RGMGenericUnionValue<TStrType>.Implicit(const AUnionValue: RGMGenericUnionValue<TStrType>): QWord;
const cResultTypeName = 'QWord';
begin
  case AUnionValue.ValueType of
   {$IFDEF CPU64}
    uvtPointer: Result := QWord(AUnionValue.PointerValue);
   {$ELSE}
    uvtPointer: Result := PtrUInt(AUnionValue.PointerValue);
   {$ENDIF}
   InsertUnionIntCase
end;

class operator RGMGenericUnionValue<TStrType>.Implicit(const AUnionValue: RGMGenericUnionValue<TStrType>): Pointer;
begin
  case AUnionValue.ValueType of
   //jvkUnassigned, jvkNull
   //uvtBoolean: Result := cBoolInt[AUnionValue.BoolValue];
   uvtString: Result := Pointer(AUnionValue.StringValue);
   //uvtInt16: Result := AUnionValue.Int16Value;
   {$IFDEF CPU64}
    uvtInt32: Result := Pointer(Int64(AUnionValue.Int32Value));
    uvtInt64: Result := Pointer(AUnionValue.Int64Value);
   {$ELSE}
    uvtInt32: Result := Pointer(AUnionValue.Int32Value);
    uvtInt64: Result := Pointer(PtrUInt(AUnionValue.Int64Value));
   {$ENDIF}

   //uvtDouble: Result := AUnionValue.DoubleValue;
   //uvtDateTime: Result := AUnionValue.DateTimeValue;
   uvtPointer: Result := AUnionValue.PointerValue;
   uvtInterface: Result := AUnionValue.IntfValue;
//   else raise XGMUnionValueConvertError.ObjError(GMFormat(srValueConvertToErrFmt, [AUnionValue.ValueTypeName, 'Pointer']), nil, 'RGMGenericUnionValue<TStrType>.Implicit');
   else RaiseExceptionFmt(GMFormat(srValueConvertToErrFmt, [AUnionValue.ValueTypeName, 'Pointer']), nil, 'RGMGenericUnionValue<TStrType>.Implicit');
  end;
end;

class operator RGMGenericUnionValue<TStrType>.Implicit(const AUnionValue: RGMGenericUnionValue<TStrType>): Single;
begin
  case AUnionValue.ValueType of
   //jvkUnassigned, jvkNull
   uvtBoolean:  Result := cBoolInt[AUnionValue.BoolValue];
   uvtString:   Result := GMStrToSingle(StrTypeCasted(AUnionValue.StringValue));
   uvtInt16:    Result := AUnionValue.Int16Value;
   uvtInt32:    Result := AUnionValue.Int32Value;
   uvtInt64:    Result := AUnionValue.Int64Value;
   uvtDouble:   Result := AUnionValue.DoubleValue;
   uvtDateTime: Result := AUnionValue.DateTimeValue;
   uvtPointer:  Result := PtrUInt(AUnionValue.PointerValue);
//   else raise XGMUnionValueConvertError.ObjError(GMFormat(srValueConvertToErrFmt, [AUnionValue.ValueTypeName, 'Single']), nil, 'RGMGenericUnionValue<TStrType>.Implicit');
     else RaiseExceptionFmt(GMFormat(srValueConvertToErrFmt, [AUnionValue.ValueTypeName, 'Single']), nil, 'RGMGenericUnionValue<TStrType>.Implicit');
  end;
end;

class operator RGMGenericUnionValue<TStrType>.Implicit(const AUnionValue: RGMGenericUnionValue<TStrType>): Double;
begin
  case AUnionValue.ValueType of
   //jvkUnassigned, jvkNull
   uvtBoolean:  Result := cBoolInt[AUnionValue.BoolValue];
   uvtString:   Result := GMStrToDouble(StrTypeCasted(AUnionValue.StringValue));
   uvtInt16:    Result := AUnionValue.Int16Value;
   uvtInt32:    Result := AUnionValue.Int32Value;
   uvtInt64:    Result := AUnionValue.Int64Value;
   uvtDouble:   Result := AUnionValue.DoubleValue;
   uvtDateTime: Result := AUnionValue.DateTimeValue;
   uvtPointer:  Result := PtrUInt(AUnionValue.PointerValue);
//   else raise XGMUnionValueConvertError.ObjError(GMFormat(srValueConvertToErrFmt, [AUnionValue.ValueTypeName, 'Double']), nil, 'RGMGenericUnionValue<TStrType>.Implicit');
     else RaiseExceptionFmt(GMFormat(srValueConvertToErrFmt, [AUnionValue.ValueTypeName, 'Double']), nil, 'RGMGenericUnionValue<TStrType>.Implicit');
  end;
end;

class operator RGMGenericUnionValue<TStrType>.Implicit(const AUnionValue: RGMGenericUnionValue<TStrType>): TDateTime;
begin
  case AUnionValue.ValueType of
   //jvkUnassigned, jvkNull
   uvtString:   Result := GMFixedDecodeDateTime(StrTypeCasted(AUnionValue.StringValue)); // GMStrToDouble(StrTypeCasted(AUnionValue.StringValue));
   uvtInt16:    Result := AUnionValue.Int16Value;
   uvtInt32:    Result := AUnionValue.Int32Value;
   uvtInt64:    Result := AUnionValue.Int64Value;
   uvtDouble:   Result := AUnionValue.DoubleValue;
   uvtDateTime: Result := AUnionValue.DateTimeValue;
//   else raise XGMUnionValueConvertError.ObjError(GMFormat(srValueConvertToErrFmt, [AUnionValue.ValueTypeName, 'TDateTime']), nil, 'RGMGenericUnionValue<TStrType>.Implicit');
     else RaiseExceptionFmt(GMFormat(srValueConvertToErrFmt, [AUnionValue.ValueTypeName, 'TDateTime']), nil, 'RGMGenericUnionValue<TStrType>.Implicit');
  end;
end;
{$IFDEF FPC}{$pop}{$ENDIF}

{$define InsertStringResultCommonPart:=
  case AUnionValue.ValueType of
   //jvkUnassigned, jvkNull
   uvtBoolean: Result := cBoolStr[AUnionValue.BoolValue];
   uvtString: Result := StrTypeCasted(AUnionValue.StringValue);
   uvtInt16: Result := GMIntToStr(AUnionValue.Int16Value);
   uvtInt32: Result := GMIntToStr(AUnionValue.Int32Value);
   uvtInt64: Result := GMIntToStr(AUnionValue.Int64Value);
   uvtDouble: Result := GMDoubleToStr(AUnionValue.DoubleValue);
   uvtDateTime: Result := GMFixedEncodeDateTime(AUnionValue.DateTimeValue); // GMDateTimeToStr(AUnionValue.DateTimeValue);
   uvtPointer: Result := GMIntToHexStr(PtrUInt(AUnionValue.PointerValue));
   {$ifdef WithIntfType}
   uvtInterface: Result := GMIntToHexStr(PtrUInt(AUnionValue.IntfValue));
   {$endif}
//   else raise XGMUnionValueConvertError.ObjError(GMFormat(srValueConvertToErrFmt, [AUnionValue.ValueTypeName, cResultTypeName]), nil, 'RGMGenericUnionValue<TStrType>.Implicit');
   else RaiseExceptionFmt(GMFormat(srValueConvertToErrFmt, [AUnionValue.ValueTypeName, cResultTypeName]), nil, 'RGMGenericUnionValue<TStrType>.Implicit');
  end;}

class operator RGMGenericUnionValue<TStrType>.Implicit(const AUnionValue: RGMGenericUnionValue<TStrType>): UnicodeString;
const cResultTypeName = 'UnicodeString';
begin
  InsertStringResultCommonPart
end;

class operator RGMGenericUnionValue<TStrType>.Implicit(const AUnionValue: RGMGenericUnionValue<TStrType>): WideString;
const cResultTypeName = 'WideString';
begin
  InsertStringResultCommonPart
end;

class operator RGMGenericUnionValue<TStrType>.Implicit(const AUnionValue: RGMGenericUnionValue<TStrType>): AnsiString;
const cResultTypeName = 'AnsiString';
begin
  InsertStringResultCommonPart
end;

class operator RGMGenericUnionValue<TStrType>.Implicit(const AUnionValue: RGMGenericUnionValue<TStrType>): Utf8String;
const cResultTypeName = 'Utf8String';
begin
  InsertStringResultCommonPart
end;

class operator RGMGenericUnionValue<TStrType>.Implicit(const AUnionValue: RGMGenericUnionValue<TStrType>): Variant;
begin
  case AUnionValue.ValueType of
   //uvtUnassigned: Result := Unassigned;
   uvtNull:     Result := Variants.Null;
   uvtString:   Result := AUnionValue.AsString;
   uvtBoolean:  Result := AUnionValue.BoolValue;
   uvtInt16:    Result := AUnionValue.Int16Value;
   uvtInt32:    Result := AUnionValue.Int32Value;
   uvtInt64:    Result := AUnionValue.Int64Value;
   uvtDouble:   Result := AUnionValue.DoubleValue;
   uvtDateTime: Result := AUnionValue.DateTimeValue;
   uvtPointer:  Result := AUnionValue.PointerValue;
   else Result := Variants.Unassigned;
  end;
end;

class operator RGMGenericUnionValue<TStrType>.Implicit(const AUnionValue: RGMGenericUnionValue<TStrType>): OleVariant;
begin
  case AUnionValue.ValueType of
   //uvtUnassigned: Result := Unassigned;
   uvtNull:     Result := Variants.Null;
   uvtString:   Result := AUnionValue.AsString;
   uvtBoolean:  Result := AUnionValue.BoolValue;
   uvtInt16:    Result := AUnionValue.Int16Value;
   uvtInt32:    Result := AUnionValue.Int32Value;
   uvtInt64:    Result := AUnionValue.Int64Value;
   uvtDouble:   Result := AUnionValue.DoubleValue;
   uvtDateTime: Result := AUnionValue.DateTimeValue;
   //uvtPointer: Result := AValue.PointerValue;
   else Result := Variants.Unassigned;
  end;
end;

{$ifdef WithIntfType}
class operator RGMGenericUnionValue<TStrType>.Implicit(const AUnionValue: RGMGenericUnionValue<TStrType>): IUnknown;
begin
  case AUnionValue.ValueType of
   //uvtUnassigned: Result := Unassigned;
   //{$ifdef CPU64}
   //uvtInt64: Result := Pointer(AUnionValue.Int64Value);
   //{$else}
   //uvtInt32: Result := Pointer(AUnionValue.Int32Value);
   //{$endif}

   uvtPointer: Result := AUnionValue.PointerValue;
   {$ifdef WithIntfType}
   uvtInterface: Result := IUnknown(AUnionValue.IntfValue);
   {$endif}
   else RaiseExceptionFmt(GMFormat(srValueConvertToErrFmt, [AUnionValue.ValueTypeName, 'IUnknown']), nil, 'RGMGenericUnionValue<TStrType>.Implicit');
  end;
end;
{$endif}

class operator RGMGenericUnionValue<TStrType>.Implicit(AValue: EGMUnionValueType): RGMGenericUnionValue<TStrType>;
begin
  Finalize(Result);
  case AValue of
    uvtUnassigned, uvtNull: Result.ValueType := AValue;
//  else raise XGMUnionValueConvertError.ObjError(GMFormat(srValueConvertFromErrFmt, [GMUnionValueTypeName(AValue)]), nil, 'RGMGenericUnionValue<TStrType>.Implicit');
    else RaiseExceptionFmt(GMFormat(srValueConvertFromErrFmt, [GMUnionValueTypeName(AValue)]), nil, 'RGMGenericUnionValue<TStrType>.Implicit');
  end;
end;

class operator RGMGenericUnionValue<TStrType>.Implicit(AValue: Boolean): RGMGenericUnionValue<TStrType>;
begin
  Finalize(Result);
  Result.ValueType := uvtBoolean;
  Result.BoolValue := AValue;
end;

class operator RGMGenericUnionValue<TStrType>.Implicit(AValue: SmallInt): RGMGenericUnionValue<TStrType>;
begin
  Finalize(Result);
  Result.Int16Value := AValue;
  Result.ValueType := uvtInt16;
end;

class operator RGMGenericUnionValue<TStrType>.Implicit(AValue: LongInt): RGMGenericUnionValue<TStrType>;
begin
  Finalize(Result);
  Result.ValueType := uvtInt32;
  Result.Int32Value := AValue;
end;

class operator RGMGenericUnionValue<TStrType>.Implicit(AValue: Int64): RGMGenericUnionValue<TStrType>;
begin
  Finalize(Result);
  Result.Int64Value := AValue;
  Result.ValueType := uvtInt64;
end;

class operator RGMGenericUnionValue<TStrType>.Implicit(AValue: Double): RGMGenericUnionValue<TStrType>;
begin
  Finalize(Result);
  Result.ValueType := uvtDouble;
  Result.DoubleValue := AValue;
end;

class operator RGMGenericUnionValue<TStrType>.Implicit(AValue: TDateTime): RGMGenericUnionValue<TStrType>;
begin
  Finalize(Result);
  Result.DateTimeValue := AValue;
  Result.ValueType := uvtDateTime;
end;

class operator RGMGenericUnionValue<TStrType>.Implicit(AValue: Pointer): RGMGenericUnionValue<TStrType>;
begin
  Finalize(Result);
  Result.ValueType := uvtPointer;
  Result.PointerValue := AValue;
end;

class operator RGMGenericUnionValue<TStrType>.Implicit(AValue: UnicodeString): RGMGenericUnionValue<TStrType>;
begin
  Finalize(Result);
  Result.InternalAssignString(AValue);
end;

class operator RGMGenericUnionValue<TStrType>.Implicit(AValue: WideString): RGMGenericUnionValue<TStrType>;
begin
  Finalize(Result);
  Result.InternalAssignString(AValue);
end;

class operator RGMGenericUnionValue<TStrType>.Implicit(AValue: AnsiString): RGMGenericUnionValue<TStrType>;
begin
  Finalize(Result);
  Result.InternalAssignString(AValue);
end;

class operator RGMGenericUnionValue<TStrType>.Implicit(AValue: Utf8String): RGMGenericUnionValue<TStrType>;
begin
  Finalize(Result);
  Result.InternalAssignString(AValue);
end;

{$define InsertAssignFromVariantCommonPart:=
  //Result := Default(RGMGenericUnionValue<TStrType>); // <- Result may be a re-used instance, assignmet will finalize it before assign
  //Finalize(Result);
  case VarType(AValue) of
   //varEmpty: Result := uvtUnassigned; <- handeled by else case
   varNull: Result := uvtNull;
   varBoolean: Result := Boolean(AValue); // AValue.vBoolean;
   varSmallint: Result := SmallInt(AValue); // AValue.vSmallint;
   varWord: Result := Word(AValue); // AValue.vWord;
   varInteger: Result := LongInt(AValue); // AValue.vInteger;
   varLongword: Result := Longword(AValue); // AValue.vLongword;
   varInt64: Result := Int64(AValue); // AValue.vInt64;
   varQWord: Result := QWord(AValue); // AValue.vQWord;
   varSingle: Result := Single(AValue); // AValue.vSingle;
   varDouble: Result := Double(AValue); // AValue.vDouble;
   varDate: Result := TDateTime(AValue); // AValue.vDate;
   varOleStr, varString: Result := GMVarToStr(AValue);
   {$ifdef WithIntfType}
    varUnknown, varDispatch: Result := IUnknown(AValue); // AValue.vUnknown;
   {$else}
    varUnknown, varDispatch: Result := Pointer(IUnknown(AValue)); // AValue.vUnknown;
   {$endif}
   varError: Result := AValue.vError;
   else Result := uvtUnassigned;
  end;}

class operator RGMGenericUnionValue<TStrType>.Implicit(AValue: Variant): RGMGenericUnionValue<TStrType>;
begin
  InsertAssignFromVariantCommonPart
end;

class operator RGMGenericUnionValue<TStrType>.Implicit(AValue: OleVariant): RGMGenericUnionValue<TStrType>;
begin
  InsertAssignFromVariantCommonPart
end;

{$ifdef WithIntfType}
class operator RGMGenericUnionValue<TStrType>.Implicit(AValue: IUnknown): RGMGenericUnionValue<TStrType>;
begin
  Finalize(Result);
  Result.ValueType := uvtInterface;
  IUnknown(Result.IntfValue) := AValue;
end;
{$endif}

class operator RGMGenericUnionValue<TStrType>.Equal(const AUnionValue: RGMGenericUnionValue<TStrType>; AValue: Int64): Boolean;
begin
  Result := AUnionValue.AsInt64 = AValue;
end;

class operator RGMGenericUnionValue<TStrType>.Equal(const AUnionValue: RGMGenericUnionValue<TStrType>; AValue: LongInt): Boolean;
begin
  Result := AUnionValue.AsInt32 = AValue;
end;

class operator RGMGenericUnionValue<TStrType>.Equal(const AUnionValue: RGMGenericUnionValue<TStrType>; AValue: Pointer): Boolean;
begin
  Result := AUnionValue.AsPointer = AValue;
end;

{$ifdef WithIntfType}
class operator RGMGenericUnionValue<TStrType>.Equal(const AUnionValue: RGMGenericUnionValue<TStrType>; AValue: IUnknown): Boolean;
begin
  Result := AUnionValue.AsInterface = AValue;
end;
{$endif}

class operator RGMGenericUnionValue<TStrType>.Equal(const AUnionValue: RGMGenericUnionValue<TStrType>; AValue: Double): Boolean;
begin
  Result := AUnionValue.AsDouble = AValue;
end;

//class operator RGMGenericUnionValue<TStrType>.Equal(const AUnionValue: RGMGenericUnionValue<TStrType>; AValue: TDateTime): Boolean;
//begin
//  Result := AUnionValue.AsDateTime = AValue;
//end;

class operator RGMGenericUnionValue<TStrType>.Equal(const AUnionValue: RGMGenericUnionValue<TStrType>; AValue: UnicodeString): Boolean;
begin
  Result := AUnionValue.AsString = AValue;
end;

class operator RGMGenericUnionValue<TStrType>.Equal(const AUnionValue: RGMGenericUnionValue<TStrType>; AValue: WideString): Boolean;
begin
  Result := AUnionValue.AsString = AValue;
end;

class operator RGMGenericUnionValue<TStrType>.Equal(const AUnionValue: RGMGenericUnionValue<TStrType>; AValue: AnsiString): Boolean;
begin
  Result := AUnionValue.AsString = AValue;
end;

class operator RGMGenericUnionValue<TStrType>.Equal(const AUnionValue: RGMGenericUnionValue<TStrType>; AValue: Utf8String): Boolean;
begin
  Result := AUnionValue.AsString = AValue;
end;

class operator RGMGenericUnionValue<TStrType>.Equal(const AValue1, AValue2: RGMGenericUnionValue<TStrType>): Boolean;
begin
  case AValue1.ValueType of
   uvtUnassigned: Result := AValue2.ValueType = uvtUnassigned;
   uvtNull: Result := AValue2.ValueType = uvtNull;
   uvtString:
    case AValue2.ValueType of
     uvtString: Result := GMSameText(AValue1.AsString, AValue2.AsString);
     uvtBoolean, uvtInt16, uvtInt32, uvtInt64, uvtDouble, uvtDateTime: Result := GMSameText(AValue1.AsString, AValue2.AsString);
     else Result := False;
    end;

   uvtBoolean:
    case AValue2.ValueType of
     uvtBoolean: Result := AValue1.BoolValue = AValue2.BoolValue;
     uvtInt16, uvtInt32, uvtInt64: Result := AValue1.BoolValue = (AValue2.AsInt64 <> 0);
     uvtdouble: Result := AValue1.BoolValue = (AVAlue2.DoubleValue <> 0);
     uvtString: Result := AValue1.BoolValue = GMStrToBool(StrTypeCasted(AValue2.StringValue));
     else Result := False;
    end;

   uvtInt16, uvtInt32, uvtInt64:
    case AValue2.ValueType of
     //uvtString: Result := AValue1.AsInt64 =
     //uvtBoolean,
     //uvtString: Result
     uvtInt16, uvtInt32, uvtInt64: Result := AValue1.AsInt64 = AValue2.AsInt64;
     uvtDouble, uvtDateTime: Result := AValue1.AsInt64 = AValue2.DoubleValue;
     else Result := False;
    end;

   uvtDouble, uvtDateTime:
    case AValue2.ValueType of
     //uvtString, uvtBoolean,
     //uvtString,
     uvtInt16, uvtInt32, uvtInt64: Result := AValue1.DoubleValue = AValue2.AsInt64;
     uvtDouble, uvtDateTime: Result := AValue1.DoubleValue = AValue2.DoubleValue;
     else Result := False;
    end;

   uvtPointer:
    case AValue2.ValueType of
     {$IFDEF CPU32}
      uvtInt32: Result := PtrUInt(AValue1.PointerValue) = PtrUInt(AValue2.AsInt32);
     {$ENDIF}
     {$IFDEF CPU64}
      uvtInt64: Result := PtrUInt(AValue1.PointerValue) = PtrUInt(AValue2.AsInt64);
     {$ENDIF}

     uvtPointer: Result := AValue1.PointerValue = AValue2.PointerValue;
     else Result := False;
    end;

   {$ifdef WithIntfType}
   uvtInterface:
    case AValue2.ValueType of
     uvtPointer: Result := AValue1.IntfValue = AValue2.PointerValue;
     uvtInterface: Result := AValue1.IntfValue = AValue2.IntfValue;
     else Result := False;
    end
   {$endif}

   else Result := False;
  end;
end;

class operator RGMGenericUnionValue<TStrType>.NotEqual(const AValue1, AValue2: RGMGenericUnionValue<TStrType>): Boolean;
begin
  Result := not (AValue1 = AValue2);
end;

class operator RGMGenericUnionValue<TStrType>.Add(const AValue1, AValue2: RGMGenericUnionValue<TStrType>): RGMGenericUnionValue<TStrType>;
begin
  case AValue1.ValueType of
   uvtInt16, uvtInt32, uvtInt64:
    case AValue2.ValueType of
     uvtInt16, uvtInt32, uvtInt64: Result := AValue1.AsInt64 + AValue2.AsInt64;
     else Result := AValue1.AsDouble + AValue2.AsDouble;
    end;
   else Result := AValue1.AsDouble + AValue2.AsDouble;
  end;
end;

class operator RGMGenericUnionValue<TStrType>.Multiply(const AValue1, AValue2: RGMGenericUnionValue<TStrType>): RGMGenericUnionValue<TStrType>;
begin
  case AValue1.ValueType of
   uvtInt16, uvtInt32, uvtInt64:
    case AValue2.ValueType of
     uvtInt16, uvtInt32, uvtInt64: Result := AValue1.AsInt64 * AValue2.AsInt64;
     else Result := AValue1.AsDouble * AValue2.AsDouble;
    end;
   else Result := AValue1.AsDouble * AValue2.AsDouble;
  end;
end;

class operator RGMGenericUnionValue<TStrType>.Divide(const AValue1, AValue2: RGMGenericUnionValue<TStrType>): RGMGenericUnionValue<TStrType>;
begin
  case AValue1.ValueType of
   uvtInt16, uvtInt32, uvtInt64:
    case AValue2.ValueType of
     uvtInt16, uvtInt32, uvtInt64: Result := AValue1.AsInt64 / AValue2.AsInt64;
     else Result := AValue1.AsDouble / AValue2.AsDouble;
    end;
   else Result := AValue1.AsDouble / AValue2.AsDouble;
  end;
end;

class operator RGMGenericUnionValue<TStrType>.IntDivide(const AValue1, AValue2: RGMGenericUnionValue<TStrType>): RGMGenericUnionValue<TStrType>;
begin
  Result := AValue1.AsInt64 div AValue2.AsInt64;
end;

class operator RGMGenericUnionValue<TStrType>.Modulus(const AValue1, AValue2: RGMGenericUnionValue<TStrType>): RGMGenericUnionValue<TStrType>;
begin
  Result := AValue1.AsInt64 mod AValue2.AsInt64;
end;

class operator RGMGenericUnionValue<TStrType>.LessThan(const AValue1, AValue2: RGMGenericUnionValue<TStrType> ): Boolean;
begin
  case AValue1.ValueType of
   uvtUnassigned, uvtNull: Result := AValue2.ValueType > uvtNull;
   uvtString:
    case AValue2.ValueType of
     uvtString: Result := GMCompareNames(StrTypeCasted(AValue1.StringValue), StrTypeCasted(AValue2.StringValue)) = crALessThanB;
     uvtBoolean, uvtInt16, uvtInt32, uvtInt64, uvtDouble, uvtDateTime: Result := GMCompareNames(StrTypeCasted((AValue1.StringValue)), AValue2.AsString) = crALessThanB;
     else Result := False;
    end;

   uvtBoolean:
    case AValue2.ValueType of
     uvtBoolean: Result := AValue1.BoolValue < AValue2.BoolValue;
     uvtInt16, uvtInt32, uvtInt64: Result := AValue1.BoolValue < (AValue2.AsInt64 <> 0);
     uvtdouble: Result := AValue1.BoolValue < (AVAlue2.DoubleValue <> 0);
     uvtString: Result := AValue1.BoolValue < GMStrToBool(StrTypeCasted(AValue2.StringValue));
     else Result := False;
    end;

   uvtInt16, uvtInt32, uvtInt64:
    case AValue2.ValueType of
     //uvtString: Result := AValue1.AsInt64 =
     //uvtBoolean,
     //uvtString: Result
     uvtInt16, uvtInt32, uvtInt64: Result := AValue1.AsInt64 < AValue2.AsInt64;
     uvtDouble, uvtDateTime: Result := AValue1.AsInt64 < AValue2.DoubleValue;
     else Result := False;
    end;

   uvtDouble, uvtDateTime:
    case AValue2.ValueType of
     //uvtString, uvtBoolean,
     //uvtString,
     uvtInt16, uvtInt32, uvtInt64: Result := AValue1.DoubleValue < AValue2.AsInt64;
     uvtDouble, uvtDateTime: Result := AValue1.DoubleValue < AValue2.DoubleValue;
     else Result := False;
    end;

   uvtPointer:
    case AValue2.ValueType of
     {$IFDEF CPU32}
      uvtInt32: Result := PtrUInt(AValue1.PointerValue) < PtrUInt(AValue2.AsInt32);
     {$ENDIF}
     {$IFDEF CPU64}
      uvtInt64: Result := PtrUInt(AValue1.PointerValue) < PtrUInt(AValue2.AsInt64);
     {$ENDIF}

     uvtPointer: Result := AValue1.PointerValue < AValue2.PointerValue;
     else Result := False;
    end;

   {$ifdef WithIntfType}
   uvtInterface:
    case AValue2.ValueType of
     uvtPointer: Result := AValue1.IntfValue < AValue2.PointerValue;
     uvtInterface: Result := AValue1.IntfValue < AValue2.IntfValue;
     else Result := False;
    end
   {$endif}

   else Result := False;
  end;
end;

class operator RGMGenericUnionValue<TStrType>.GreaterThan(const AValue1, AValue2: RGMGenericUnionValue<TStrType>): Boolean;
begin
  Result := not ((AValue1 < AValue2) or (AValue1 = AValue2));
end;

class operator RGMGenericUnionValue<TStrType>.LessThanOrEqual(const AValue1, AValue2: RGMGenericUnionValue<TStrType>): Boolean;
begin
  Result := (AValue1 < AValue2) or (AValue1 = AValue2);
end;

class operator RGMGenericUnionValue<TStrType>.GreaterThanOrEqual(const AValue1, AValue2: RGMGenericUnionValue<TStrType>): Boolean;
begin
  Result := not (AValue1 < AValue2); // (AValue1 > AValue2) or (AValue1 = AValue2);
end;

function RGMGenericUnionValue<TStrType>.AsBoolean: Boolean;
begin
  Result := Self;
end;

function RGMGenericUnionValue<TStrType>.AsInt16: SmallInt;
begin
  Result := Self;
end;

function RGMGenericUnionValue<TStrType>.AsInt32: LongInt;
begin
  Result := Self;
end;

function RGMGenericUnionValue<TStrType>.AsInt64: Int64;
begin
  Result := Self;
end;

function RGMGenericUnionValue<TStrType>.AsDouble: Double;
begin
  Result := Self;
end;

function RGMGenericUnionValue<TStrType>.AsDateTime: TDateTime;
begin
  Result := Self;
end;

function RGMGenericUnionValue<TStrType>.AsString: TGMString;
begin
  Result := Self;
end;

function RGMGenericUnionValue<TStrType>.AsPointer: Pointer;
begin
  Result := Self;
end;

{$ifdef WithIntfType}
function RGMGenericUnionValue<TStrType>.AsInterface: IUnknown;
begin
  Result := Self;
end;
{$endif}

//function RGMGenericUnionValue<TStrType>.AsDisplayString: TGMString;
//begin
//  case ValueType of
//   uvtUnassigned, uvtNull: Result := '';
//   else Result := Self;
//  end;
//end;

{$define CommonAsDfltresult:=
  case ValueType of
   uvtUnassigned, uvtNull: Result := ADefaultValue;
   else Result := Self;
  end;}

function RGMGenericUnionValue<TStrType>.AsBooleanDflt(const ADefaultValue: Boolean): Boolean;
begin
  CommonAsDfltresult
end;

function RGMGenericUnionValue<TStrType>.AsInt16Dflt(const ADefaultValue: SmallInt): SmallInt;
begin
  CommonAsDfltresult
end;

function RGMGenericUnionValue<TStrType>.AsInt32Dflt(const ADefaultValue: LongInt): LongInt;
begin
  CommonAsDfltresult
end;

function RGMGenericUnionValue<TStrType>.AsInt64Dflt(const ADefaultValue: Int64): Int64;
begin
  CommonAsDfltresult
end;

function RGMGenericUnionValue<TStrType>.AsDoubleDflt(const ADefaultValue: Double): Double;
begin
  CommonAsDfltresult
end;

function RGMGenericUnionValue<TStrType>.AsDateTimeDflt(const ADefaultValue: TDateTime): TDateTime;
begin
  CommonAsDfltresult
end;

function RGMGenericUnionValue<TStrType>.AsPointerDflt(const ADefaultValue: Pointer): Pointer;
begin
  CommonAsDfltresult
end;

function RGMGenericUnionValue<TStrType>.AsStringDflt(const ADefaultValue: TGMString): TGMString;
begin
  CommonAsDfltresult
end;

{$ifdef WithIntfType}
function RGMGenericUnionValue<TStrType>.AsInterfaceDflt(const ADefaultValue: IUnknown): IUnknown;
begin
  CommonAsDfltresult
end;
{$endif}


end.