unit Myc.Data.Types.Tuple; interface uses System.SysUtils, Myc.Data.Types; type // Implements the singleton tuple data type. TImplDataTupleType = 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. TImplDataTupleValue = 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. TImplDataTupleValue0 = 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. TImplDataTupleValue1 = 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. TImplDataTupleValue2 = 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. TImplDataTupleValue3 = 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. TImplDataTupleValue4 = 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. TImplDataTupleValue5 = 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; { TImplDataTupleValue (generic implementation for N > 5 elements) } constructor TImplDataTupleValue.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 TImplDataTupleValue.GetDataType: IDataType; begin Result := TImplDataTupleType.Singleton; end; function TImplDataTupleValue.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 TImplDataTupleValue.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 TImplDataTupleValue.GetItem(Idx: Integer): IDataValue; begin Result := FItems[Idx]; end; function TImplDataTupleValue.GetItemCount: Integer; begin Result := Length(FItems); end; { TImplDataTupleValue0 } class constructor TImplDataTupleValue0.CreateClass; var emptyArray: TArray; begin FSingleton := TImplDataTupleValue0.Create; SetLength(emptyArray, 0); FEmptyTValue := TValue.From>(emptyArray); end; function TImplDataTupleValue0.GetAsString: string; begin Result := '()'; end; function TImplDataTupleValue0.AsTValue: TValue; begin Result := FEmptyTValue; end; function TImplDataTupleValue0.GetDataType: IDataType; begin Result := TImplDataTupleType.Singleton; end; function TImplDataTupleValue0.GetItem(Idx: Integer): IDataValue; begin raise EArgumentException.Create('Index out of bounds'); end; function TImplDataTupleValue0.GetItemCount: Integer; begin Result := 0; end; { TImplDataTupleValue1 } constructor TImplDataTupleValue1.Create(const AItems: array of IDataValue); begin inherited Create; FItem0 := AItems[0]; end; function TImplDataTupleValue1.GetAsString: string; begin Result := '(' + FItem0.AsString + ')'; end; function TImplDataTupleValue1.AsTValue: TValue; var tvalueArray: TArray; begin SetLength(tvalueArray, 1); tvalueArray[0] := FItem0.AsTValue; Result := TValue.From>(tvalueArray); end; function TImplDataTupleValue1.GetDataType: IDataType; begin Result := TImplDataTupleType.Singleton; end; function TImplDataTupleValue1.GetItem(Idx: Integer): IDataValue; begin case Idx of 0: Result := FItem0; else raise EArgumentException.Create('Index out of bounds'); end; end; function TImplDataTupleValue1.GetItemCount: Integer; begin Result := 1; end; { TImplDataTupleValue2 } constructor TImplDataTupleValue2.Create(const AItems: array of IDataValue); begin inherited Create; FItem0 := AItems[0]; FItem1 := AItems[1]; end; function TImplDataTupleValue2.GetAsString: string; begin Result := '(' + FItem0.AsString + ', ' + FItem1.AsString + ')'; end; function TImplDataTupleValue2.AsTValue: TValue; var tvalueArray: TArray; begin SetLength(tvalueArray, 2); tvalueArray[0] := FItem0.AsTValue; tvalueArray[1] := FItem1.AsTValue; Result := TValue.From>(tvalueArray); end; function TImplDataTupleValue2.GetDataType: IDataType; begin Result := TImplDataTupleType.Singleton; end; function TImplDataTupleValue2.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 TImplDataTupleValue2.GetItemCount: Integer; begin Result := 2; end; { TImplDataTupleValue3 } constructor TImplDataTupleValue3.Create(const AItems: array of IDataValue); begin inherited Create; FItem0 := AItems[0]; FItem1 := AItems[1]; FItem2 := AItems[2]; end; function TImplDataTupleValue3.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 TImplDataTupleValue3.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 TImplDataTupleValue3.GetDataType: IDataType; begin Result := TImplDataTupleType.Singleton; end; function TImplDataTupleValue3.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 TImplDataTupleValue3.GetItemCount: Integer; begin Result := 3; end; { TImplDataTupleValue4 } constructor TImplDataTupleValue4.Create(const AItems: array of IDataValue); begin inherited Create; FItem0 := AItems[0]; FItem1 := AItems[1]; FItem2 := AItems[2]; FItem3 := AItems[3]; end; function TImplDataTupleValue4.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 TImplDataTupleValue4.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 TImplDataTupleValue4.GetDataType: IDataType; begin Result := TImplDataTupleType.Singleton; end; function TImplDataTupleValue4.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 TImplDataTupleValue4.GetItemCount: Integer; begin Result := 4; end; { TImplDataTupleValue5 } constructor TImplDataTupleValue5.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 TImplDataTupleValue5.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 TImplDataTupleValue5.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 TImplDataTupleValue5.GetDataType: IDataType; begin Result := TImplDataTupleType.Singleton; end; function TImplDataTupleValue5.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 TImplDataTupleValue5.GetItemCount: Integer; begin Result := 5; end; { TImplDataTupleType } class constructor TImplDataTupleType.CreateClass; begin FSingleton := TImplDataTupleType.Create; end; function TImplDataTupleType.CreateValue(const AItems: array of IDataValue): IDataTupleValue; begin // Use optimized implementations for tuples with 0 to 5 elements. case Length(AItems) of 0: Result := TImplDataTupleValue0.Singleton; 1: Result := TImplDataTupleValue1.Create(AItems); 2: Result := TImplDataTupleValue2.Create(AItems); 3: Result := TImplDataTupleValue3.Create(AItems); 4: Result := TImplDataTupleValue4.Create(AItems); 5: Result := TImplDataTupleValue5.Create(AItems); else // Use the generic implementation for tuples with more than 5 elements. Result := TImplDataTupleValue.Create(AItems); end; end; function TImplDataTupleType.GetName: String; begin Result := 'Tuple'; end; function TImplDataTupleType.GetKind: TDataKind; begin Result := dkTuple; end; end.