THRIFT-922. cpp: Update C++ generator to emit templatized code
When the "templates" option is passed to the C++ generator, it now emits
templatized versions of the client and processor. Generated types emit
templatized read() and write() functions.
This allows the generated code to invoke the correct non-virtual
TTransport and TProtocol implementations, resulting in faster
serialization and deserialization.
git-svn-id: https://svn.apache.org/repos/asf/incubator/thrift/trunk@1005138 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/compiler/cpp/src/generate/t_cpp_generator.cc b/compiler/cpp/src/generate/t_cpp_generator.cc
index 98edcfa..c8d9dda 100644
--- a/compiler/cpp/src/generate/t_cpp_generator.cc
+++ b/compiler/cpp/src/generate/t_cpp_generator.cc
@@ -65,6 +65,9 @@
iter = parsed_options.find("no_client_completion");
gen_no_client_completion_ = (iter != parsed_options.end());
+ iter = parsed_options.find("templates");
+ gen_templates_ = (iter != parsed_options.end());
+
out_dir_base_ = "gen-cpp";
}
@@ -113,7 +116,8 @@
void generate_service_client (t_service* tservice, string style);
void generate_service_processor (t_service* tservice, string style);
void generate_service_skeleton (t_service* tservice);
- void generate_process_function (t_service* tservice, t_function* tfunction, string style);
+ void generate_process_function (t_service* tservice, t_function* tfunction,
+ string style, bool specialized=false);
void generate_function_helpers (t_service* tservice, t_function* tfunction);
void generate_service_async_skeleton (t_service* tservice);
@@ -230,6 +234,11 @@
bool gen_dense_;
/**
+ * True iff we should generate templatized reader/writer methods.
+ */
+ bool gen_templates_;
+
+ /**
* True iff we should use a path prefix in our #include statements for other
* thrift-generated header files.
*/
@@ -259,8 +268,10 @@
std::ofstream f_types_;
std::ofstream f_types_impl_;
+ std::ofstream f_types_tcc_;
std::ofstream f_header_;
std::ofstream f_service_;
+ std::ofstream f_service_tcc_;
/**
* When generating local reflections, make sure we don't generate duplicates.
@@ -286,17 +297,30 @@
string f_types_impl_name = get_out_dir()+program_name_+"_types.cpp";
f_types_impl_.open(f_types_impl_name.c_str());
+ if (gen_templates_) {
+ // If we don't open the stream, it appears to just discard data,
+ // which is fine.
+ string f_types_tcc_name = get_out_dir()+program_name_+"_types.tcc";
+ f_types_tcc_.open(f_types_tcc_name.c_str());
+ }
+
// Print header
f_types_ <<
autogen_comment();
f_types_impl_ <<
autogen_comment();
+ f_types_tcc_ <<
+ autogen_comment();
// Start ifndef
f_types_ <<
"#ifndef " << program_name_ << "_TYPES_H" << endl <<
"#define " << program_name_ << "_TYPES_H" << endl <<
endl;
+ f_types_tcc_ <<
+ "#ifndef " << program_name_ << "_TYPES_TCC" << endl <<
+ "#define " << program_name_ << "_TYPES_TCC" << endl <<
+ endl;
// Include base types
f_types_ <<
@@ -312,6 +336,12 @@
f_types_ <<
"#include \"" << get_include_prefix(*(includes[i])) <<
includes[i]->get_name() << "_types.h\"" << endl;
+
+ // XXX(simpkins): If gen_templates_ is enabled, we currently assume all
+ // included files were also generated with templates enabled.
+ f_types_tcc_ <<
+ "#include \"" << get_include_prefix(*(includes[i])) <<
+ includes[i]->get_name() << "_types.tcc\"" << endl;
}
f_types_ << endl;
@@ -334,6 +364,10 @@
"#include \"" << get_include_prefix(*get_program()) << program_name_ <<
"_types.h\"" << endl <<
endl;
+ f_types_tcc_ <<
+ "#include \"" << get_include_prefix(*get_program()) << program_name_ <<
+ "_types.h\"" << endl <<
+ endl;
// If we are generating local reflection metadata, we need to include
// the definition of TypeSpec.
@@ -354,6 +388,10 @@
f_types_impl_ <<
ns_open_ << endl <<
endl;
+
+ f_types_tcc_ <<
+ ns_open_ << endl <<
+ endl;
}
/**
@@ -366,14 +404,30 @@
endl;
f_types_impl_ <<
ns_close_ << endl;
+ f_types_tcc_ <<
+ ns_close_ << endl <<
+ endl;
+
+ // Include the types.tcc file from the types header file,
+ // so clients don't have to explicitly include the tcc file.
+ // TODO(simpkins): Make this a separate option.
+ if (gen_templates_) {
+ f_types_ <<
+ "#include \"" << get_include_prefix(*get_program()) << program_name_ <<
+ "_types.tcc\"" << endl <<
+ endl;
+ }
// Close ifndef
f_types_ <<
"#endif" << endl;
+ f_types_tcc_ <<
+ "#endif" << endl;
// Close output file
f_types_.close();
f_types_impl_.close();
+ f_types_tcc_.close();
}
/**
@@ -645,8 +699,10 @@
generate_local_reflection(f_types_, tstruct, false);
generate_local_reflection(f_types_impl_, tstruct, true);
generate_local_reflection_pointer(f_types_impl_, tstruct);
- generate_struct_reader(f_types_impl_, tstruct);
- generate_struct_writer(f_types_impl_, tstruct);
+
+ std::ofstream& out = (gen_templates_ ? f_types_tcc_ : f_types_impl_);
+ generate_struct_reader(out, tstruct);
+ generate_struct_writer(out, tstruct);
}
/**
@@ -842,13 +898,28 @@
indent() << "bool operator < (const "
<< tstruct->get_name() << " & ) const;" << endl << endl;
}
+
if (read) {
- out <<
- indent() << "uint32_t read(::apache::thrift::protocol::TProtocol* iprot);" << endl;
+ if (gen_templates_) {
+ out <<
+ indent() << "template <class Protocol_>" << endl <<
+ indent() << "uint32_t read(Protocol_* iprot);" << endl;
+ } else {
+ out <<
+ indent() << "uint32_t read(" <<
+ "::apache::thrift::protocol::TProtocol* iprot);" << endl;
+ }
}
if (write) {
- out <<
- indent() << "uint32_t write(::apache::thrift::protocol::TProtocol* oprot) const;" << endl;
+ if (gen_templates_) {
+ out <<
+ indent() << "template <class Protocol_>" << endl <<
+ indent() << "uint32_t write(Protocol_* oprot) const;" << endl;
+ } else {
+ out <<
+ indent() << "uint32_t write(" <<
+ "::apache::thrift::protocol::TProtocol* oprot) const;" << endl;
+ }
}
out << endl;
@@ -1035,8 +1106,16 @@
void t_cpp_generator::generate_struct_reader(ofstream& out,
t_struct* tstruct,
bool pointers) {
- indent(out) <<
- "uint32_t " << tstruct->get_name() << "::read(::apache::thrift::protocol::TProtocol* iprot) {" << endl;
+ if (gen_templates_) {
+ out <<
+ indent() << "template <class Protocol_>" << endl <<
+ indent() << "uint32_t " << tstruct->get_name() <<
+ "::read(Protocol_* iprot) {" << endl;
+ } else {
+ indent(out) <<
+ "uint32_t " << tstruct->get_name() <<
+ "::read(::apache::thrift::protocol::TProtocol* iprot) {" << endl;
+ }
indent_up();
const vector<t_field*>& fields = tstruct->get_members();
@@ -1174,8 +1253,16 @@
const vector<t_field*>& fields = tstruct->get_sorted_members();
vector<t_field*>::const_iterator f_iter;
- indent(out) <<
- "uint32_t " << tstruct->get_name() << "::write(::apache::thrift::protocol::TProtocol* oprot) const {" << endl;
+ if (gen_templates_) {
+ out <<
+ indent() << "template <class Protocol_>" << endl <<
+ indent() << "uint32_t " << tstruct->get_name() <<
+ "::write(Protocol_* oprot) const {" << endl;
+ } else {
+ indent(out) <<
+ "uint32_t " << tstruct->get_name() <<
+ "::write(::apache::thrift::protocol::TProtocol* oprot) const {" << endl;
+ }
indent_up();
out <<
@@ -1236,8 +1323,16 @@
const vector<t_field*>& fields = tstruct->get_sorted_members();
vector<t_field*>::const_iterator f_iter;
- indent(out) <<
- "uint32_t " << tstruct->get_name() << "::write(::apache::thrift::protocol::TProtocol* oprot) const {" << endl;
+ if (gen_templates_) {
+ out <<
+ indent() << "template <class Protocol_>" << endl <<
+ indent() << "uint32_t " << tstruct->get_name() <<
+ "::write(Protocol_* oprot) const {" << endl;
+ } else {
+ indent(out) <<
+ "uint32_t " << tstruct->get_name() <<
+ "::write(::apache::thrift::protocol::TProtocol* oprot) const {" << endl;
+ }
indent_up();
out <<
@@ -1360,9 +1455,34 @@
f_service_ <<
"#include \"async/TAsyncChannel.h\"" << endl;
}
- f_service_ << endl <<
- ns_open_ << endl <<
- endl;
+ if (gen_templates_) {
+ f_service_ <<
+ "#include \"" << get_include_prefix(*get_program()) << svcname <<
+ ".tcc\"" << endl;
+
+ string f_service_tcc_name = get_out_dir()+svcname+".tcc";
+ f_service_tcc_.open(f_service_tcc_name.c_str());
+ f_service_tcc_ <<
+ autogen_comment();
+ f_service_tcc_ <<
+ "#include \"" << get_include_prefix(*get_program()) << svcname <<
+ ".h\"" << endl;
+
+ f_service_tcc_ <<
+ "#ifndef " << svcname << "_TCC" << endl <<
+ "#define " << svcname << "_TCC" << endl <<
+ endl;
+
+ if (gen_cob_style_) {
+ f_service_tcc_ <<
+ "#include \"async/TAsyncChannel.h\"" << endl;
+ }
+ }
+
+ f_service_ <<
+ endl << ns_open_ << endl << endl;
+ f_service_tcc_ <<
+ endl << ns_open_ << endl << endl;
// Generate all the components
generate_service_interface(tservice, "");
@@ -1387,13 +1507,30 @@
f_service_ <<
ns_close_ << endl <<
endl;
- f_header_ <<
+ f_service_tcc_ <<
ns_close_ << endl <<
endl;
f_header_ <<
+ ns_close_ << endl <<
+ endl;
+
+ // TODO(simpkins): Make this a separate option
+ if (gen_templates_) {
+ f_header_ <<
+ "#include \"" << get_include_prefix(*get_program()) << svcname <<
+ ".tcc\"" << endl <<
+ "#include \"" << get_include_prefix(*get_program()) << program_name_ <<
+ "_types.tcc\"" << endl <<
+ endl;
+ }
+
+ f_header_ <<
+ "#endif" << endl;
+ f_service_tcc_ <<
"#endif" << endl;
// Close the files
+ f_service_tcc_.close();
f_service_.close();
f_header_.close();
}
@@ -1407,6 +1544,8 @@
void t_cpp_generator::generate_service_helpers(t_service* tservice) {
vector<t_function*> functions = tservice->get_functions();
vector<t_function*>::iterator f_iter;
+ std::ofstream& out = (gen_templates_ ? f_service_tcc_ : f_service_);
+
for (f_iter = functions.begin(); f_iter != functions.end(); ++f_iter) {
t_struct* ts = (*f_iter)->get_arglist();
string name_orig = ts->get_name();
@@ -1414,11 +1553,11 @@
// TODO(dreiss): Why is this stuff not in generate_function_helpers?
ts->set_name(tservice->get_name() + "_" + (*f_iter)->get_name() + "_args");
generate_struct_definition(f_header_, ts, false);
- generate_struct_reader(f_service_, ts);
- generate_struct_writer(f_service_, ts);
+ generate_struct_reader(out, ts);
+ generate_struct_writer(out, ts);
ts->set_name(tservice->get_name() + "_" + (*f_iter)->get_name() + "_pargs");
generate_struct_definition(f_header_, ts, false, true, false, true);
- generate_struct_writer(f_service_, ts, true);
+ generate_struct_writer(out, ts, true);
ts->set_name(name_orig);
generate_function_helpers(tservice, *f_iter);
@@ -1432,21 +1571,40 @@
*/
void t_cpp_generator::generate_service_interface(t_service* tservice, string style) {
+ string service_if_name = service_name_ + style + "If";
if (style == "CobCl") {
// Forward declare the client.
- indent(f_header_) << "class " << service_name_ << "CobClient;" << endl << endl;
+ string client_name = service_name_ + "CobClient";
+ if (gen_templates_) {
+ client_name += "T";
+ service_if_name += "T";
+ indent(f_header_) <<
+ "template <class Protocol_>" << endl;
+ }
+ indent(f_header_) << "class " << client_name << ";" <<
+ endl << endl;
}
string extends = "";
if (tservice->get_extends() != NULL) {
- extends = " : virtual public " + type_name(tservice->get_extends()) + style + "If";
+ extends = " : virtual public " + type_name(tservice->get_extends()) +
+ style + "If";
+ if (style == "CobCl" && gen_templates_) {
+ // TODO(simpkins): If gen_templates_ is enabled, we currently assume all
+ // parent services were also generated with templates enabled.
+ extends += "T<Protocol_>";
+ }
+ }
+
+ if (style == "CobCl" && gen_templates_) {
+ f_header_ << "template <class Protocol_>" << endl;
}
f_header_ <<
- "class " << service_name_ << style << "If" << extends << " {" << endl <<
+ "class " << service_if_name << extends << " {" << endl <<
" public:" << endl;
indent_up();
f_header_ <<
- indent() << "virtual ~" << service_name_ << style << "If" << "() {}" << endl;
+ indent() << "virtual ~" << service_if_name << "() {}" << endl;
vector<t_function*> functions = tservice->get_functions();
vector<t_function*>::iterator f_iter;
@@ -1457,6 +1615,16 @@
indent_down();
f_header_ <<
"};" << endl << endl;
+
+ if (style == "CobCl" && gen_templates_) {
+ // generate a backwards-compatible typedef for clients that do not
+ // know about the new template-style code
+ f_header_ <<
+ "typedef " << service_if_name <<
+ "< ::apache::thrift::protocol::TProtocol> " <<
+ service_name_ << style << "If;" <<
+ endl << endl;
+ }
}
/**
@@ -1756,24 +1924,49 @@
ifstyle = "CobCl";
}
+ std::ofstream& out = (gen_templates_ ? f_service_tcc_ : f_service_);
+ string template_header, template_suffix, short_suffix, protocol_type, _this;
+ string const prot_factory_type =
+ "::apache::thrift::protocol::TProtocolFactory";
+ if (gen_templates_) {
+ template_header = "template <class Protocol_>\n";
+ short_suffix = "T";
+ template_suffix = "T<Protocol_>";
+ protocol_type = "Protocol_";
+ _this = "this->";
+ } else {
+ protocol_type = "::apache::thrift::protocol::TProtocol";
+ }
+ string prot_ptr = "boost::shared_ptr< " + protocol_type + ">";
+ string client_suffix = "Client" + template_suffix;
+ string if_suffix = "If";
+ if (style == "Cob") {
+ if_suffix += template_suffix;
+ }
+
string extends = "";
string extends_client = "";
if (tservice->get_extends() != NULL) {
+ // TODO(simpkins): If gen_templates_ is enabled, we currently assume all
+ // parent services were also generated with templates enabled.
extends = type_name(tservice->get_extends());
- extends_client = ", public " + extends + style + "Client";
+ extends_client = ", public " + extends + style + client_suffix;
}
// Generate the header portion
f_header_ <<
- "class " << service_name_ << style << "Client : " <<
- "virtual public " << service_name_ << ifstyle << "If" <<
+ template_header <<
+ "class " << service_name_ << style << "Client" << short_suffix << " : " <<
+ "virtual public " << service_name_ << ifstyle << if_suffix <<
extends_client << " {" << endl <<
" public:" << endl;
indent_up();
if (style != "Cob") {
f_header_ <<
- indent() << service_name_ << style << "Client(boost::shared_ptr< ::apache::thrift::protocol::TProtocol> prot) :" << endl;
+ indent() << service_name_ << style << "Client" << short_suffix <<
+ "(" << prot_ptr << " prot) :" <<
+ endl;
if (extends.empty()) {
f_header_ <<
indent() << " piprot_(prot)," << endl <<
@@ -1783,16 +1976,14 @@
indent() << "}" << endl;
} else {
f_header_ <<
- indent() << " " << extends << style << "Client(prot, prot) {}" << endl;
+ indent() << " " << extends << style << client_suffix <<
+ "(prot, prot) {}" << endl;
}
f_header_ <<
- indent() << service_name_ << style << "Client(boost::shared_ptr< ::apache::thrift::protocol::TProtocol> iprot, boost::shared_ptr< ::apache::thrift::protocol::TProtocol> oprot) :" << endl;
+ indent() << service_name_ << style << "Client" << short_suffix <<
+ "(" << prot_ptr << " iprot, " << prot_ptr << " oprot) :" << endl;
if (extends.empty()) {
- if (style == "Cob") {
- f_header_ <<
- indent() << " rpc_ctx_(ctx)," << endl;
- }
f_header_ <<
indent() << " piprot_(iprot)," << endl <<
indent() << " poprot_(oprot) {" << endl <<
@@ -1801,46 +1992,72 @@
indent() << "}" << endl;
} else {
f_header_ <<
- indent() << " " << extends << style << "Client(iprot, oprot) {}" << endl;
+ indent() << " " << extends << style << client_suffix <<
+ "(iprot, oprot) {}" << endl;
}
-
+
// Generate getters for the protocols.
+ // Note that these are not currently templated for simplicity.
+ // TODO(simpkins): should they be templated?
f_header_ <<
indent() << "boost::shared_ptr< ::apache::thrift::protocol::TProtocol> getInputProtocol() {" << endl <<
- indent() << " return piprot_;" << endl <<
+ indent() << " return " << _this << "piprot_;" << endl <<
indent() << "}" << endl;
-
+
f_header_ <<
indent() << "boost::shared_ptr< ::apache::thrift::protocol::TProtocol> getOutputProtocol() {" << endl <<
- indent() << " return poprot_;" << endl <<
+ indent() << " return " << _this << "poprot_;" << endl <<
indent() << "}" << endl;
} else /* if (style == "Cob") */ {
- // Generate getters for the protocols.
f_header_ <<
- indent() << service_name_ << style << "Client("
- << "boost::shared_ptr< ::apache::thrift::async::TAsyncChannel> channel, "
- << "::apache::thrift::protocol::TProtocolFactory* protocolFactory) :" << endl;
+ indent() << service_name_ << style << "Client" << short_suffix << "(" <<
+ "boost::shared_ptr< ::apache::thrift::async::TAsyncChannel> channel, " <<
+ "::apache::thrift::protocol::TProtocolFactory* protocolFactory) :" <<
+ endl;
if (extends.empty()) {
f_header_ <<
indent() << " channel_(channel)," << endl <<
indent() << " itrans_(new ::apache::thrift::transport::TMemoryBuffer())," << endl <<
- indent() << " otrans_(new ::apache::thrift::transport::TMemoryBuffer())," << endl <<
- indent() << " piprot_(protocolFactory->getProtocol(itrans_))," << endl <<
- indent() << " poprot_(protocolFactory->getProtocol(otrans_)) {" << endl <<
+ indent() << " otrans_(new ::apache::thrift::transport::TMemoryBuffer())," << endl;
+ if (gen_templates_) {
+ // TProtocolFactory classes return generic TProtocol pointers.
+ // We have to dynamic cast to the Protocol_ type we are expecting.
+ f_header_ <<
+ indent() << " piprot_(boost::dynamic_pointer_cast<Protocol_>(" <<
+ "protocolFactory->getProtocol(itrans_)))," << endl <<
+ indent() << " poprot_(boost::dynamic_pointer_cast<Protocol_>(" <<
+ "protocolFactory->getProtocol(otrans_))) {" << endl;
+ // Throw a TException if either dynamic cast failed.
+ f_header_ <<
+ indent() << " if (!piprot_ || !poprot_) {" << endl <<
+ indent() << " throw ::apache::thrift::TException(\"" <<
+ "TProtocolFactory returned unexpected protocol type in " <<
+ service_name_ << style << "Client" << short_suffix <<
+ " constructor\");" << endl <<
+ indent() << " }" << endl;
+ } else {
+ f_header_ <<
+ indent() << " piprot_(protocolFactory->getProtocol(itrans_))," <<
+ endl <<
+ indent() << " poprot_(protocolFactory->getProtocol(otrans_)) {" <<
+ endl;
+ }
+ f_header_ <<
indent() << " iprot_ = piprot_.get();" << endl <<
indent() << " oprot_ = poprot_.get();" << endl <<
indent() << "}" << endl;
} else {
f_header_ <<
- indent() << " " << extends << style << "Client(channel, protocolFactory) {}" << endl;
+ indent() << " " << extends << style << client_suffix <<
+ "(channel, protocolFactory) {}" << endl;
}
}
if (style == "Cob") {
f_header_ <<
indent() << "boost::shared_ptr< ::apache::thrift::async::TAsyncChannel> getChannel() {" << endl <<
- indent() << " return channel_;" << endl <<
+ indent() << " return " << _this << "channel_;" << endl <<
indent() << "}" << endl;
if (!gen_no_client_completion_) {
f_header_ <<
@@ -1879,10 +2096,10 @@
indent() << "boost::shared_ptr< ::apache::thrift::transport::TMemoryBuffer> otrans_;" << endl;
}
f_header_ <<
- indent() << "boost::shared_ptr< ::apache::thrift::protocol::TProtocol> piprot_;" << endl <<
- indent() << "boost::shared_ptr< ::apache::thrift::protocol::TProtocol> poprot_;" << endl <<
- indent() << "::apache::thrift::protocol::TProtocol* iprot_;" << endl <<
- indent() << "::apache::thrift::protocol::TProtocol* oprot_;" << endl;
+ indent() << prot_ptr << " piprot_;" << endl <<
+ indent() << prot_ptr << " poprot_;" << endl <<
+ indent() << protocol_type << "* iprot_;" << endl <<
+ indent() << protocol_type << "* oprot_;" << endl;
indent_down();
}
@@ -1891,17 +2108,30 @@
"};" << endl <<
endl;
- string scope = service_name_ + style + "Client::";
+ if (gen_templates_) {
+ // Output a backwards compatibility typedef using
+ // TProtocol as the template parameter.
+ f_header_ <<
+ "typedef " << service_name_ << style <<
+ "ClientT< ::apache::thrift::protocol::TProtocol> " <<
+ service_name_ << style << "Client;" << endl <<
+ endl;
+ }
+
+ string scope = service_name_ + style + client_suffix + "::";
// Generate client method implementations
for (f_iter = functions.begin(); f_iter != functions.end(); ++f_iter) {
string funname = (*f_iter)->get_name();
// Open function
- indent(f_service_) <<
+ if (gen_templates_) {
+ indent(out) << template_header;
+ }
+ indent(out) <<
function_signature(*f_iter, ifstyle, scope) << endl;
- scope_up(f_service_);
- indent(f_service_) <<
+ scope_up(out);
+ indent(out) <<
"send_" << funname << "(";
// Get the struct of function call params
@@ -1915,40 +2145,40 @@
if (first) {
first = false;
} else {
- f_service_ << ", ";
+ out << ", ";
}
- f_service_ << (*fld_iter)->get_name();
+ out << (*fld_iter)->get_name();
}
- f_service_ << ");" << endl;
+ out << ");" << endl;
if (style != "Cob") {
if (!(*f_iter)->is_oneway()) {
- f_service_ << indent();
+ out << indent();
if (!(*f_iter)->get_returntype()->is_void()) {
if (is_complex_type((*f_iter)->get_returntype())) {
- f_service_ << "recv_" << funname << "(_return);" << endl;
+ out << "recv_" << funname << "(_return);" << endl;
} else {
- f_service_ << "return recv_" << funname << "();" << endl;
+ out << "return recv_" << funname << "();" << endl;
}
} else {
- f_service_ <<
+ out <<
"recv_" << funname << "();" << endl;
}
}
} else {
if (!(*f_iter)->is_oneway()) {
- f_service_ <<
+ out <<
indent() << _this << "channel_->sendAndRecvMessage(" <<
"std::tr1::bind(cob, this), " << _this << "otrans_.get(), " <<
_this << "itrans_.get());" << endl;
} else {
- f_service_ <<
+ out <<
indent() << _this << "channel_->sendMessage(" <<
"std::tr1::bind(cob, this), " << _this << "otrans_.get());" << endl;
}
}
- scope_down(f_service_);
- f_service_ << endl;
+ scope_down(out);
+ out << endl;
//if (style != "Cob") // TODO(dreiss): Libify the client and don't generate this for cob-style
if (true) {
@@ -1958,35 +2188,40 @@
(*f_iter)->get_arglist());
// Open the send function
- indent(f_service_) <<
+ if (gen_templates_) {
+ indent(out) << template_header;
+ }
+ indent(out) <<
function_signature(&send_function, "", scope) << endl;
- scope_up(f_service_);
+ scope_up(out);
// Function arguments and results
string argsname = tservice->get_name() + "_" + (*f_iter)->get_name() + "_pargs";
string resultname = tservice->get_name() + "_" + (*f_iter)->get_name() + "_presult";
// Serialize the request
- f_service_ <<
+ out <<
indent() << "int32_t cseqid = 0;" << endl <<
- indent() << "oprot_->writeMessageBegin(\"" << (*f_iter)->get_name() << "\", ::apache::thrift::protocol::T_CALL, cseqid);" << endl <<
+ indent() << _this << "oprot_->writeMessageBegin(\"" <<
+ (*f_iter)->get_name() <<
+ "\", ::apache::thrift::protocol::T_CALL, cseqid);" << endl <<
endl <<
indent() << argsname << " args;" << endl;
for (fld_iter = fields.begin(); fld_iter != fields.end(); ++fld_iter) {
- f_service_ <<
+ out <<
indent() << "args." << (*fld_iter)->get_name() << " = &" << (*fld_iter)->get_name() << ";" << endl;
}
- f_service_ <<
- indent() << "args.write(oprot_);" << endl <<
+ out <<
+ indent() << "args.write(" << _this << "oprot_);" << endl <<
endl <<
- indent() << "oprot_->writeMessageEnd();" << endl <<
- indent() << "oprot_->getTransport()->writeEnd();" << endl <<
- indent() << "oprot_->getTransport()->flush();" << endl;
+ indent() << _this << "oprot_->writeMessageEnd();" << endl <<
+ indent() << _this << "oprot_->getTransport()->writeEnd();" << endl <<
+ indent() << _this << "oprot_->getTransport()->flush();" << endl;
- scope_down(f_service_);
- f_service_ << endl;
+ scope_down(out);
+ out << endl;
// Generate recv function only if not an oneway function
if (!(*f_iter)->is_oneway()) {
@@ -1994,104 +2229,112 @@
t_function recv_function((*f_iter)->get_returntype(),
string("recv_") + (*f_iter)->get_name(),
&noargs);
- // Open function
- indent(f_service_) <<
+ // Open the recv function
+ if (gen_templates_) {
+ indent(out) << template_header;
+ }
+ indent(out) <<
function_signature(&recv_function, "", scope) << endl;
- scope_up(f_service_);
+ scope_up(out);
- f_service_ <<
+ out <<
endl <<
indent() << "int32_t rseqid = 0;" << endl <<
indent() << "std::string fname;" << endl <<
indent() << "::apache::thrift::protocol::TMessageType mtype;" << endl;
if (style == "Cob" && !gen_no_client_completion_) {
- f_service_ <<
+ out <<
indent() << "bool completed = false;" << endl << endl <<
indent() << "try {";
indent_up();
}
- f_service_ << endl <<
- indent() << "iprot_->readMessageBegin(fname, mtype, rseqid);" << endl <<
+ out << endl <<
+ indent() << _this << "iprot_->readMessageBegin(fname, mtype, rseqid);" << endl <<
indent() << "if (mtype == ::apache::thrift::protocol::T_EXCEPTION) {" << endl <<
indent() << " ::apache::thrift::TApplicationException x;" << endl <<
- indent() << " x.read(iprot_);" << endl <<
- indent() << " iprot_->readMessageEnd();" << endl <<
- indent() << " iprot_->getTransport()->readEnd();" << endl;
+ indent() << " x.read(" << _this << "iprot_);" << endl <<
+ indent() << " " << _this << "iprot_->readMessageEnd();" << endl <<
+ indent() << " " << _this << "iprot_->getTransport()->readEnd();" <<
+ endl;
if (style == "Cob" && !gen_no_client_completion_) {
- f_service_ <<
+ out <<
indent() << " completed = true;" << endl <<
indent() << " completed__(true);" << endl;
}
- f_service_ <<
+ out <<
indent() << " throw x;" << endl <<
indent() << "}" << endl <<
indent() << "if (mtype != ::apache::thrift::protocol::T_REPLY) {" << endl <<
- indent() << " iprot_->skip(::apache::thrift::protocol::T_STRUCT);" << endl <<
- indent() << " iprot_->readMessageEnd();" << endl <<
- indent() << " iprot_->getTransport()->readEnd();" << endl;
+ indent() << " " << _this << "iprot_->skip(" <<
+ "::apache::thrift::protocol::T_STRUCT);" << endl <<
+ indent() << " " << _this << "iprot_->readMessageEnd();" << endl <<
+ indent() << " " << _this << "iprot_->getTransport()->readEnd();" <<
+ endl;
if (style == "Cob" && !gen_no_client_completion_) {
- f_service_ <<
+ out <<
indent() << " completed = true;" << endl <<
indent() << " completed__(false);" << endl;
}
- f_service_ <<
+ out <<
indent() << "}" << endl <<
indent() << "if (fname.compare(\"" << (*f_iter)->get_name() << "\") != 0) {" << endl <<
- indent() << " iprot_->skip(::apache::thrift::protocol::T_STRUCT);" << endl <<
- indent() << " iprot_->readMessageEnd();" << endl <<
- indent() << " iprot_->getTransport()->readEnd();" << endl;
+ indent() << " " << _this << "iprot_->skip(" <<
+ "::apache::thrift::protocol::T_STRUCT);" << endl <<
+ indent() << " " << _this << "iprot_->readMessageEnd();" << endl <<
+ indent() << " " << _this << "iprot_->getTransport()->readEnd();" <<
+ endl;
if (style == "Cob" && !gen_no_client_completion_) {
- f_service_ <<
+ out <<
indent() << " completed = true;" << endl <<
indent() << " completed__(false);" << endl;
}
- f_service_ <<
+ out <<
indent() << "}" << endl;
if (!(*f_iter)->get_returntype()->is_void() &&
!is_complex_type((*f_iter)->get_returntype())) {
t_field returnfield((*f_iter)->get_returntype(), "_return");
- f_service_ <<
+ out <<
indent() << declare_field(&returnfield) << endl;
}
- f_service_ <<
+ out <<
indent() << resultname << " result;" << endl;
if (!(*f_iter)->get_returntype()->is_void()) {
- f_service_ <<
+ out <<
indent() << "result.success = &_return;" << endl;
}
- f_service_ <<
- indent() << "result.read(iprot_);" << endl <<
- indent() << "iprot_->readMessageEnd();" << endl <<
- indent() << "iprot_->getTransport()->readEnd();" << endl <<
+ out <<
+ indent() << "result.read(" << _this << "iprot_);" << endl <<
+ indent() << _this << "iprot_->readMessageEnd();" << endl <<
+ indent() << _this << "iprot_->getTransport()->readEnd();" << endl <<
endl;
// Careful, only look for _result if not a void function
if (!(*f_iter)->get_returntype()->is_void()) {
if (is_complex_type((*f_iter)->get_returntype())) {
- f_service_ <<
+ out <<
indent() << "if (result.__isset.success) {" << endl <<
indent() << " // _return pointer has now been filled" << endl;
if (style == "Cob" && !gen_no_client_completion_) {
- f_service_ <<
+ out <<
indent() << " completed = true;" << endl <<
indent() << " completed__(true);" << endl;
}
- f_service_ <<
+ out <<
indent() << " return;" << endl <<
indent() << "}" << endl;
} else {
- f_service_ <<
+ out <<
indent() << "if (result.__isset.success) {" << endl;
if (style == "Cob" && !gen_no_client_completion_) {
- f_service_ <<
+ out <<
indent() << " completed = true;" << endl <<
indent() << " completed__(true);" << endl;
}
- f_service_ <<
+ out <<
indent() << " return _return;" << endl <<
indent() << "}" << endl;
}
@@ -2101,14 +2344,14 @@
const std::vector<t_field*>& xceptions = xs->get_members();
vector<t_field*>::const_iterator x_iter;
for (x_iter = xceptions.begin(); x_iter != xceptions.end(); ++x_iter) {
- f_service_ <<
+ out <<
indent() << "if (result.__isset." << (*x_iter)->get_name() << ") {" << endl;
if (style == "Cob" && !gen_no_client_completion_) {
- f_service_ <<
+ out <<
indent() << " completed = true;" << endl <<
indent() << " completed__(true);" << endl;
}
- f_service_ <<
+ out <<
indent() << " throw result." << (*x_iter)->get_name() << ";" << endl <<
indent() << "}" << endl;
}
@@ -2116,24 +2359,24 @@
// We only get here if we are a void function
if ((*f_iter)->get_returntype()->is_void()) {
if (style == "Cob" && !gen_no_client_completion_) {
- f_service_ <<
+ out <<
indent() << "completed = true;" << endl <<
indent() << "completed__(true);" << endl;
}
- indent(f_service_) <<
+ indent(out) <<
"return;" << endl;
} else {
if (style == "Cob" && !gen_no_client_completion_) {
- f_service_ <<
+ out <<
indent() << "completed = true;" << endl <<
indent() << "completed__(true);" << endl;
}
- f_service_ <<
+ out <<
indent() << "throw ::apache::thrift::TApplicationException(::apache::thrift::TApplicationException::MISSING_RESULT, \"" << (*f_iter)->get_name() << " failed: unknown result\");" << endl;
}
if (style == "Cob" && !gen_no_client_completion_) {
indent_down();
- f_service_ <<
+ out <<
indent() << "} catch (...) {" << endl <<
indent() << " if (!completed) {" << endl <<
indent() << " completed__(false);" << endl <<
@@ -2142,17 +2385,17 @@
indent() << "}" << endl;
}
// Close function
- scope_down(f_service_);
- f_service_ << endl;
+ scope_down(out);
+ out << endl;
}
}
}
}
/**
- * Generates a service server definition.
+ * Generates a service processor definition.
*
- * @param tservice The service to generate a server for.
+ * @param tservice The service to generate a processor for.
*/
void t_cpp_generator::generate_service_processor(t_service* tservice, string style) {
string ifstyle;
@@ -2177,14 +2420,29 @@
string extends = "";
string extends_processor = "";
if (tservice->get_extends() != NULL) {
- extends = type_name(tservice->get_extends());
- extends_processor = ", public " + extends + pstyle + "Processor";
+ extends = type_name(tservice->get_extends()) + pstyle + "Processor";
+ if (gen_templates_) {
+ // TODO(simpkins): If gen_templates_ is enabled, we currently assume all
+ // parent services were also generated with templates enabled.
+ extends += "T<Protocol_>";
+ }
+ extends_processor = ", public " + extends;
+ }
+
+ string template_header, template_suffix, typename_str;
+ string class_suffix = "Processor";
+ if (gen_templates_) {
+ class_suffix = "ProcessorT";
+ template_header = "template <class Protocol_>\n";
+ template_suffix = "<Protocol_>";
+ typename_str = "typename ";
}
// Generate the header portion
f_header_ <<
- "class " << service_name_ << pstyle << "Processor : " <<
- "virtual public ::apache::thrift::T" << pstyle << "Processor" <<
+ template_header <<
+ "class " << service_name_ << pstyle << class_suffix <<
+ " : virtual public ::apache::thrift::T" << pstyle << "Processor" <<
extends_processor << " {" << endl;
// Protected data members
@@ -2202,19 +2460,47 @@
" private:" << endl;
indent_up();
f_header_ <<
- indent() << "std::map<std::string, void (" << service_name_ << pstyle << "Processor::*)(" << finish_cob_decl << "int32_t, ::apache::thrift::protocol::TProtocol*, ::apache::thrift::protocol::TProtocol*)> processMap_;" << endl;
+ indent() << "std::map<std::string, void (" <<
+ service_name_ << pstyle << class_suffix << "::*)(" << finish_cob_decl <<
+ "int32_t, ::apache::thrift::protocol::TProtocol*, " <<
+ "::apache::thrift::protocol::TProtocol*)> processMap_;" << endl;
for (f_iter = functions.begin(); f_iter != functions.end(); ++f_iter) {
indent(f_header_) <<
"void process_" << (*f_iter)->get_name() << "(" << finish_cob << "int32_t seqid, ::apache::thrift::protocol::TProtocol* iprot, ::apache::thrift::protocol::TProtocol* oprot);" << endl;
+ if (gen_templates_) {
+ indent(f_header_) <<
+ "void process_" << (*f_iter)->get_name() << "(" << finish_cob <<
+ "int32_t seqid, Protocol_* iprot, Protocol_* oprot);" << endl;
+ }
if (style == "Cob") {
// XXX Factor this out, even if it is a pain.
string ret_arg = ((*f_iter)->get_returntype()->is_void()
? ""
: ", const " + type_name((*f_iter)->get_returntype()) + "& _return");
+ f_header_ <<
+ indent() << "void return_" << (*f_iter)->get_name() <<
+ "(std::tr1::function<void(bool ok)> cob, int32_t seqid, " <<
+ "::apache::thrift::protocol::TProtocol* oprot, " <<
+ "void* ctx" << ret_arg << ");" << endl;
+ if (gen_templates_) {
+ f_header_ <<
+ indent() << "void return_" << (*f_iter)->get_name() <<
+ "(std::tr1::function<void(bool ok)> cob, int32_t seqid, " <<
+ "Protocol_* oprot, void* ctx" << ret_arg << ");" << endl;
+ }
// XXX Don't declare throw if it doesn't exist
f_header_ <<
- "void return_" << (*f_iter)->get_name() << "(std::tr1::function<void(bool ok)> cob, int32_t seqid, ::apache::thrift::protocol::TProtocol* oprot, void* ctx" << ret_arg << ");" << endl <<
- "void throw_" << (*f_iter)->get_name() << "(std::tr1::function<void(bool ok)> cob, int32_t seqid, ::apache::thrift::protocol::TProtocol* oprot, void* ctx, ::apache::thrift::TDelayedException* _throw);" << endl;
+ indent() << "void throw_" << (*f_iter)->get_name() <<
+ "(std::tr1::function<void(bool ok)> cob, int32_t seqid, " <<
+ "::apache::thrift::protocol::TProtocol* oprot, void* ctx, " <<
+ "::apache::thrift::TDelayedException* _throw);" << endl;
+ if (gen_templates_) {
+ f_header_ <<
+ indent() << "void throw_" << (*f_iter)->get_name() <<
+ "(std::tr1::function<void(bool ok)> cob, int32_t seqid, " <<
+ "Protocol_* oprot, void* ctx, " <<
+ "::apache::thrift::TDelayedException* _throw);" << endl;
+ }
}
}
indent_down();
@@ -2230,7 +2516,8 @@
declare_map += "\"] = &";
declare_map += service_name_;
declare_map += pstyle;
- declare_map += "Processor::process_";
+ declare_map += class_suffix;
+ declare_map += "::process_";
declare_map += (*f_iter)->get_name();
declare_map += ";\n";
}
@@ -2238,13 +2525,14 @@
f_header_ <<
" public:" << endl <<
- indent() << service_name_ << pstyle << "Processor(boost::shared_ptr<" << service_name_ << ifstyle << "If> iface) :" << endl;
+ indent() << service_name_ << pstyle << class_suffix <<
+ "(boost::shared_ptr<" << service_name_ << ifstyle << "If> iface) :" << endl;
if (extends.empty()) {
f_header_ <<
indent() << " iface_(iface) {" << endl;
} else {
f_header_ <<
- indent() << " " << extends << pstyle << "Processor(iface)," << endl <<
+ indent() << " " << extends << "(iface)," << endl <<
indent() << " iface_(iface) {" << endl;
}
f_header_ <<
@@ -2252,17 +2540,24 @@
indent() << "}" << endl <<
endl <<
indent() << "virtual " << ret_type << "process(" << finish_cob << "boost::shared_ptr< ::apache::thrift::protocol::TProtocol> piprot, boost::shared_ptr< ::apache::thrift::protocol::TProtocol> poprot);" << endl <<
- indent() << "virtual ~" << service_name_ << pstyle << "Processor() {}" << endl;
+ indent() << "virtual ~" << service_name_ << pstyle << class_suffix <<
+ "() {}" << endl;
indent_down();
f_header_ <<
"};" << endl << endl;
// Generate the server implementation
- f_service_ <<
- ret_type << service_name_ << pstyle << "Processor::process(" << finish_cob << "boost::shared_ptr< ::apache::thrift::protocol::TProtocol> piprot, boost::shared_ptr< ::apache::thrift::protocol::TProtocol> poprot) {" << endl;
+ std::ofstream& out = (gen_templates_ ? f_service_tcc_ : f_service_);
+ out <<
+ template_header <<
+ ret_type << service_name_ << pstyle << class_suffix << template_suffix <<
+ "::process(" << finish_cob <<
+ "boost::shared_ptr< ::apache::thrift::protocol::TProtocol> piprot, " <<
+ "boost::shared_ptr< ::apache::thrift::protocol::TProtocol> poprot) {" <<
+ endl;
indent_up();
- f_service_ <<
+ out <<
endl <<
indent() << "::apache::thrift::protocol::TProtocol* iprot = piprot.get();" << endl <<
indent() << "::apache::thrift::protocol::TProtocol* oprot = poprot.get();" << endl <<
@@ -2290,21 +2585,29 @@
endl;
indent_down();
- f_service_ <<
+ out <<
indent() << "}" << endl <<
endl;
- f_service_ <<
- ret_type << service_name_ << pstyle << "Processor::process_fn(" << finish_cob << "::apache::thrift::protocol::TProtocol* iprot, ::apache::thrift::protocol::TProtocol* oprot, std::string& fname, int32_t seqid) {" << endl;
+ out <<
+ template_header <<
+ ret_type << service_name_ << pstyle << class_suffix << template_suffix <<
+ "::process_fn(" << finish_cob <<
+ "::apache::thrift::protocol::TProtocol* iprot, " <<
+ "::apache::thrift::protocol::TProtocol* oprot, " <<
+ "std::string& fname, int32_t seqid) {" << endl;
indent_up();
// HOT: member function pointer map
- f_service_ <<
- indent() << "std::map<std::string, void (" << service_name_ << pstyle << "Processor::*)(" << finish_cob_decl << "int32_t, ::apache::thrift::protocol::TProtocol*, ::apache::thrift::protocol::TProtocol*)>::iterator pfn;" << endl <<
+ out <<
+ indent() << typename_str << "std::map<std::string, void (" <<
+ service_name_ << pstyle << class_suffix << "::*)(" << finish_cob_decl <<
+ "int32_t, ::apache::thrift::protocol::TProtocol*, " <<
+ "::apache::thrift::protocol::TProtocol*)>::iterator pfn;" << endl <<
indent() << "pfn = processMap_.find(fname);" << endl <<
indent() << "if (pfn == processMap_.end()) {" << endl;
if (extends.empty()) {
- f_service_ <<
+ out <<
indent() << " iprot->skip(::apache::thrift::protocol::T_STRUCT);" << endl <<
indent() << " iprot->readMessageEnd();" << endl <<
indent() << " iprot->getTransport()->readEnd();" << endl <<
@@ -2316,33 +2619,54 @@
indent() << " oprot->getTransport()->flush();" << endl <<
indent() << (style == "Cob" ? " return cob(true);" : " return true;") << endl;
} else {
- f_service_ <<
+ out <<
indent() << " return "
- << extends << pstyle << "Processor::process_fn("
+ << extends << "::process_fn("
<< (style == "Cob" ? "cob, " : "")
<< "iprot, oprot, fname, seqid);" << endl;
}
- f_service_ <<
+ out <<
indent() << "}" << endl <<
indent() << "(this->*(pfn->second))(" << cob_arg << "seqid, iprot, oprot);" << endl;
// TODO(dreiss): return pfn ret?
if (style == "Cob") {
- f_service_ <<
+ out <<
indent() << "return;" << endl;
} else {
- f_service_ <<
+ out <<
indent() << "return true;" << endl;
}
indent_down();
- f_service_ <<
+ out <<
"}" << endl <<
endl;
// Generate the process subfunctions
for (f_iter = functions.begin(); f_iter != functions.end(); ++f_iter) {
- generate_process_function(tservice, *f_iter, style);
+ if (gen_templates_) {
+ generate_process_function(tservice, *f_iter, style, false);
+ generate_process_function(tservice, *f_iter, style, true);
+ } else {
+ generate_process_function(tservice, *f_iter, style, false);
+ }
+ }
+
+ if (gen_templates_) {
+ // Generate a backwards compatible typedef, for callers who don't know
+ // about the new template-style code.
+ //
+ // We can't use TProtocol as the template parameter, since ProcessorT
+ // provides overloaded versions of most methods, one of which accepts
+ // TProtocol pointers, and one which accepts Protocol_ pointers. This
+ // results in a compile error if instantiated with Protocol_ == TProtocol.
+ // Therefore, we define TDummyProtocol solely so we can use it as the
+ // template parameter here.
+ f_header_ <<
+ "typedef " << service_name_ << pstyle <<
+ "ProcessorT< ::apache::thrift::protocol::TDummyProtocol> " <<
+ service_name_ << pstyle << "Processor;" << endl << endl;
}
}
@@ -2357,6 +2681,8 @@
return;
}
+ std::ofstream& out = (gen_templates_ ? f_service_tcc_ : f_service_);
+
t_struct result(program_, tservice->get_name() + "_" + tfunction->get_name() + "_result");
t_field success(tfunction->get_returntype(), "success", 0);
if (!tfunction->get_returntype()->is_void()) {
@@ -2371,14 +2697,14 @@
}
generate_struct_definition(f_header_, &result, false);
- generate_struct_reader(f_service_, &result);
- generate_struct_result_writer(f_service_, &result);
+ generate_struct_reader(out, &result);
+ generate_struct_result_writer(out, &result);
result.set_name(tservice->get_name() + "_" + tfunction->get_name() + "_presult");
generate_struct_definition(f_header_, &result, false, true, true, gen_cob_style_);
- generate_struct_reader(f_service_, &result, true);
+ generate_struct_reader(out, &result, true);
if (gen_cob_style_) {
- generate_struct_writer(f_service_, &result, true);
+ generate_struct_writer(out, &result, true);
}
}
@@ -2390,7 +2716,8 @@
*/
void t_cpp_generator::generate_process_function(t_service* tservice,
t_function* tfunction,
- string style) {
+ string style,
+ bool specialized) {
t_struct* arg_struct = tfunction->get_arglist();
const std::vector<t_field*>& fields = arg_struct->get_members();
vector<t_field*>::const_iterator f_iter;
@@ -2400,18 +2727,46 @@
vector<t_field*>::const_iterator x_iter;
string service_func_name = tservice->get_name() + "." + tfunction->get_name();
+ std::ofstream& out = (gen_templates_ ? f_service_tcc_ : f_service_);
+
+ string prot_type =
+ (specialized ? "Protocol_" : "::apache::thrift::protocol::TProtocol");
+ string class_suffix;
+ if (gen_templates_) {
+ class_suffix = "T<Protocol_>";
+ }
+
// I tried to do this as one function. I really did. But it was too hard.
if (style != "Cob") {
// Open function
- f_service_ <<
- "void " << tservice->get_name() << "Processor::" <<
- "process_" << tfunction->get_name() << "(int32_t seqid, ::apache::thrift::protocol::TProtocol* iprot, ::apache::thrift::protocol::TProtocol* oprot)" << endl;
- scope_up(f_service_);
+ if (gen_templates_) {
+ out <<
+ indent() << "template <class Protocol_>" << endl;
+ }
+ out <<
+ "void " << tservice->get_name() << "Processor" << class_suffix << "::" <<
+ "process_" << tfunction->get_name() << "(int32_t seqid, " <<
+ prot_type << "* iprot, " << prot_type << "* oprot)" << endl;
+ scope_up(out);
+
+ if (gen_templates_ && !specialized) {
+ // If these are instances of Protocol_, instead of any old TProtocol,
+ // use the specialized process function instead.
+ out <<
+ indent() << "Protocol_* _iprot = dynamic_cast<Protocol_*>(iprot);" <<
+ endl <<
+ indent() << "Protocol_* _oprot = dynamic_cast<Protocol_*>(oprot);" <<
+ endl <<
+ indent() << "if (_iprot && _oprot) {" << endl <<
+ indent() << " return process_" << tfunction->get_name() <<
+ "(seqid, _iprot, _oprot);" << endl <<
+ indent() << "}" << endl << endl;
+ }
string argsname = tservice->get_name() + "_" + tfunction->get_name() + "_args";
string resultname = tservice->get_name() + "_" + tfunction->get_name() + "_result";
- f_service_ <<
+ out <<
indent() << "void* ctx = NULL;" << endl <<
indent() << "if (eventHandler_.get() != NULL) {" << endl <<
indent() << " ctx = eventHandler_->getContext(\"" << service_func_name << "\");" << endl <<
@@ -2431,101 +2786,101 @@
// Declare result
if (!tfunction->is_oneway()) {
- f_service_ <<
+ out <<
indent() << resultname << " result;" << endl;
}
// Try block for functions with exceptions
- f_service_ <<
+ out <<
indent() << "try {" << endl;
indent_up();
// Generate the function call
bool first = true;
- f_service_ << indent();
+ out << indent();
if (!tfunction->is_oneway() && !tfunction->get_returntype()->is_void()) {
if (is_complex_type(tfunction->get_returntype())) {
first = false;
- f_service_ << "iface_->" << tfunction->get_name() << "(result.success";
+ out << "iface_->" << tfunction->get_name() << "(result.success";
} else {
- f_service_ << "result.success = iface_->" << tfunction->get_name() << "(";
+ out << "result.success = iface_->" << tfunction->get_name() << "(";
}
} else {
- f_service_ <<
+ out <<
"iface_->" << tfunction->get_name() << "(";
}
for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) {
if (first) {
first = false;
} else {
- f_service_ << ", ";
+ out << ", ";
}
- f_service_ << "args." << (*f_iter)->get_name();
+ out << "args." << (*f_iter)->get_name();
}
- f_service_ << ");" << endl;
+ out << ");" << endl;
// Set isset on success field
if (!tfunction->is_oneway() && !tfunction->get_returntype()->is_void()) {
- f_service_ <<
+ out <<
indent() << "result.__isset.success = true;" << endl;
}
indent_down();
- f_service_ << indent() << "}";
+ out << indent() << "}";
if (!tfunction->is_oneway()) {
for (x_iter = xceptions.begin(); x_iter != xceptions.end(); ++x_iter) {
- f_service_ << " catch (" << type_name((*x_iter)->get_type()) << " &" << (*x_iter)->get_name() << ") {" << endl;
+ out << " catch (" << type_name((*x_iter)->get_type()) << " &" << (*x_iter)->get_name() << ") {" << endl;
if (!tfunction->is_oneway()) {
indent_up();
- f_service_ <<
+ out <<
indent() << "result." << (*x_iter)->get_name() << " = " << (*x_iter)->get_name() << ";" << endl <<
indent() << "result.__isset." << (*x_iter)->get_name() << " = true;" << endl;
indent_down();
- f_service_ << indent() << "}";
+ out << indent() << "}";
} else {
- f_service_ << "}";
+ out << "}";
}
}
}
- f_service_ << " catch (const std::exception& e) {" << endl;
+ out << " catch (const std::exception& e) {" << endl;
indent_up();
- f_service_ <<
+ out <<
indent() << "if (eventHandler_.get() != NULL) {" << endl <<
indent() << " eventHandler_->handlerError(ctx, \"" << service_func_name << "\");" << endl <<
indent() << "}" << endl;
- if (!tfunction->is_oneway()) {
- f_service_ <<
- endl <<
+ if (!tfunction->is_oneway()) {
+ out <<
+ endl <<
indent() << "::apache::thrift::TApplicationException x(e.what());" << endl <<
indent() << "oprot->writeMessageBegin(\"" << tfunction->get_name() << "\", ::apache::thrift::protocol::T_EXCEPTION, seqid);" << endl <<
indent() << "x.write(oprot);" << endl <<
indent() << "oprot->writeMessageEnd();" << endl <<
indent() << "oprot->getTransport()->writeEnd();" << endl <<
indent() << "oprot->getTransport()->flush();" << endl;
- }
- f_service_ << indent() << "return;" << endl;
- indent_down();
- f_service_ << indent() << "}" << endl << endl;
+ }
+ out << indent() << "return;" << endl;
+ indent_down();
+ out << indent() << "}" << endl << endl;
// Shortcut out here for oneway functions
if (tfunction->is_oneway()) {
- f_service_ <<
+ out <<
indent() << "if (eventHandler_.get() != NULL) {" << endl <<
indent() << " eventHandler_->asyncComplete(ctx, \"" << service_func_name << "\");" << endl <<
indent() << "}" << endl << endl <<
indent() << "return;" << endl;
indent_down();
- f_service_ << "}" << endl <<
+ out << "}" << endl <<
endl;
return;
}
// Serialize the result into a struct
- f_service_ <<
+ out <<
indent() << "if (eventHandler_.get() != NULL) {" << endl <<
indent() << " eventHandler_->preWrite(ctx, \"" << service_func_name << "\");" << endl <<
indent() << "}" << endl << endl <<
@@ -2539,19 +2894,41 @@
indent() << "}" << endl;
// Close function
- scope_down(f_service_);
- f_service_ << endl;
+ scope_down(out);
+ out << endl;
}
// Cob style.
else {
// Processor entry point.
- f_service_ <<
- "void " << tservice->get_name() << "AsyncProcessor::" <<
- "process_" << tfunction->get_name() << "(std::tr1::function<void(bool ok)> cob, int32_t seqid, ::apache::thrift::protocol::TProtocol* iprot, ::apache::thrift::protocol::TProtocol* oprot)" << endl;
- scope_up(f_service_);
+ if (gen_templates_) {
+ out <<
+ indent() << "template <class Protocol_>" << endl;
+ }
+ out <<
+ "void " << tservice->get_name() << "AsyncProcessor" << class_suffix <<
+ "::process_" << tfunction->get_name() <<
+ "(std::tr1::function<void(bool ok)> cob, int32_t seqid, " <<
+ prot_type << "* iprot, " << prot_type << "* oprot)" << endl;
+ scope_up(out);
- f_service_ <<
+ // TODO(simpkins): we could try to consoldate this
+ // with the non-cob code above
+ if (gen_templates_ && !specialized) {
+ // If these are instances of Protocol_, instead of any old TProtocol,
+ // use the specialized process function instead.
+ out <<
+ indent() << "Protocol_* _iprot = dynamic_cast<Protocol_*>(iprot);" <<
+ endl <<
+ indent() << "Protocol_* _oprot = dynamic_cast<Protocol_*>(oprot);" <<
+ endl <<
+ indent() << "if (_iprot && _oprot) {" << endl <<
+ indent() << " return process_" << tfunction->get_name() <<
+ "(cob, seqid, _iprot, _oprot);" << endl <<
+ indent() << "}" << endl << endl;
+ }
+
+ out <<
indent() << tservice->get_name() + "_" + tfunction->get_name() + "_args" << " args;" << endl <<
indent() << "void* ctx = NULL;" << endl <<
indent() << "if (eventHandler_.get() != NULL) {" << endl <<
@@ -2560,7 +2937,7 @@
indent() << "::apache::thrift::TProcessorContextFreer freer(eventHandler_.get(), ctx, \"" << service_func_name << "\");" << endl << endl <<
indent() << "try {" << endl;
indent_up();
- f_service_ <<
+ out <<
indent() << "if (eventHandler_.get() != NULL) {" << endl <<
indent() << " eventHandler_->preRead(ctx, \"" << service_func_name << "\");" << endl <<
indent() << "}" << endl <<
@@ -2570,10 +2947,10 @@
indent() << "if (eventHandler_.get() != NULL) {" << endl <<
indent() << " eventHandler_->postRead(ctx, \"" << service_func_name << "\", bytes);" << endl <<
indent() << "}" << endl;
- scope_down(f_service_);
+ scope_down(out);
// TODO(dreiss): Handle TExceptions? Expose to server?
- f_service_ <<
+ out <<
indent() << "catch (const std::exception& exn) {" << endl <<
indent() << " if (eventHandler_.get() != NULL) {" << endl <<
indent() << " eventHandler_->handlerError(ctx, \"" << service_func_name << "\");" << endl <<
@@ -2582,72 +2959,118 @@
indent() << "}" << endl;
if (tfunction->is_oneway()) {
- f_service_ <<
+ out <<
indent() << "if (eventHandler_.get() != NULL) {" << endl <<
indent() << " eventHandler_->asyncComplete(ctx, \"" << service_func_name << "\");" << endl <<
indent() << "}" << endl;
}
// TODO(dreiss): Figure out a strategy for exceptions in async handlers.
- f_service_ <<
- indent() << "freer.unregister();" << endl <<
- indent() << "iface_->" << tfunction->get_name() << "(";
- indent_up(); indent_up();
+ out <<
+ indent() << "freer.unregister();" << endl;
if (tfunction->is_oneway()) {
// No return. Just hand off our cob.
// TODO(dreiss): Call the cob immediately?
- f_service_ <<
+ out <<
+ indent() << "iface_->" << tfunction->get_name() << "(" <<
"std::tr1::bind(cob, true)" << endl;
+ indent_up(); indent_up();
} else {
- f_service_ << endl;
- string ret_placeholder = ", std::tr1::placeholders::_1";
- string comma = "";
- if (tfunction->get_returntype()->is_void()) {
- ret_placeholder = "";
+ string ret_arg, ret_placeholder;
+ if (!tfunction->get_returntype()->is_void()) {
+ ret_arg = ", const " + type_name(tfunction->get_returntype()) +
+ "& _return";
+ ret_placeholder = ", std::tr1::placeholders::_1";
}
- f_service_ <<
- indent() << "std::tr1::bind(&" << tservice->get_name() << "AsyncProcessor::"
- << "return_" << tfunction->get_name()
- << ", this, cob, seqid, oprot, ctx" << ret_placeholder << ")";
+
+ // When gen_templates_ is true, the return_ and throw_ functions are
+ // overloaded. We have to declare pointers to them so that the compiler
+ // can resolve the correct overloaded version.
+ out <<
+ indent() << "void (" << tservice->get_name() << "AsyncProcessor" <<
+ class_suffix << "::*return_fn)(std::tr1::function<void(bool ok)> " <<
+ "cob, int32_t seqid, " << prot_type << "* oprot, void* ctx" <<
+ ret_arg << ") =" << endl;
+ out <<
+ indent() << " &" << tservice->get_name() << "AsyncProcessor" <<
+ class_suffix << "::return_" << tfunction->get_name() << ";" << endl;
if (!xceptions.empty()) {
- f_service_
- << ',' << endl <<
- indent() << "std::tr1::bind(&" << tservice->get_name() << "AsyncProcessor::"
- << "throw_" << tfunction->get_name()
- << ", this, cob, seqid, oprot, ctx, std::tr1::placeholders::_1)";
+ out <<
+ indent() << "void (" << tservice->get_name() << "AsyncProcessor" <<
+ class_suffix << "::*throw_fn)(std::tr1::function<void(bool ok)> " <<
+ "cob, int32_t seqid, " << prot_type << "* oprot, void* ctx, " <<
+ "::apache::thrift::TDelayedException* _throw) =" << endl;
+ out <<
+ indent() << " &" << tservice->get_name() << "AsyncProcessor" <<
+ class_suffix << "::throw_" << tfunction->get_name() << ";" << endl;
+ }
+
+ out <<
+ indent() << "iface_->" << tfunction->get_name() << "(" << endl;
+ indent_up(); indent_up();
+ out <<
+ indent() << "std::tr1::bind(return_fn, this, cob, seqid, oprot, ctx" <<
+ ret_placeholder << ")";
+ if (!xceptions.empty()) {
+ out
+ << ',' << endl <<
+ indent() << "std::tr1::bind(throw_fn, this, cob, seqid, oprot, " <<
+ "ctx, std::tr1::placeholders::_1)";
}
}
// XXX Whitespace cleanup.
for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) {
- f_service_
+ out
<< ',' << endl <<
indent() << "args." << (*f_iter)->get_name();
}
- f_service_ << ");" << endl;
+ out << ");" << endl;
indent_down(); indent_down();
- scope_down(f_service_);
- f_service_ << endl;
+ scope_down(out);
+ out << endl;
// Normal return.
if (!tfunction->is_oneway()) {
- string ret_arg = (tfunction->get_returntype()->is_void()
- ? ""
- : ", const " + type_name(tfunction->get_returntype()) + "& _return");
- f_service_ <<
- "void " << tservice->get_name() << "AsyncProcessor::" <<
- "return_" << tfunction->get_name() << "(std::tr1::function<void(bool ok)> cob, int32_t seqid, ::apache::thrift::protocol::TProtocol* oprot, void* ctx" << ret_arg << ')' << endl;
- scope_up(f_service_);
- f_service_ <<
+ string ret_arg_decl, ret_arg_name;
+ if (!tfunction->get_returntype()->is_void()) {
+ ret_arg_decl = ", const " + type_name(tfunction->get_returntype()) +
+ "& _return";
+ ret_arg_name = ", _return";
+ }
+ if (gen_templates_) {
+ out <<
+ indent() << "template <class Protocol_>" << endl;
+ }
+ out <<
+ "void " << tservice->get_name() << "AsyncProcessor" << class_suffix <<
+ "::return_" << tfunction->get_name() <<
+ "(std::tr1::function<void(bool ok)> cob, int32_t seqid, " <<
+ prot_type << "* oprot, void* ctx" << ret_arg_decl << ')' << endl;
+ scope_up(out);
+
+ if (gen_templates_ && !specialized) {
+ // If oprot is a Protocol_ instance,
+ // use the specialized return function instead.
+ out <<
+ indent() << "Protocol_* _oprot = dynamic_cast<Protocol_*>(oprot);" <<
+ endl <<
+ indent() << "if (_oprot) {" << endl <<
+ indent() << " return return_" << tfunction->get_name() <<
+ "(cob, seqid, _oprot, ctx" << ret_arg_name << ");" << endl <<
+ indent() << "}" << endl << endl;
+ }
+
+ out <<
indent() << tservice->get_name() << "_" << tfunction->get_name() << "_presult result;" << endl;
if (!tfunction->get_returntype()->is_void()) {
// The const_cast here is unfortunate, but it would be a pain to avoid,
// and we only do a write with this struct, which is const-safe.
- f_service_ <<
+ out <<
indent() << "result.success = const_cast<" << type_name(tfunction->get_returntype()) << "*>(&_return);" << endl <<
indent() << "result.__isset.success = true;" << endl;
}
// Serialize the result into a struct
- f_service_ <<
+ out <<
endl <<
indent() << "if (eventHandler_.get() != NULL) {" << endl <<
indent() << " ctx = eventHandler_->getContext(\"" << service_func_name << "\");" << endl <<
@@ -2665,38 +3088,58 @@
indent() << " eventHandler_->postWrite(ctx, \"" << service_func_name << "\", bytes);" << endl <<
indent() << "}" << endl <<
indent() << "return cob(true);" << endl;
- scope_down(f_service_);
- f_service_ << endl;
+ scope_down(out);
+ out << endl;
}
// Exception return.
if (!tfunction->is_oneway() && !xceptions.empty()) {
- f_service_ <<
- "void " << tservice->get_name() << "AsyncProcessor::" <<
- "throw_" << tfunction->get_name() << "(std::tr1::function<void(bool ok)> cob, int32_t seqid, ::apache::thrift::protocol::TProtocol* oprot, void* ctx, ::apache::thrift::TDelayedException* _throw)" << endl;
- scope_up(f_service_);
- f_service_ <<
+ if (gen_templates_) {
+ out <<
+ indent() << "template <class Protocol_>" << endl;
+ }
+ out <<
+ "void " << tservice->get_name() << "AsyncProcessor" << class_suffix <<
+ "::throw_" << tfunction->get_name() <<
+ "(std::tr1::function<void(bool ok)> cob, int32_t seqid, " <<
+ prot_type << "* oprot, void* ctx, " <<
+ "::apache::thrift::TDelayedException* _throw)" << endl;
+ scope_up(out);
+
+ if (gen_templates_ && !specialized) {
+ // If oprot is a Protocol_ instance,
+ // use the specialized throw function instead.
+ out <<
+ indent() << "Protocol_* _oprot = dynamic_cast<Protocol_*>(oprot);" <<
+ endl <<
+ indent() << "if (_oprot) {" << endl <<
+ indent() << " return throw_" << tfunction->get_name() <<
+ "(cob, seqid, _oprot, ctx, _throw);" << endl <<
+ indent() << "}" << endl << endl;
+ }
+
+ out <<
indent() << tservice->get_name() << "_" << tfunction->get_name() << "_result result;" << endl << endl <<
indent() << "try {" << endl;
indent_up();
- f_service_ <<
+ out <<
indent() << "_throw->throw_it();" << endl <<
indent() << "return cob(false);" << endl; // Is this possible? TBD.
indent_down();
- f_service_ <<
+ out <<
indent() << '}';
for (x_iter = xceptions.begin(); x_iter != xceptions.end(); ++x_iter) {
- f_service_ << " catch (" << type_name((*x_iter)->get_type()) << " &" << (*x_iter)->get_name() << ") {" << endl;
+ out << " catch (" << type_name((*x_iter)->get_type()) << " &" << (*x_iter)->get_name() << ") {" << endl;
indent_up();
- f_service_ <<
+ out <<
indent() << "result." << (*x_iter)->get_name() << " = " << (*x_iter)->get_name() << ";" << endl <<
indent() << "result.__isset." << (*x_iter)->get_name() << " = true;" << endl;
- scope_down(f_service_);
+ scope_down(out);
}
// TODO(dreiss): Handle the case where an undeclared exception is thrown?
// Serialize the result into a struct
- f_service_ <<
+ out <<
endl <<
indent() << "if (eventHandler_.get() != NULL) {" << endl <<
indent() << " ctx = eventHandler_->getContext(\"" << service_func_name << "\");" << endl <<
@@ -2714,8 +3157,8 @@
indent() << " eventHandler_->postWrite(ctx, \"" << service_func_name << "\", bytes);" << endl <<
indent() << "}" << endl <<
indent() << "return cob(true);" << endl;
- scope_down(f_service_);
- f_service_ << endl;
+ scope_down(out);
+ out << endl;
} // for each function
} // cob style
}
@@ -3458,7 +3901,11 @@
string cob_type;
string exn_cob;
if (style == "CobCl") {
- cob_type = "(" + service_name_ + "CobClient* client)";
+ cob_type = "(" + service_name_ + "CobClient";
+ if (gen_templates_) {
+ cob_type += "T<Protocol_>";
+ }
+ cob_type += "* client)";
} else if (style =="CobSv") {
cob_type = (ttype->is_void()
? "()"
diff --git a/lib/cpp/src/protocol/TProtocol.h b/lib/cpp/src/protocol/TProtocol.h
index 3c06bf5..6059d13 100644
--- a/lib/cpp/src/protocol/TProtocol.h
+++ b/lib/cpp/src/protocol/TProtocol.h
@@ -671,6 +671,15 @@
virtual boost::shared_ptr<TProtocol> getProtocol(boost::shared_ptr<TTransport> trans) = 0;
};
+/**
+ * Dummy protocol class.
+ *
+ * This class does nothing, and should never be instantiated.
+ * It is used only by the generator code.
+ */
+class TDummyProtocol : public TProtocol {
+};
+
}}} // apache::thrift::protocol
#endif // #define _THRIFT_PROTOCOL_TPROTOCOL_H_ 1
diff --git a/test/cpp/Thrift-test.mk b/test/cpp/Thrift-test.mk
index 97f0dec..2e17e7e 100644
--- a/test/cpp/Thrift-test.mk
+++ b/test/cpp/Thrift-test.mk
@@ -56,7 +56,7 @@
debug: server-debug client-debug
stubs: ../ThriftTest.thrift
- $(THRIFT) --gen cpp ../ThriftTest.thrift
+ $(THRIFT) --gen cpp:templates ../ThriftTest.thrift
server-debug: stubs
g++ -o TestServer $(DCFL) src/TestServer.cpp ./gen-cpp/ThriftTest.cpp ./gen-cpp/ThriftTest_types.cpp $(thrift_home)/lib/cpp/test/ThriftTest_extras.cpp
@@ -71,7 +71,7 @@
g++ -o TestClient $(CFL) src/TestClient.cpp ./gen-cpp/ThriftTest.cpp ./gen-cpp/ThriftTest_types.cpp $(thrift_home)/lib/cpp/test/ThriftTest_extras.cpp
small:
- $(THRIFT) --gen cpp ../SmallTest.thrift
+ $(THRIFT) --gen cpp:templates ../SmallTest.thrift
g++ -c $(CCFL) ./gen-cpp/SmallService.cpp ./gen-cpp/SmallTest_types.cpp
clean:
diff --git a/test/cpp/src/TestClient.cpp b/test/cpp/src/TestClient.cpp
index 8ae819a..4fcf6b1 100644
--- a/test/cpp/src/TestClient.cpp
+++ b/test/cpp/src/TestClient.cpp
@@ -89,7 +89,7 @@
shared_ptr< TBinaryProtocolT<TBufferBase> > protocol(
new TBinaryProtocolT<TBufferBase>(transport));
- ThriftTestClient testClient(protocol);
+ ThriftTestClientT< TBinaryProtocolT<TBufferBase> > testClient(protocol);
uint64_t time_min = 0;
uint64_t time_max = 0;
diff --git a/test/cpp/src/TestServer.cpp b/test/cpp/src/TestServer.cpp
index 369237e..685957a 100644
--- a/test/cpp/src/TestServer.cpp
+++ b/test/cpp/src/TestServer.cpp
@@ -392,11 +392,13 @@
}
// Dispatcher
- shared_ptr<TProtocolFactory> protocolFactory(new TBinaryProtocolFactory());
+ shared_ptr<TProtocolFactory> protocolFactory(
+ new TBinaryProtocolFactoryT<TBufferBase>());
shared_ptr<TestHandler> testHandler(new TestHandler());
- shared_ptr<ThriftTestProcessor> testProcessor(new ThriftTestProcessor(testHandler));
+ shared_ptr<TProcessor> testProcessor(
+ new ThriftTestProcessorT< TBinaryProtocolT<TBufferBase> >(testHandler));
if (!args["processor-events"].empty()) {