THRIFT-1993 Factory to create instances from known (generated) interface types with...
authorJens Geyer <jensg@apache.org>
Tue, 4 Jun 2013 19:43:40 +0000 (21:43 +0200)
committerJens Geyer <jensg@apache.org>
Tue, 4 Jun 2013 20:06:30 +0000 (22:06 +0200)
Patch: Jens Geyer

compiler/cpp/src/generate/t_delphi_generator.cc
lib/delphi/src/Thrift.TypeRegistry.pas [new file with mode: 0644]
lib/delphi/test/codegen/run-Pascal-Codegen-Tests.bat.tmpl
lib/delphi/test/typeregistry/TestTypeRegistry.dpr [new file with mode: 0644]

index 0fc9c06..8476f80 100644 (file)
@@ -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 (file)
index 0000000..1b863d2
--- /dev/null
@@ -0,0 +1,84 @@
+(*\r
+ * Licensed to the Apache Software Foundation (ASF) under one\r
+ * or more contributor license agreements. See the NOTICE file\r
+ * distributed with this work for additional information\r
+ * regarding copyright ownership. The ASF licenses this file\r
+ * to you under the Apache License, Version 2.0 (the\r
+ * "License"); you may not use this file except in compliance\r
+ * with the License. You may obtain a copy of the License at\r
+ *\r
+ *   http://www.apache.org/licenses/LICENSE-2.0\r
+ *\r
+ * Unless required by applicable law or agreed to in writing,\r
+ * software distributed under the License is distributed on an\r
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY\r
+ * KIND, either express or implied. See the License for the\r
+ * specific language governing permissions and limitations\r
+ * under the License.\r
+ *)\r
+\r
+unit Thrift.TypeRegistry;\r
+\r
+interface\r
+\r
+uses\r
+  Generics.Collections;\r
+\r
+type\r
+  TFactoryMethod<T> = function:T;\r
+\r
+  TypeRegistry = class\r
+  private\r
+    class var FTypeInfoToFactoryLookup : TDictionary<Pointer, Pointer>;\r
+  public\r
+    class constructor Create;\r
+    class destructor Destroy;\r
+    class procedure RegisterTypeFactory<F>(const aFactoryMethod: TFactoryMethod<F>);\r
+    class function  Construct<F>: F;\r
+  end;\r
+\r
+implementation\r
+\r
+uses\r
+  TypInfo;\r
+\r
+{ TypeRegistration }\r
+\r
+class constructor TypeRegistry.Create;\r
+begin\r
+  FTypeInfoToFactoryLookup := TDictionary<Pointer, Pointer>.Create;\r
+end;\r
+\r
+class destructor TypeRegistry.Destroy;\r
+begin\r
+  FTypeInfoToFactoryLookup.Free;\r
+end;\r
+\r
+class procedure TypeRegistry.RegisterTypeFactory<F>(const aFactoryMethod: TFactoryMethod<F>);\r
+var\r
+  TypeInfo     : Pointer;\r
+begin\r
+  TypeInfo := System.TypeInfo(F);\r
+\r
+  if (TypeInfo <> nil) and (PTypeInfo(TypeInfo).Kind = tkInterface)\r
+  then FTypeInfoToFactoryLookup.AddOrSetValue(TypeInfo, @aFactoryMethod);\r
+end;\r
+\r
+class function TypeRegistry.Construct<F>: F;\r
+var\r
+  TypeInfo     : PTypeInfo;\r
+  Factory      : Pointer;\r
+begin\r
+  Result := default(F);\r
+\r
+  TypeInfo := System.TypeInfo(F);\r
+\r
+  if Assigned(TypeInfo) and (TypeInfo.Kind = tkInterface)\r
+  then begin\r
+    if FTypeInfoToFactoryLookup.TryGetValue(TypeInfo, Factory)\r
+    then Result := TFactoryMethod<F>(Factory)();\r
+  end;\r
+end;\r
+\r
+\r
+end.\r
index 8d25eae..6ccd260 100644 (file)
@@ -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 (file)
index 0000000..64d5771
--- /dev/null
@@ -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<T : IInterface> = class
+  public
+    class procedure Test;
+  end;
+
+class procedure Tester<T>.Test;
+var instance : T;
+    name : string;
+begin
+  instance := TypeRegistry.Construct<T>;
+  name := GetTypeName(TypeInfo(T));
+  if instance <> nil\r
+  then Writeln( name, ' = ok')\r
+  else begin\r
+    Writeln( name, ' = failed');\r
+    raise Exception.Create( 'Test with '+name+' failed!');\r
+  end;\r
+end;
+
+begin
+  Writeln('Testing ...');
+  Tester<IDoubles>.Test;
+  Tester<IOneOfEach>.Test;\r
+  Tester<IBonk>.Test;\r
+  Tester<INesting>.Test;\r
+  Tester<IHolyMoley>.Test;\r
+  Tester<IBackwards>.Test;\r
+  Tester<IEmpty>.Test;\r
+  Tester<IWrapper>.Test;\r
+  Tester<IRandomStuff>.Test;\r
+  Tester<IBase64>.Test;\r
+  Tester<ICompactProtoTestStruct>.Test;\r
+  Tester<ISingleMapTestStruct>.Test;\r
+  Tester<IBlowUp>.Test;\r
+  Tester<IReverseOrderStruct>.Test;\r
+  Tester<IStructWithSomeEnum>.Test;\r
+  Tester<ITestUnion>.Test;\r
+  Tester<ITestUnionMinusStringField>.Test;\r
+  Tester<IComparableUnion>.Test;\r
+  Tester<IStructWithAUnion>.Test;\r
+  Tester<IPrimitiveThenStruct>.Test;\r
+  Tester<IStructWithASomemap>.Test;\r
+  Tester<IBigFieldIdStruct>.Test;\r
+  Tester<IBreaksRubyCompactProtocol>.Test;\r
+  Tester<ITupleProtocolTestStruct>.Test;\r
+  Writeln('Completed.');\r
+\r
+\r
+end.\r
+