From c2c4d72b90f322e46a8cbb4e196c598bbcb3f615 Mon Sep 17 00:00:00 2001 From: Jens Geyer Date: Tue, 4 Jun 2013 21:43:40 +0200 Subject: [PATCH] THRIFT-1993 Factory to create instances from known (generated) interface types with Delphi Patch: Jens Geyer --- .../cpp/src/generate/t_delphi_generator.cc | 109 ++++++++++++++++-- lib/delphi/src/Thrift.TypeRegistry.pas | 84 ++++++++++++++ .../codegen/run-Pascal-Codegen-Tests.bat.tmpl | 2 +- .../test/typeregistry/TestTypeRegistry.dpr | 89 ++++++++++++++ 4 files changed, 271 insertions(+), 13 deletions(-) create mode 100644 lib/delphi/src/Thrift.TypeRegistry.pas create mode 100644 lib/delphi/test/typeregistry/TestTypeRegistry.dpr diff --git a/compiler/cpp/src/generate/t_delphi_generator.cc b/compiler/cpp/src/generate/t_delphi_generator.cc index 0fc9c069..8476f805 100644 --- a/compiler/cpp/src/generate/t_delphi_generator.cc +++ b/compiler/cpp/src/generate/t_delphi_generator.cc @@ -60,6 +60,8 @@ class t_delphi_generator : public t_oop_generator iter = parsed_options.find("ansistr_binary"); ansistr_binary_ = (iter != parsed_options.end()); + iter = parsed_options.find("register_types"); + register_types_ = (iter != parsed_options.end()); out_dir_base_ = "gen-delphi"; escape_.clear(); @@ -108,6 +110,9 @@ class t_delphi_generator : public t_oop_generator void generate_delphi_struct(t_struct* tstruct, bool is_exception); void generate_delphi_struct_impl( ostream& out, std::string cls_prefix, t_struct* tstruct, bool is_exception, bool is_result = false, bool is_x_factory = false); + void print_delphi_struct_type_factory_func( ostream& out, t_struct* tstruct); + void generate_delphi_struct_type_factory( ostream& out, std::string cls_prefix, t_struct* tstruct, bool is_exception, bool is_result = false, bool is_x_factory = false); + void generate_delphi_struct_type_factory_registration( ostream& out, std::string cls_prefix, t_struct* tstruct, bool is_exception, bool is_result = false, bool is_x_factory = false); void generate_delphi_struct_definition(std::ostream& out, t_struct* tstruct, bool is_xception=false, bool in_class=false, bool is_result=false, bool is_x_factory = false); void generate_delphi_struct_reader(std::ostream& out, t_struct* tstruct); void generate_delphi_struct_result_writer(std::ostream& out, t_struct* tstruct); @@ -188,6 +193,8 @@ class t_delphi_generator : public t_oop_generator std::ostringstream s_const_impl; std::ostringstream s_struct_impl; std::ostringstream s_service_impl; + std::ostringstream s_type_factory_registration; + std::ostringstream s_type_factory_funcs; bool has_enum; bool has_const; std::string namespace_dir_; @@ -207,6 +214,7 @@ class t_delphi_generator : public t_oop_generator bool is_void( t_type* type ); int indent_impl_; bool ansistr_binary_; + bool register_types_; void indent_up_impl(){ ++indent_impl_; }; @@ -428,6 +436,11 @@ void t_delphi_generator::init_generator() { add_delphi_uses_list("Thrift.Protocol"); add_delphi_uses_list("Thrift.Transport"); + if (register_types_) + { + add_delphi_uses_list("Thrift.TypeRegistry"); + } + init_known_types_list(); string unitname, nsname; @@ -509,20 +522,39 @@ void t_delphi_generator::close_generator() { f_all << s_struct_impl.str(); f_all << s_service_impl.str(); f_all << s_const_impl.str(); - - if ( has_const ) { - f_all << "{$IF CompilerVersion < 21.0}" << endl; - f_all << "initialization" << endl; - f_all << "begin" << endl; + + + if (register_types_) + { + f_all << endl; + f_all << "// Type factory methods and registration" << endl; + f_all << s_type_factory_funcs.str(); + f_all << "procedure RegisterTypeFactories;" << endl; + f_all << "begin" << endl; + f_all << s_type_factory_registration.str(); + f_all << "end;" << endl; + } + f_all << endl; + + f_all << "initialization" << endl; + if ( has_const ) { + f_all << "{$IF CompilerVersion < 21.0}" << endl; f_all << " TConstants_Initialize;" << endl; - f_all << "end;" << endl << endl; + f_all << "{$IFEND}" << endl; + } + if (register_types_) { + f_all << " RegisterTypeFactories;" << endl; + } + f_all << endl; - f_all << "finalization" << endl; - f_all << "begin" << endl; + f_all << "finalization" << endl; + if ( has_const ) { + f_all << "{$IF CompilerVersion < 21.0}" << endl; f_all << " TConstants_Finalize;" << endl; - f_all << "end;" << endl; - f_all << "{$IFEND}" << endl << endl; + f_all << "{$IFEND}" << endl; } + f_all << endl << endl; + f_all << "end." << endl; f_all.close(); @@ -924,6 +956,10 @@ void t_delphi_generator::generate_delphi_struct(t_struct* tstruct, bool is_excep add_defined_type( tstruct); generate_delphi_struct_impl(s_struct_impl, "", tstruct, is_exception); + if (register_types_) { + generate_delphi_struct_type_factory(s_type_factory_funcs, "", tstruct, is_exception); + generate_delphi_struct_type_factory_registration(s_type_factory_registration, "", tstruct, is_exception); + } } void t_delphi_generator::generate_delphi_struct_impl( ostream& out, string cls_prefix, t_struct* tstruct, bool is_exception, bool is_result, bool is_x_factory) { @@ -1064,6 +1100,54 @@ void t_delphi_generator::generate_delphi_struct_impl( ostream& out, string cls_p } } +void t_delphi_generator::print_delphi_struct_type_factory_func( ostream& out, t_struct* tstruct) { + string struct_intf_name = type_name(tstruct); + out << "Create_"; + out << struct_intf_name; + out << "_Impl"; +} + + +void t_delphi_generator::generate_delphi_struct_type_factory( ostream& out, string cls_prefix, t_struct* tstruct, bool is_exception, bool is_result, bool is_x_factory) { + + if (is_exception) + return; + if (is_result) + return; + if (is_x_factory) + return; + + string struct_intf_name = type_name(tstruct); + string cls_nm = type_name(tstruct,true,false); + + out << "function "; + print_delphi_struct_type_factory_func(out, tstruct); + out << ": "; + out << struct_intf_name; + out << ";" << endl; + out << "begin" << endl; + indent_up(); + indent(out) << "Result := " << cls_nm << ".Create;" << endl; + indent_down(); + out << "end;" << endl << endl; +} + +void t_delphi_generator::generate_delphi_struct_type_factory_registration( ostream& out, string cls_prefix, t_struct* tstruct, bool is_exception, bool is_result, bool is_x_factory) { + if (is_exception) + return; + if (is_result) + return; + if (is_x_factory) + return; + + string struct_intf_name = type_name(tstruct); + + indent(out) << " TypeRegistry.RegisterTypeFactory<" << struct_intf_name << ">("; + print_delphi_struct_type_factory_func(out, tstruct); + out << ");"; + out << endl; +} + void t_delphi_generator::generate_delphi_struct_definition(ostream &out, t_struct* tstruct, bool is_exception, bool in_class, bool is_result, bool is_x_factory) { bool is_final = (tstruct->annotations_.find("final") != tstruct->annotations_.end()); string struct_intf_name; @@ -3038,6 +3122,7 @@ bool t_delphi_generator::is_void( t_type* type ) { return false; } -THRIFT_REGISTER_GENERATOR(delphi, "delphi", -" ansistr_binary: Use AnsiString as binary properties.\n") +THRIFT_REGISTER_GENERATOR(delphi, "delphi", +" ansistr_binary: Use AnsiString as binary properties.\n" +" register_types: Register structs and there implementations in a global type registry\n"); diff --git a/lib/delphi/src/Thrift.TypeRegistry.pas b/lib/delphi/src/Thrift.TypeRegistry.pas new file mode 100644 index 00000000..1b863d2e --- /dev/null +++ b/lib/delphi/src/Thrift.TypeRegistry.pas @@ -0,0 +1,84 @@ +(* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + *) + +unit Thrift.TypeRegistry; + +interface + +uses + Generics.Collections; + +type + TFactoryMethod = function:T; + + TypeRegistry = class + private + class var FTypeInfoToFactoryLookup : TDictionary; + public + class constructor Create; + class destructor Destroy; + class procedure RegisterTypeFactory(const aFactoryMethod: TFactoryMethod); + class function Construct: F; + end; + +implementation + +uses + TypInfo; + +{ TypeRegistration } + +class constructor TypeRegistry.Create; +begin + FTypeInfoToFactoryLookup := TDictionary.Create; +end; + +class destructor TypeRegistry.Destroy; +begin + FTypeInfoToFactoryLookup.Free; +end; + +class procedure TypeRegistry.RegisterTypeFactory(const aFactoryMethod: TFactoryMethod); +var + TypeInfo : Pointer; +begin + TypeInfo := System.TypeInfo(F); + + if (TypeInfo <> nil) and (PTypeInfo(TypeInfo).Kind = tkInterface) + then FTypeInfoToFactoryLookup.AddOrSetValue(TypeInfo, @aFactoryMethod); +end; + +class function TypeRegistry.Construct: F; +var + TypeInfo : PTypeInfo; + Factory : Pointer; +begin + Result := default(F); + + TypeInfo := System.TypeInfo(F); + + if Assigned(TypeInfo) and (TypeInfo.Kind = tkInterface) + then begin + if FTypeInfoToFactoryLookup.TryGetValue(TypeInfo, Factory) + then Result := TFactoryMethod(Factory)(); + end; +end; + + +end. diff --git a/lib/delphi/test/codegen/run-Pascal-Codegen-Tests.bat.tmpl b/lib/delphi/test/codegen/run-Pascal-Codegen-Tests.bat.tmpl index 8d25eaee..6ccd2606 100644 --- a/lib/delphi/test/codegen/run-Pascal-Codegen-Tests.bat.tmpl +++ b/lib/delphi/test/codegen/run-Pascal-Codegen-Tests.bat.tmpl @@ -58,7 +58,7 @@ rem * compile all thrift files, generate PAS and C++ code echo. echo Generating code, please wait ... cd "%TARGET%" -for %%a in (*.thrift) do "%BIN%\thrift.exe" -v --gen delphi:ansistr_binary "%%a" >> "%LOGFILE%" +for %%a in (*.thrift) do "%BIN%\thrift.exe" -v --gen delphi:ansistr_binary,register_types "%%a" 2>> "%LOGFILE%" REM * for %%a in (*.thrift) do "%BIN%\thrift.exe" -v --gen cpp "%%a" >> NUL: cmd /c start notepad "%LOGFILE%" cd .. diff --git a/lib/delphi/test/typeregistry/TestTypeRegistry.dpr b/lib/delphi/test/typeregistry/TestTypeRegistry.dpr new file mode 100644 index 00000000..64d57713 --- /dev/null +++ b/lib/delphi/test/typeregistry/TestTypeRegistry.dpr @@ -0,0 +1,89 @@ +(* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + *) + +program TestTypeRegistry; + +{$APPTYPE CONSOLE} + +uses + Classes, Windows, SysUtils, Generics.Collections, TypInfo, + Thrift in '..\..\src\Thrift.pas', + Thrift.Transport in '..\..\src\Thrift.Transport.pas', + Thrift.Protocol in '..\..\src\Thrift.Protocol.pas', + Thrift.Protocol.JSON in '..\..\src\Thrift.Protocol.JSON.pas', + Thrift.Collections in '..\..\src\Thrift.Collections.pas', + Thrift.Server in '..\..\src\Thrift.Server.pas', + Thrift.Console in '..\..\src\Thrift.Console.pas', + Thrift.Utils in '..\..\src\Thrift.Utils.pas', + Thrift.Serializer in '..\..\src\Thrift.Serializer.pas', + Thrift.Stream in '..\..\src\Thrift.Stream.pas', + Thrift.TypeRegistry in '..\..\src\Thrift.TypeRegistry.pas', + DebugProtoTest; + +type + Tester = class + public + class procedure Test; + end; + +class procedure Tester.Test; +var instance : T; + name : string; +begin + instance := TypeRegistry.Construct; + name := GetTypeName(TypeInfo(T)); + if instance <> nil + then Writeln( name, ' = ok') + else begin + Writeln( name, ' = failed'); + raise Exception.Create( 'Test with '+name+' failed!'); + end; +end; + +begin + Writeln('Testing ...'); + Tester.Test; + Tester.Test; + Tester.Test; + Tester.Test; + Tester.Test; + Tester.Test; + Tester.Test; + Tester.Test; + Tester.Test; + Tester.Test; + Tester.Test; + Tester.Test; + Tester.Test; + Tester.Test; + Tester.Test; + Tester.Test; + Tester.Test; + Tester.Test; + Tester.Test; + Tester.Test; + Tester.Test; + Tester.Test; + Tester.Test; + Tester.Test; + Writeln('Completed.'); + + +end. + -- 2.17.1