THRIFT-1993 Factory to create instances from known (generated) interface types with Delphi
Patch: Jens Geyer
diff --git a/compiler/cpp/src/generate/t_delphi_generator.cc b/compiler/cpp/src/generate/t_delphi_generator.cc
index 0fc9c06..8476f80 100644
--- a/compiler/cpp/src/generate/t_delphi_generator.cc
+++ b/compiler/cpp/src/generate/t_delphi_generator.cc
@@ -60,6 +60,8 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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;
- f_all << " TConstants_Initialize;" << endl;
- f_all << "end;" << endl << endl;
-
- f_all << "finalization" << endl;
- f_all << "begin" << endl;
- f_all << " TConstants_Finalize;" << endl;
- f_all << "end;" << endl;
- f_all << "{$IFEND}" << endl << 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 << "{$IFEND}" << endl;
+ }
+ if (register_types_) {
+ f_all << " RegisterTypeFactories;" << endl;
+ }
+ f_all << endl;
+
+ f_all << "finalization" << endl;
+ if ( has_const ) {
+ f_all << "{$IF CompilerVersion < 21.0}" << endl;
+ f_all << " TConstants_Finalize;" << endl;
+ f_all << "{$IFEND}" << endl;
+ }
+ f_all << endl << endl;
+
f_all << "end." << endl;
f_all.close();
@@ -924,6 +956,10 @@
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::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 @@
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 0000000..1b863d2
--- /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<T> = function:T;
+
+ TypeRegistry = class
+ private
+ class var FTypeInfoToFactoryLookup : TDictionary<Pointer, Pointer>;
+ public
+ class constructor Create;
+ class destructor Destroy;
+ class procedure RegisterTypeFactory<F>(const aFactoryMethod: TFactoryMethod<F>);
+ class function Construct<F>: F;
+ end;
+
+implementation
+
+uses
+ TypInfo;
+
+{ TypeRegistration }
+
+class constructor TypeRegistry.Create;
+begin
+ FTypeInfoToFactoryLookup := TDictionary<Pointer, Pointer>.Create;
+end;
+
+class destructor TypeRegistry.Destroy;
+begin
+ FTypeInfoToFactoryLookup.Free;
+end;
+
+class procedure TypeRegistry.RegisterTypeFactory<F>(const aFactoryMethod: TFactoryMethod<F>);
+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>: 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<F>(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 8d25eae..6ccd260 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 @@
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 0000000..64d5771
--- /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<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
+ then Writeln( name, ' = ok')
+ else begin
+ Writeln( name, ' = failed');
+ raise Exception.Create( 'Test with '+name+' failed!');
+ end;
+end;
+
+begin
+ Writeln('Testing ...');
+ Tester<IDoubles>.Test;
+ Tester<IOneOfEach>.Test;
+ Tester<IBonk>.Test;
+ Tester<INesting>.Test;
+ Tester<IHolyMoley>.Test;
+ Tester<IBackwards>.Test;
+ Tester<IEmpty>.Test;
+ Tester<IWrapper>.Test;
+ Tester<IRandomStuff>.Test;
+ Tester<IBase64>.Test;
+ Tester<ICompactProtoTestStruct>.Test;
+ Tester<ISingleMapTestStruct>.Test;
+ Tester<IBlowUp>.Test;
+ Tester<IReverseOrderStruct>.Test;
+ Tester<IStructWithSomeEnum>.Test;
+ Tester<ITestUnion>.Test;
+ Tester<ITestUnionMinusStringField>.Test;
+ Tester<IComparableUnion>.Test;
+ Tester<IStructWithAUnion>.Test;
+ Tester<IPrimitiveThenStruct>.Test;
+ Tester<IStructWithASomemap>.Test;
+ Tester<IBigFieldIdStruct>.Test;
+ Tester<IBreaksRubyCompactProtocol>.Test;
+ Tester<ITupleProtocolTestStruct>.Test;
+ Writeln('Completed.');
+
+
+end.
+