This is an automated email from the ASF dual-hosted git repository. jensg pushed a commit to branch master in repository https://gitbox.apache.org/repos/asf/thrift.git
commit 8f7487e1086d8da6baff3376679436e526dd8fd0 Author: Jens Geyer <[email protected]> AuthorDate: Thu May 9 22:21:32 2019 +0200 THRIFT-4862 better ToString() support for enums and container types Client: Delphi Patch: Jens Geyer This closes #1795 --- .../cpp/src/thrift/generate/t_delphi_generator.cc | 8 +- lib/delphi/src/Thrift.Collections.pas | 87 ++++++++++++-- lib/delphi/src/Thrift.Protocol.pas | 25 +--- lib/delphi/src/Thrift.Utils.pas | 75 +++++++++++- lib/delphi/test/TestServer.pas | 129 ++++++--------------- 5 files changed, 194 insertions(+), 130 deletions(-) diff --git a/compiler/cpp/src/thrift/generate/t_delphi_generator.cc b/compiler/cpp/src/thrift/generate/t_delphi_generator.cc index 505120e..11501bf 100644 --- a/compiler/cpp/src/thrift/generate/t_delphi_generator.cc +++ b/compiler/cpp/src/thrift/generate/t_delphi_generator.cc @@ -1743,7 +1743,7 @@ void t_delphi_generator::generate_delphi_struct_definition(ostream& out, if (is_exception && (!is_x_factory)) { out << "TException"; } else { - out << "TInterfacedObject, IBase, " << struct_intf_name; + out << "TInterfacedObject, IBase, ISupportsToString, " << struct_intf_name; } out << ")" << endl; @@ -3949,8 +3949,10 @@ void t_delphi_generator::generate_delphi_struct_tostring_impl(ostream& out, << ".Append('<null>') else " << tmp_sb << ".Append( Self." << prop_name((*f_iter), is_exception) << ".ToString());" << endl; } else if (ttype->is_enum()) { - indent_impl(out) << tmp_sb << ".Append(System.Integer( Self." << prop_name((*f_iter), is_exception) - << "));" << endl; + indent_impl(out) << tmp_sb << ".Append(EnumUtils<" + << type_name(ttype, false, true, is_exception, true) + << ">.ToString( System.Ord( Self." + << prop_name((*f_iter), is_exception) << ")));" << endl; } else { indent_impl(out) << tmp_sb << ".Append( Self." << prop_name((*f_iter), is_exception) << ");" << endl; diff --git a/lib/delphi/src/Thrift.Collections.pas b/lib/delphi/src/Thrift.Collections.pas index b2206cb..2e13724 100644 --- a/lib/delphi/src/Thrift.Collections.pas +++ b/lib/delphi/src/Thrift.Collections.pas @@ -22,7 +22,7 @@ unit Thrift.Collections; interface uses - Generics.Collections, Generics.Defaults, Thrift.Utils; + SysUtils, Generics.Collections, Generics.Defaults, Thrift.Utils; type @@ -30,11 +30,11 @@ type TArray<T> = array of T; {$IFEND} - IThriftContainer = interface - ['{93DEF5A0-D162-461A-AB22-5B4EE0734050}'] - function ToString: string; + IThriftContainer = interface( ISupportsToString) + ['{E05C0F9D-A4F5-491D-AADA-C926B4BDB6E4}'] end; + IThriftDictionary<TKey,TValue> = interface(IThriftContainer) ['{25EDD506-F9D1-4008-A40F-5940364B7E46}'] function GetEnumerator: TEnumerator<TPair<TKey,TValue>>; @@ -64,7 +64,7 @@ type property Values: TDictionary<TKey,TValue>.TValueCollection read GetValues; end; - TThriftDictionaryImpl<TKey,TValue> = class( TInterfacedObject, IThriftDictionary<TKey,TValue>) + TThriftDictionaryImpl<TKey,TValue> = class( TInterfacedObject, IThriftDictionary<TKey,TValue>, IThriftContainer, ISupportsToString) private FDictionaly : TDictionary<TKey,TValue>; protected @@ -95,6 +95,7 @@ type public constructor Create(ACapacity: Integer = 0); destructor Destroy; override; + function ToString : string; override; end; IThriftList<T> = interface(IThriftContainer) @@ -140,7 +141,7 @@ type property Items[Index: Integer]: T read GetItem write SetItem; default; end; - TThriftListImpl<T> = class( TInterfacedObject, IThriftList<T>) + TThriftListImpl<T> = class( TInterfacedObject, IThriftList<T>, IThriftContainer, ISupportsToString) private FList : TList<T>; protected @@ -186,6 +187,7 @@ type public constructor Create; destructor Destroy; override; + function ToString : string; override; end; IHashSet<TValue> = interface(IThriftContainer) @@ -202,7 +204,7 @@ type function Remove( const item: TValue ): Boolean; end; - THashSetImpl<TValue> = class( TInterfacedObject, IHashSet<TValue>) + THashSetImpl<TValue> = class( TInterfacedObject, IHashSet<TValue>, IThriftContainer, ISupportsToString) private FDictionary : IThriftDictionary<TValue,Integer>; FIsReadOnly: Boolean; @@ -219,6 +221,7 @@ type function Remove( const item: TValue ): Boolean; public constructor Create; + function ToString : string; override; end; implementation @@ -287,6 +290,28 @@ begin end; end; +function THashSetImpl<TValue>.ToString : string; +var elm : TValue; + sb : TThriftStringBuilder; + first : Boolean; +begin + sb := TThriftStringBuilder.Create('{'); + try + first := TRUE; + for elm in FDictionary.Keys do begin + if first + then first := FALSE + else sb.Append(', '); + + sb.Append( StringUtils<TValue>.ToString(elm)); + end; + sb.Append('}'); + Result := sb.ToString; + finally + sb.Free; + end; +end; + { TThriftDictionaryImpl<TKey, TValue> } procedure TThriftDictionaryImpl<TKey, TValue>.Add(const Key: TKey; @@ -393,6 +418,32 @@ begin {$IFEND} end; +function TThriftDictionaryImpl<TKey, TValue>.ToString : string; +var pair : TPair<TKey, TValue>; + sb : TThriftStringBuilder; + first : Boolean; +begin + sb := TThriftStringBuilder.Create('{'); + try + first := TRUE; + for pair in FDictionaly do begin + if first + then first := FALSE + else sb.Append(', '); + + sb.Append( '('); + sb.Append( StringUtils<TKey>.ToString(pair.Key)); + sb.Append(' => '); + sb.Append( StringUtils<TValue>.ToString(pair.Value)); + sb.Append(')'); + end; + sb.Append('}'); + Result := sb.ToString; + finally + sb.Free; + end; +end; + procedure TThriftDictionaryImpl<TKey, TValue>.TrimExcess; begin FDictionaly.TrimExcess; @@ -611,6 +662,28 @@ begin {$IFEND} end; +function TThriftListImpl<T>.ToString : string; +var elm : T; + sb : TThriftStringBuilder; + first : Boolean; +begin + sb := TThriftStringBuilder.Create('{'); + try + first := TRUE; + for elm in FList do begin + if first + then first := FALSE + else sb.Append(', '); + + sb.Append( StringUtils<T>.ToString(elm)); + end; + sb.Append('}'); + Result := sb.ToString; + finally + sb.Free; + end; +end; + procedure TThriftListImpl<T>.TrimExcess; begin FList.TrimExcess; diff --git a/lib/delphi/src/Thrift.Protocol.pas b/lib/delphi/src/Thrift.Protocol.pas index 36509ca..609dfc6 100644 --- a/lib/delphi/src/Thrift.Protocol.pas +++ b/lib/delphi/src/Thrift.Protocol.pas @@ -29,6 +29,7 @@ uses Contnrs, Thrift.Exception, Thrift.Stream, + Thrift.Utils, Thrift.Collections, Thrift.Transport; @@ -111,12 +112,6 @@ type function GetProtocol( const trans: ITransport): IProtocol; end; - TThriftStringBuilder = class( TStringBuilder) - public - function Append(const Value: TBytes): TStringBuilder; overload; - function Append(const Value: IThriftContainer): TStringBuilder; overload; - end; - TProtocolException = class( TException) public const // TODO(jensg): change into enum @@ -292,9 +287,8 @@ type constructor Create( trans: ITransport ); end; - IBase = interface - ['{08D9BAA8-5EAA-410F-B50B-AC2E6E5E4155}'] - function ToString: string; + IBase = interface( ISupportsToString) + ['{AFF6CECA-5200-4540-950E-9B89E0C1C00C}'] procedure Read( const iprot: IProtocol); procedure Write( const iprot: IProtocol); end; @@ -1034,19 +1028,6 @@ begin inherited HiddenCreate(Msg); end; -{ TThriftStringBuilder } - -function TThriftStringBuilder.Append(const Value: TBytes): TStringBuilder; -begin - Result := Append( string( RawByteString(Value)) ); -end; - -function TThriftStringBuilder.Append( - const Value: IThriftContainer): TStringBuilder; -begin - Result := Append( Value.ToString ); -end; - { TBinaryProtocolImpl.TFactory } constructor TBinaryProtocolImpl.TFactory.Create(AStrictRead, AStrictWrite: Boolean); diff --git a/lib/delphi/src/Thrift.Utils.pas b/lib/delphi/src/Thrift.Utils.pas index 7e57863..46e238c 100644 --- a/lib/delphi/src/Thrift.Utils.pas +++ b/lib/delphi/src/Thrift.Utils.pas @@ -25,12 +25,19 @@ interface uses {$IFDEF OLD_UNIT_NAMES} - Classes, Windows, SysUtils, Character, SyncObjs; + Classes, Windows, SysUtils, Character, SyncObjs, TypInfo, Rtti; {$ELSE} - System.Classes, Winapi.Windows, System.SysUtils, System.Character, System.SyncObjs; + System.Classes, Winapi.Windows, System.SysUtils, System.Character, + System.SyncObjs, System.TypInfo, System.Rtti; {$ENDIF} type + ISupportsToString = interface + ['{AF71C350-E0CD-4E94-B77C-0310DC8227FF}'] + function ToString : string; + end; + + IOverlappedHelper = interface ['{A1832EFA-2E02-4884-8F09-F0A0277157FA}'] function Overlapped : TOverlapped; @@ -55,6 +62,13 @@ type end; + TThriftStringBuilder = class( TStringBuilder) + public + function Append(const Value: TBytes): TStringBuilder; overload; + function Append(const Value: ISupportsToString): TStringBuilder; overload; + end; + + Base64Utils = class sealed public class function Encode( const src : TBytes; srcOff, len : Integer; dst : TBytes; dstOff : Integer) : Integer; static; @@ -68,6 +82,16 @@ type class function IsLowSurrogate( const c : Char) : Boolean; static; inline; end; + EnumUtils<T> = class sealed + public + class function ToString(const value : Integer) : string; reintroduce; static; inline; + end; + + StringUtils<T> = class sealed + public + class function ToString(const value : T) : string; reintroduce; static; inline; + end; + {$IFDEF Win64} function InterlockedExchangeAdd64( var Addend : Int64; Value : Int64) : Int64; @@ -256,4 +280,51 @@ end; {$ENDIF} +{ EnumUtils<T> } + +class function EnumUtils<T>.ToString(const value : Integer) : string; +var pType : PTypeInfo; +begin + pType := PTypeInfo(TypeInfo(T)); + if Assigned(pType) and (pType^.Kind = tkEnumeration) + then result := GetEnumName(pType,value) + else result := IntToStr(Ord(value)); +end; + + +{ StringUtils<T> } + +class function StringUtils<T>.ToString(const value : T) : string; +var pType : PTypeInfo; + base : ISupportsToString; +begin + pType := PTypeInfo(TypeInfo(T)); + if Assigned(pType) then begin + case pType^.Kind of + tkInterface : begin + if Supports(IInterface(value), ISupportsToString, base) then begin + result := base.toString; + Exit; + end; + end; + end; + end; + + result := TValue.From<T>(value).ToString; +end; + + +{ TThriftStringBuilder } + +function TThriftStringBuilder.Append(const Value: TBytes): TStringBuilder; +begin + Result := Append( string( RawByteString(Value)) ); +end; + +function TThriftStringBuilder.Append( const Value: ISupportsToString): TStringBuilder; +begin + Result := Append( Value.ToString ); +end; + + end. diff --git a/lib/delphi/test/TestServer.pas b/lib/delphi/test/TestServer.pas index 374472c..4cb0090 100644 --- a/lib/delphi/test/TestServer.pas +++ b/lib/delphi/test/TestServer.pas @@ -150,7 +150,7 @@ end; function TTestServer.TTestHandlerImpl.testEnum(thing: TNumberz): TNumberz; begin - Console.WriteLine('testEnum(' + IntToStr( Integer( thing)) + ')'); + Console.WriteLine('testEnum(' + EnumUtils<TNumberz>.ToString(Ord(thing)) + ')'); Result := thing; end; @@ -191,7 +191,10 @@ var insane : IThriftDictionary<Int64, IThriftDictionary<TNumberz, IInsanity>>; begin - Console.WriteLine('testInsanity()'); + Console.Write('testInsanity('); + if argument <> nil then Console.Write(argument.ToString); + Console.WriteLine(')'); + (** * So you think you've got this all worked, out eh? @@ -222,49 +225,20 @@ begin Result := insane; end; -function TTestServer.TTestHandlerImpl.testList( - const thing: IThriftList<Integer>): IThriftList<Integer>; -var - first : Boolean; - elem : Integer; +function TTestServer.TTestHandlerImpl.testList( const thing: IThriftList<Integer>): IThriftList<Integer>; begin - Console.Write('testList({'); - first := True; - for elem in thing do - begin - if first then - begin - first := False; - end else - begin - Console.Write(', '); - end; - Console.Write( IntToStr( elem)); - end; - Console.WriteLine('})'); + Console.Write('testList('); + if thing <> nil then Console.Write(thing.ToString); + Console.WriteLine(')'); Result := thing; end; function TTestServer.TTestHandlerImpl.testMap( const thing: IThriftDictionary<Integer, Integer>): IThriftDictionary<Integer, Integer>; -var - first : Boolean; - key : Integer; begin - Console.Write('testMap({'); - first := True; - for key in thing.Keys do - begin - if (first) then - begin - first := false; - end else - begin - Console.Write(', '); - end; - Console.Write(IntToStr(key) + ' => ' + IntToStr( thing[key])); - end; - Console.WriteLine('})'); + Console.Write('testMap('); + if thing <> nil then Console.Write(thing.ToString); + Console.WriteLine(')'); Result := thing; end; @@ -313,12 +287,11 @@ var x2 : TXception2; begin Console.WriteLine('testMultiException(' + arg0 + ', ' + arg1 + ')'); - if ( arg0 = 'Xception') then - begin + if ( arg0 = 'Xception') then begin raise TXception.Create( 1001, 'This is an Xception'); // test the new rich CTOR - end else - if ( arg0 = 'Xception2') then - begin + end; + + if ( arg0 = 'Xception2') then begin x2 := TXception2.Create; // the old way still works too? x2.ErrorCode := 2002; x2.Struct_thing := TXtructImpl.Create; @@ -332,17 +305,11 @@ begin end; function TTestServer.TTestHandlerImpl.testNest( const thing: IXtruct2): IXtruct2; -var - temp : IXtruct; begin - temp := thing.Struct_thing; - Console.WriteLine('testNest({' + - IntToStr( thing.Byte_thing) + ', {' + - '"' + temp.String_thing + '", ' + - IntToStr( temp.Byte_thing) + ', ' + - IntToStr( temp.I32_thing) + ', ' + - IntToStr( temp.I64_thing) + '}, ' + - IntToStr( temp.I32_thing) + '})'); + Console.Write('testNest('); + if thing <> nil then Console.Write(thing.ToString); + Console.WriteLine(')'); + Result := thing; end; @@ -353,34 +320,18 @@ begin Console.WriteLine('testOneway finished'); end; -function TTestServer.TTestHandlerImpl.testSet( - const thing: IHashSet<Integer>):IHashSet<Integer>; -var - first : Boolean; - elem : Integer; +function TTestServer.TTestHandlerImpl.testSet( const thing: IHashSet<Integer>):IHashSet<Integer>; begin - Console.Write('testSet({'); - first := True; + Console.Write('testSet('); + if thing <> nil then Console.Write(thing.ToString); + Console.WriteLine(')');; - for elem in thing do - begin - if first then - begin - first := False; - end else - begin - Console.Write( ', '); - end; - Console.Write( IntToStr( elem)); - end; - Console.WriteLine('})'); Result := thing; end; procedure TTestServer.TTestHandlerImpl.testStop; begin - if FServer <> nil then - begin + if FServer <> nil then begin FServer.Stop; end; end; @@ -399,24 +350,11 @@ end; function TTestServer.TTestHandlerImpl.testStringMap( const thing: IThriftDictionary<string, string>): IThriftDictionary<string, string>; -var - first : Boolean; - key : string; begin - Console.Write('testStringMap({'); - first := True; - for key in thing.Keys do - begin - if (first) then - begin - first := false; - end else - begin - Console.Write(', '); - end; - Console.Write(key + ' => ' + thing[key]); - end; - Console.WriteLine('})'); + Console.Write('testStringMap('); + if thing <> nil then Console.Write(thing.ToString); + Console.WriteLine(')'); + Result := thing; end; @@ -433,11 +371,10 @@ end; function TTestServer.TTestHandlerImpl.testStruct( const thing: IXtruct): IXtruct; begin - Console.WriteLine('testStruct({' + - '"' + thing.String_thing + '", ' + - IntToStr( thing.Byte_thing) + ', ' + - IntToStr( thing.I32_thing) + ', ' + - IntToStr( thing.I64_thing)); + Console.Write('testStruct('); + if thing <> nil then Console.Write(thing.ToString); + Console.WriteLine(')'); + Result := thing; end;
