unit Myc.Data.Types.Tuple; interface uses System.SysUtils, Myc.Data.Types; type // Implements the singleton tuple data type. TDataTupleType = class(TInterfacedObject, IDataType, IDataTupleType) strict private class var FSingleton: IDataTupleType; class constructor CreateClass; private function GetName: String; function GetKind: TDataKind; public function CreateValue(const AItems: array of IDataValue): IDataTupleValue; class property Singleton: IDataTupleType read FSingleton; end; implementation uses System.Rtti; type // Implements the simple tuple data value. TDataTupleValue = class(TInterfacedObject, IDataValue, IDataTupleValue) private FItems: TArray; function GetDataType: IDataType; function GetAsString: string; function AsTValue: TValue; function GetItemCount: Integer; function GetItem(Idx: Integer): IDataValue; public constructor Create(const AItems: array of IDataValue); end; // Optimized singleton implementation for a tuple with 0 elements. TDataTupleValue0 = class(TInterfacedObject, IDataValue, IDataTupleValue) strict private class var FSingleton: IDataTupleValue; FEmptyTValue: TValue; class constructor CreateClass; private function GetDataType: IDataType; function GetAsString: string; function AsTValue: TValue; function GetItemCount: Integer; function GetItem(Idx: Integer): IDataValue; public class property Singleton: IDataTupleValue read FSingleton; end; // Optimized implementation for a tuple with 1 element. TDataTupleValue1 = class(TInterfacedObject, IDataValue, IDataTupleValue) private FItem0: IDataValue; function GetDataType: IDataType; function GetAsString: string; function AsTValue: TValue; function GetItemCount: Integer; function GetItem(Idx: Integer): IDataValue; public constructor Create(const AItems: array of IDataValue); end; // Optimized implementation for a tuple with 2 elements. TDataTupleValue2 = class(TInterfacedObject, IDataValue, IDataTupleValue) private FItem0, FItem1: IDataValue; function GetDataType: IDataType; function GetAsString: string; function AsTValue: TValue; function GetItemCount: Integer; function GetItem(Idx: Integer): IDataValue; public constructor Create(const AItems: array of IDataValue); end; // Optimized implementation for a tuple with 3 elements. TDataTupleValue3 = class(TInterfacedObject, IDataValue, IDataTupleValue) private FItem0, FItem1, FItem2: IDataValue; function GetDataType: IDataType; function GetAsString: string; function AsTValue: TValue; function GetItemCount: Integer; function GetItem(Idx: Integer): IDataValue; public constructor Create(const AItems: array of IDataValue); end; // Optimized implementation for a tuple with 4 elements. TDataTupleValue4 = class(TInterfacedObject, IDataValue, IDataTupleValue) private FItem0, FItem1, FItem2, FItem3: IDataValue; function GetDataType: IDataType; function GetAsString: string; function AsTValue: TValue; function GetItemCount: Integer; function GetItem(Idx: Integer): IDataValue; public constructor Create(const AItems: array of IDataValue); end; // Optimized implementation for a tuple with 5 elements. TDataTupleValue5 = class(TInterfacedObject, IDataValue, IDataTupleValue) private FItem0, FItem1, FItem2, FItem3, FItem4: IDataValue; function GetDataType: IDataType; function GetAsString: string; function AsTValue: TValue; function GetItemCount: Integer; function GetItem(Idx: Integer): IDataValue; public constructor Create(const AItems: array of IDataValue); end; { TDataTupleValue (generic implementation for N > 5 elements) } constructor TDataTupleValue.Create(const AItems: array of IDataValue); var i: Integer; begin inherited Create; SetLength(FItems, Length(AItems)); for i := 0 to High(AItems) do FItems[i] := AItems[i]; end; function TDataTupleValue.GetDataType: IDataType; begin Result := TDataTupleType.Singleton; end; function TDataTupleValue.GetAsString: string; var sb: TStringBuilder; i: Integer; begin sb := TStringBuilder.Create; try sb.Append('('); for i := 0 to High(FItems) do begin sb.Append(FItems[i].AsString); if (i < High(FItems)) then sb.Append(', '); end; sb.Append(')'); Result := sb.ToString; finally sb.Free; end; end; function TDataTupleValue.AsTValue: TValue; var tvalueArray: TArray; i: Integer; begin SetLength(tvalueArray, Length(FItems)); for i := 0 to High(FItems) do tvalueArray[i] := FItems[i].AsTValue; Result := TValue.From>(tvalueArray); end; function TDataTupleValue.GetItem(Idx: Integer): IDataValue; begin Result := FItems[Idx]; end; function TDataTupleValue.GetItemCount: Integer; begin Result := Length(FItems); end; { TDataTupleValue0 } class constructor TDataTupleValue0.CreateClass; var emptyArray: TArray; begin FSingleton := TDataTupleValue0.Create; SetLength(emptyArray, 0); FEmptyTValue := TValue.From>(emptyArray); end; function TDataTupleValue0.GetAsString: string; begin Result := '()'; end; function TDataTupleValue0.AsTValue: TValue; begin Result := FEmptyTValue; end; function TDataTupleValue0.GetDataType: IDataType; begin Result := TDataTupleType.Singleton; end; function TDataTupleValue0.GetItem(Idx: Integer): IDataValue; begin raise EArgumentException.Create('Index out of bounds'); end; function TDataTupleValue0.GetItemCount: Integer; begin Result := 0; end; { TDataTupleValue1 } constructor TDataTupleValue1.Create(const AItems: array of IDataValue); begin inherited Create; FItem0 := AItems[0]; end; function TDataTupleValue1.GetAsString: string; begin Result := '(' + FItem0.AsString + ')'; end; function TDataTupleValue1.AsTValue: TValue; var tvalueArray: TArray; begin SetLength(tvalueArray, 1); tvalueArray[0] := FItem0.AsTValue; Result := TValue.From>(tvalueArray); end; function TDataTupleValue1.GetDataType: IDataType; begin Result := TDataTupleType.Singleton; end; function TDataTupleValue1.GetItem(Idx: Integer): IDataValue; begin case Idx of 0: Result := FItem0; else raise EArgumentException.Create('Index out of bounds'); end; end; function TDataTupleValue1.GetItemCount: Integer; begin Result := 1; end; { TDataTupleValue2 } constructor TDataTupleValue2.Create(const AItems: array of IDataValue); begin inherited Create; FItem0 := AItems[0]; FItem1 := AItems[1]; end; function TDataTupleValue2.GetAsString: string; begin Result := '(' + FItem0.AsString + ', ' + FItem1.AsString + ')'; end; function TDataTupleValue2.AsTValue: TValue; var tvalueArray: TArray; begin SetLength(tvalueArray, 2); tvalueArray[0] := FItem0.AsTValue; tvalueArray[1] := FItem1.AsTValue; Result := TValue.From>(tvalueArray); end; function TDataTupleValue2.GetDataType: IDataType; begin Result := TDataTupleType.Singleton; end; function TDataTupleValue2.GetItem(Idx: Integer): IDataValue; begin case Idx of 0: Result := FItem0; 1: Result := FItem1; else raise EArgumentException.Create('Index out of bounds'); end; end; function TDataTupleValue2.GetItemCount: Integer; begin Result := 2; end; { TDataTupleValue3 } constructor TDataTupleValue3.Create(const AItems: array of IDataValue); begin inherited Create; FItem0 := AItems[0]; FItem1 := AItems[1]; FItem2 := AItems[2]; end; function TDataTupleValue3.GetAsString: string; var sb: TStringBuilder; begin sb := TStringBuilder.Create; try sb.Append('('); sb.Append(FItem0.AsString); sb.Append(', '); sb.Append(FItem1.AsString); sb.Append(', '); sb.Append(FItem2.AsString); sb.Append(')'); Result := sb.ToString; finally sb.Free; end; end; function TDataTupleValue3.AsTValue: TValue; var tvalueArray: TArray; begin SetLength(tvalueArray, 3); tvalueArray[0] := FItem0.AsTValue; tvalueArray[1] := FItem1.AsTValue; tvalueArray[2] := FItem2.AsTValue; Result := TValue.From>(tvalueArray); end; function TDataTupleValue3.GetDataType: IDataType; begin Result := TDataTupleType.Singleton; end; function TDataTupleValue3.GetItem(Idx: Integer): IDataValue; begin case Idx of 0: Result := FItem0; 1: Result := FItem1; 2: Result := FItem2; else raise EArgumentException.Create('Index out of bounds'); end; end; function TDataTupleValue3.GetItemCount: Integer; begin Result := 3; end; { TDataTupleValue4 } constructor TDataTupleValue4.Create(const AItems: array of IDataValue); begin inherited Create; FItem0 := AItems[0]; FItem1 := AItems[1]; FItem2 := AItems[2]; FItem3 := AItems[3]; end; function TDataTupleValue4.GetAsString: string; var sb: TStringBuilder; begin sb := TStringBuilder.Create; try sb.Append('('); sb.Append(FItem0.AsString); sb.Append(', '); sb.Append(FItem1.AsString); sb.Append(', '); sb.Append(FItem2.AsString); sb.Append(', '); sb.Append(FItem3.AsString); sb.Append(')'); Result := sb.ToString; finally sb.Free; end; end; function TDataTupleValue4.AsTValue: TValue; var tvalueArray: TArray; begin SetLength(tvalueArray, 4); tvalueArray[0] := FItem0.AsTValue; tvalueArray[1] := FItem1.AsTValue; tvalueArray[2] := FItem2.AsTValue; tvalueArray[3] := FItem3.AsTValue; Result := TValue.From>(tvalueArray); end; function TDataTupleValue4.GetDataType: IDataType; begin Result := TDataTupleType.Singleton; end; function TDataTupleValue4.GetItem(Idx: Integer): IDataValue; begin case Idx of 0: Result := FItem0; 1: Result := FItem1; 2: Result := FItem2; 3: Result := FItem3; else raise EArgumentException.Create('Index out of bounds'); end; end; function TDataTupleValue4.GetItemCount: Integer; begin Result := 4; end; { TDataTupleValue5 } constructor TDataTupleValue5.Create(const AItems: array of IDataValue); begin inherited Create; FItem0 := AItems[0]; FItem1 := AItems[1]; FItem2 := AItems[2]; FItem3 := AItems[3]; FItem4 := AItems[4]; end; function TDataTupleValue5.GetAsString: string; var sb: TStringBuilder; begin sb := TStringBuilder.Create; try sb.Append('('); sb.Append(FItem0.AsString); sb.Append(', '); sb.Append(FItem1.AsString); sb.Append(', '); sb.Append(FItem2.AsString); sb.Append(', '); sb.Append(FItem3.AsString); sb.Append(', '); sb.Append(FItem4.AsString); sb.Append(')'); Result := sb.ToString; finally sb.Free; end; end; function TDataTupleValue5.AsTValue: TValue; var tvalueArray: TArray; begin SetLength(tvalueArray, 5); tvalueArray[0] := FItem0.AsTValue; tvalueArray[1] := FItem1.AsTValue; tvalueArray[2] := FItem2.AsTValue; tvalueArray[3] := FItem3.AsTValue; tvalueArray[4] := FItem4.AsTValue; Result := TValue.From>(tvalueArray); end; function TDataTupleValue5.GetDataType: IDataType; begin Result := TDataTupleType.Singleton; end; function TDataTupleValue5.GetItem(Idx: Integer): IDataValue; begin case Idx of 0: Result := FItem0; 1: Result := FItem1; 2: Result := FItem2; 3: Result := FItem3; 4: Result := FItem4; else raise EArgumentException.Create('Index out of bounds'); end; end; function TDataTupleValue5.GetItemCount: Integer; begin Result := 5; end; { TDataTupleType } class constructor TDataTupleType.CreateClass; begin FSingleton := TDataTupleType.Create; end; function TDataTupleType.CreateValue(const AItems: array of IDataValue): IDataTupleValue; begin // Use optimized implementations for tuples with 0 to 5 elements. case Length(AItems) of 0: Result := TDataTupleValue0.Singleton; 1: Result := TDataTupleValue1.Create(AItems); 2: Result := TDataTupleValue2.Create(AItems); 3: Result := TDataTupleValue3.Create(AItems); 4: Result := TDataTupleValue4.Create(AItems); 5: Result := TDataTupleValue5.Create(AItems); else // Use the generic implementation for tuples with more than 5 elements. Result := TDataTupleValue.Create(AItems); end; end; function TDataTupleType.GetName: String; begin Result := 'Tuple'; end; function TDataTupleType.GetKind: TDataKind; begin Result := dkTuple; end; end.