From: David Reiss Date: Fri, 31 Aug 2007 01:42:55 +0000 (+0000) Subject: Thrift: Local Reflection for C++. X-Git-Tag: 0.2.0~1232 X-Git-Url: https://source.supwisdom.com/gerrit/gitweb?a=commitdiff_plain;h=d779cbe48c1335169cb87ed8977e293247b1434a;p=common%2Fthrift.git Thrift: Local Reflection for C++. Summary: The compiler now takes a "-dense" flag that will cause it to generate some extra metadata for C++. This metadata will be used by TDenseProtocol. This should be the last compiler change necessary to enable that feature. Reviewed By: mcslee Test Plan: test/DenseLinkingTest.thrift Revert Plan: ok git-svn-id: https://svn.apache.org/repos/asf/incubator/thrift/trunk@665240 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 2fa1ccdc..10127fc3 100644 --- a/compiler/cpp/src/generate/t_cpp_generator.cc +++ b/compiler/cpp/src/generate/t_cpp_generator.cc @@ -4,7 +4,8 @@ // See accompanying file LICENSE or visit the Thrift site at: // http://developers.facebook.com/thrift/ -#include +#include +#include #include #include #include @@ -39,7 +40,7 @@ void t_cpp_generator::init_generator() { "#ifndef " << program_name_ << "_TYPES_H" << endl << "#define " << program_name_ << "_TYPES_H" << endl << endl; - + // Include base types f_types_ << "#include " << endl << @@ -64,12 +65,20 @@ void t_cpp_generator::init_generator() { } f_types_ << endl; - + // Include the types file f_types_impl_ << "#include \"" << program_name_ << "_types.h\"" << endl << endl; + // If we are generating local reflection metadata, we need to include + // the definition of TypeSpec. + if (gen_dense_) { + f_types_impl_ << + "#include " << endl << + endl; + } + // Open namespace ns_open_ = namespace_open(program_->get_cpp_namespace()); ns_close_ = namespace_close(program_->get_cpp_namespace()); @@ -97,7 +106,7 @@ void t_cpp_generator::close_generator() { // Close ifndef f_types_ << "#endif" << endl; - + // Close output file f_types_.close(); f_types_impl_.close(); @@ -199,7 +208,7 @@ void t_cpp_generator::generate_consts(std::vector consts) { } indent_down(); f_consts << - "};" << endl; + "};" << endl; f_consts_impl << "const " << program_name_ << "Constants g_" << program_name_ << "_constants;" << endl << @@ -350,6 +359,8 @@ string t_cpp_generator::render_const_value(ofstream& out, string name, t_type* t void t_cpp_generator::generate_cpp_struct(t_struct* tstruct, bool is_exception) { generate_struct_definition(f_types_, tstruct, is_exception); generate_struct_fingerprint(f_types_impl_, tstruct, true); + generate_local_reflection(f_types_, tstruct, false); + generate_local_reflection(f_types_impl_, tstruct, true); generate_struct_reader(f_types_impl_, tstruct); generate_struct_writer(f_types_impl_, tstruct); } @@ -382,9 +393,9 @@ void t_cpp_generator::generate_struct_definition(ofstream& out, generate_struct_fingerprint(out, tstruct, false); // Get members - vector::const_iterator m_iter; + vector::const_iterator m_iter; const vector& members = tstruct->get_members(); - + if (!pointers) { // Default constructor indent(out) << @@ -429,7 +440,7 @@ void t_cpp_generator::generate_struct_definition(ofstream& out, } scope_down(out); } - + out << endl << indent() << "virtual ~" << tstruct->get_name() << "() throw() {}" << endl << endl; @@ -439,7 +450,7 @@ void t_cpp_generator::generate_struct_definition(ofstream& out, indent(out) << declare_field(*m_iter, false, pointers && !(*m_iter)->get_type()->is_xception(), !read) << endl; } - + // Isset struct has boolean fields, but only for non-required fields. bool has_nonrequired_fields = false; for (m_iter = members.begin(); m_iter != members.end(); ++m_iter) { @@ -452,7 +463,7 @@ void t_cpp_generator::generate_struct_definition(ofstream& out, endl << indent() << "struct __isset {" << endl; indent_up(); - + indent(out) << "__isset() : "; bool first = true; @@ -470,7 +481,7 @@ void t_cpp_generator::generate_struct_definition(ofstream& out, } } out << " {}" << endl; - + for (m_iter = members.begin(); m_iter != members.end(); ++m_iter) { if ((*m_iter)->get_req() != t_field::REQUIRED) { indent(out) << @@ -480,7 +491,7 @@ void t_cpp_generator::generate_struct_definition(ofstream& out, indent_down(); indent(out) << - "} __isset;" << endl; + "} __isset;" << endl; } out << endl; @@ -527,7 +538,7 @@ void t_cpp_generator::generate_struct_definition(ofstream& out, } out << endl; - indent_down(); + indent_down(); indent(out) << "};" << endl << endl; @@ -569,6 +580,106 @@ void t_cpp_generator::generate_struct_fingerprint(ofstream& out, } } +/** + * Writes the local reflection of a type (either declaration or definition). + */ +void t_cpp_generator::generate_local_reflection(std::ofstream& out, + t_type* ttype, + bool is_definition) { + if (!gen_dense_) { + return; + } + ttype = get_true_type(ttype); + assert(ttype->has_fingerprint()); + string key = ttype->get_ascii_fingerprint() + (is_definition ? "-defn" : "-decl"); + // Note that we have generated this fingerprint. If we already did, bail out. + if (!reflected_fingerprints_.insert(key).second) { + return; + } + // Let each program handle its own structures. + if (ttype->get_program() != NULL && ttype->get_program() != program_) { + return; + } + + // Do dependencies. + if (ttype->is_list()) { + generate_local_reflection(out, ((t_list*)ttype)->get_elem_type(), is_definition); + } else if (ttype->is_set()) { + generate_local_reflection(out, ((t_set*)ttype)->get_elem_type(), is_definition); + } else if (ttype->is_map()) { + generate_local_reflection(out, ((t_map*)ttype)->get_key_type(), is_definition); + generate_local_reflection(out, ((t_map*)ttype)->get_val_type(), is_definition); + } else if (ttype->is_struct() || ttype->is_xception()) { + const vector& members = ((t_struct*)ttype)->get_members(); + vector::const_iterator m_iter; + for (m_iter = members.begin(); m_iter != members.end(); ++m_iter) { + generate_local_reflection(out, (**m_iter).get_type(), is_definition); + } + + // For definitions of structures, do the arrays of tags and field specs also. + if (is_definition) { + indent(out) << "int16_t " << local_reflection_name("ftags", ttype) <<"[] = {" << endl; + indent_up(); + indent(out); + for (m_iter = members.begin(); m_iter != members.end(); ++m_iter) { + out << (*m_iter)->get_key() << ", "; + } + indent_down(); + out << endl << "};" << endl; + + out << + indent() << "facebook::thrift::reflection::local::TypeSpec*" << endl << + indent() << local_reflection_name("specs", ttype) <<"[] = {" << endl; + indent_up(); + for (m_iter = members.begin(); m_iter != members.end(); ++m_iter) { + indent(out) << "&" << + local_reflection_name("typespec", (*m_iter)->get_type()) << "," << endl; + } + indent_down(); + indent(out) << "};" << endl; + } + } + + out << + indent() << "// " << ttype->get_fingerprint_material() << endl << + indent() << (is_definition ? "" : "extern ") << + "facebook::thrift::reflection::local::TypeSpec" << endl << + local_reflection_name("typespec", ttype) << + (is_definition ? "(" : ";") << endl; + + if (!is_definition) { + out << endl; + return; + } + + indent_up(); + + indent(out) << type_to_enum(ttype); + + if (ttype->is_struct()) { + out << "," << endl << + indent() << ((t_struct*)ttype)->get_members().size() << "," << endl << + indent() << local_reflection_name("ftags", ttype) << "," << endl << + indent() << local_reflection_name("specs", ttype); + } else if (ttype->is_list()) { + out << "," << endl << + indent() << "&" << local_reflection_name("typespec", ((t_list*)ttype)->get_elem_type()) << "," << endl << + indent() << "NULL"; + } else if (ttype->is_set()) { + out << "," << endl << + indent() << "&" << local_reflection_name("typespec", ((t_set*)ttype)->get_elem_type()) << "," << endl << + indent() << "NULL"; + } else if (ttype->is_map()) { + out << "," << endl << + indent() << "&" << local_reflection_name("typespec", ((t_map*)ttype)->get_key_type()) << "," << endl << + indent() << "&" << local_reflection_name("typespec", ((t_map*)ttype)->get_val_type()); + } + + out << ");" << endl << endl; + + indent_down(); +} + /** * Makes a helper function to gen a struct reader. * @@ -587,7 +698,7 @@ void t_cpp_generator::generate_struct_reader(ofstream& out, // Declare stack tmp variables out << - endl << + endl << indent() << "uint32_t xfer = 0;" << endl << indent() << "std::string fname;" << endl << indent() << "facebook::thrift::protocol::TType ftype;" << endl << @@ -604,29 +715,29 @@ void t_cpp_generator::generate_struct_reader(ofstream& out, indent(out) << "bool isset_" << (*f_iter)->get_name() << " = false;" << endl; } out << endl; - - + + // Loop over reading in fields indent(out) << "while (true)" << endl; scope_up(out); - + // Read beginning field marker indent(out) << "xfer += iprot->readFieldBegin(fname, ftype, fid);" << endl; - + // Check for field STOP marker out << indent() << "if (ftype == facebook::thrift::protocol::T_STOP) {" << endl << indent() << " break;" << endl << indent() << "}" << endl; - + // Switch statement on the field we are reading indent(out) << "switch (fid)" << endl; scope_up(out); - + // Generate deserialization code for known cases for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) { indent(out) << @@ -667,13 +778,13 @@ void t_cpp_generator::generate_struct_reader(ofstream& out, indent() << "break;" << endl; indent_down(); } - + // In the default case we skip the field out << indent() << "default:" << endl << indent() << " xfer += iprot->skip(ftype);" << endl << indent() << " break;" << endl; - + scope_down(out); // Read field end marker @@ -795,7 +906,7 @@ void t_cpp_generator::generate_struct_result_writer(ofstream& out, for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) { if (first) { first = false; - out << + out << endl << indent() << "if "; } else { @@ -804,7 +915,7 @@ void t_cpp_generator::generate_struct_result_writer(ofstream& out, } out << "(this->__isset." << (*f_iter)->get_name() << ") {" << endl; - + indent_up(); // Write field header @@ -849,7 +960,7 @@ void t_cpp_generator::generate_struct_result_writer(ofstream& out, */ void t_cpp_generator::generate_service(t_service* tservice) { string svcname = tservice->get_name(); - + // Make output files string f_header_name = string(T_CPP_DIR)+"/"+svcname+".h"; f_header_.open(f_header_name.c_str()); @@ -880,7 +991,7 @@ void t_cpp_generator::generate_service(t_service* tservice) { f_service_ << autogen_comment(); f_service_ << - "#include \"" << svcname << ".h\"" << endl << + "#include \"" << svcname << ".h\"" << endl << endl << ns_open_ << endl << endl; @@ -917,7 +1028,7 @@ void t_cpp_generator::generate_service(t_service* tservice) { */ void t_cpp_generator::generate_service_helpers(t_service* tservice) { vector functions = tservice->get_functions(); - vector::iterator f_iter; + vector::iterator f_iter; for (f_iter = functions.begin(); f_iter != functions.end(); ++f_iter) { t_struct* ts = (*f_iter)->get_arglist(); string name_orig = ts->get_name(); @@ -950,7 +1061,7 @@ void t_cpp_generator::generate_service_interface(t_service* tservice) { f_header_ << "class " << service_name_ << "If" << extends << " {" << endl << " public:" << endl; - indent_up(); + indent_up(); f_header_ << indent() << "virtual ~" << service_name_ << "If() {}" << endl; @@ -968,7 +1079,7 @@ void t_cpp_generator::generate_service_interface(t_service* tservice) { //scope_down(f_header_); vector functions = tservice->get_functions(); - vector::iterator f_iter; + vector::iterator f_iter; for (f_iter = functions.begin(); f_iter != functions.end(); ++f_iter) { f_header_ << indent() << "virtual " << function_signature(*f_iter) << " = 0;" << endl; @@ -991,11 +1102,11 @@ void t_cpp_generator::generate_service_null(t_service* tservice) { f_header_ << "class " << service_name_ << "Null : virtual public " << service_name_ << "If" << extends << " {" << endl << " public:" << endl; - indent_up(); + indent_up(); f_header_ << indent() << "virtual ~" << service_name_ << "Null() {}" << endl; vector functions = tservice->get_functions(); - vector::iterator f_iter; + vector::iterator f_iter; for (f_iter = functions.begin(); f_iter != functions.end(); ++f_iter) { f_header_ << indent() << function_signature(*f_iter) << " {" << endl; @@ -1035,7 +1146,7 @@ void t_cpp_generator::generate_service_null(t_service* tservice) { void t_cpp_generator::generate_service_multiface(t_service* tservice) { // Generate the dispatch methods vector functions = tservice->get_functions(); - vector::iterator f_iter; + vector::iterator f_iter; string extends = ""; string extends_multiface = ""; @@ -1053,7 +1164,7 @@ void t_cpp_generator::generate_service_multiface(t_service* tservice) { extends_multiface << " {" << endl << " public:" << endl; indent_up(); - f_header_ << + f_header_ << indent() << service_name_ << "Multiface(" << list_type << "& ifaces) : ifaces_(ifaces) {" << endl; if (!extends.empty()) { f_header_ << @@ -1137,7 +1248,7 @@ void t_cpp_generator::generate_service_multiface(t_service* tservice) { f_header_ << indent() << "}" << endl; - + indent_down(); f_header_ << indent() << "}" << endl << @@ -1168,7 +1279,7 @@ void t_cpp_generator::generate_service_client(t_service* tservice) { "class " << service_name_ << "Client : " << "virtual public " << service_name_ << "If" << extends_client << " {" << endl << - " public:" << endl; + " public:" << endl; indent_up(); f_header_ << @@ -1200,7 +1311,7 @@ void t_cpp_generator::generate_service_client(t_service* tservice) { } vector functions = tservice->get_functions(); - vector::const_iterator f_iter; + vector::const_iterator f_iter; for (f_iter = functions.begin(); f_iter != functions.end(); ++f_iter) { t_function send_function(g_type_void, string("send_") + (*f_iter)->get_name(), @@ -1216,7 +1327,7 @@ void t_cpp_generator::generate_service_client(t_service* tservice) { } } indent_down(); - + if (extends.empty()) { f_header_ << " protected:" << endl; @@ -1226,13 +1337,13 @@ void t_cpp_generator::generate_service_client(t_service* tservice) { indent() << "boost::shared_ptr poprot_;" << endl << indent() << "facebook::thrift::protocol::TProtocol* iprot_;" << endl << indent() << "facebook::thrift::protocol::TProtocol* oprot_;" << endl; - indent_down(); + indent_down(); } f_header_ << "};" << endl << endl; - + string scope = service_name_ + "Client::"; // Generate client method implementations @@ -1248,7 +1359,7 @@ void t_cpp_generator::generate_service_client(t_service* tservice) { // Get the struct of function call params t_struct* arg_struct = (*f_iter)->get_arglist(); - + // Declare the function arguments const vector& fields = arg_struct->get_members(); vector::const_iterator fld_iter; @@ -1299,18 +1410,18 @@ void t_cpp_generator::generate_service_client(t_service* tservice) { indent() << "oprot_->writeMessageBegin(\"" << (*f_iter)->get_name() << "\", facebook::thrift::protocol::T_CALL, cseqid);" << endl << endl << indent() << argsname << " args;" << endl; - + for (fld_iter = fields.begin(); fld_iter != fields.end(); ++fld_iter) { f_service_ << indent() << "args." << (*fld_iter)->get_name() << " = &" << (*fld_iter)->get_name() << ";" << endl; } - + f_service_ << indent() << "args.write(oprot_);" << endl << endl << indent() << "oprot_->writeMessageEnd();" << endl << indent() << "oprot_->getTransport()->flush();" << endl; - + scope_down(f_service_); f_service_ << endl; @@ -1407,7 +1518,7 @@ void t_cpp_generator::generate_service_client(t_service* tservice) { f_service_ << indent() << "throw facebook::thrift::TApplicationException(facebook::thrift::TApplicationException::MISSING_RESULT, \"" << (*f_iter)->get_name() << " failed: unknown result\");" << endl; } - + // Close function scope_down(f_service_); f_service_ << endl; @@ -1423,7 +1534,7 @@ void t_cpp_generator::generate_service_client(t_service* tservice) { void t_cpp_generator::generate_service_processor(t_service* tservice) { // Generate the dispatch methods vector functions = tservice->get_functions(); - vector::iterator f_iter; + vector::iterator f_iter; string extends = ""; string extends_processor = ""; @@ -1435,7 +1546,7 @@ void t_cpp_generator::generate_service_processor(t_service* tservice) { // Generate the header portion f_header_ << "class " << service_name_ << "Processor : " << - "virtual public facebook::thrift::TProcessor" << + "virtual public facebook::thrift::TProcessor" << extends_processor << " {" << endl; // Protected data members @@ -1476,7 +1587,7 @@ void t_cpp_generator::generate_service_processor(t_service* tservice) { } indent_down(); - f_header_ << + f_header_ << " public:" << endl << indent() << service_name_ << "Processor(boost::shared_ptr<" << service_name_ << "If> iface) :" << endl; if (extends.empty()) { @@ -1505,7 +1616,7 @@ void t_cpp_generator::generate_service_processor(t_service* tservice) { f_service_ << endl << indent() << "facebook::thrift::protocol::TProtocol* iprot = piprot.get();" << endl << - indent() << "facebook::thrift::protocol::TProtocol* oprot = poprot.get();" << endl << + indent() << "facebook::thrift::protocol::TProtocol* oprot = poprot.get();" << endl << indent() << "std::string fname;" << endl << indent() << "facebook::thrift::protocol::TMessageType mtype;" << endl << indent() << "int32_t seqid;" << endl << @@ -1535,7 +1646,7 @@ void t_cpp_generator::generate_service_processor(t_service* tservice) { f_service_ << "bool " << service_name_ << "Processor::process_fn(facebook::thrift::protocol::TProtocol* iprot, facebook::thrift::protocol::TProtocol* oprot, std::string& fname, int32_t seqid) {" << endl; indent_up(); - + // HOT: member function pointer map f_service_ << indent() << "std::map::iterator pfn;" << endl << @@ -1559,7 +1670,7 @@ void t_cpp_generator::generate_service_processor(t_service* tservice) { f_service_ << indent() << "} else {" << endl << indent() << " (this->*(pfn->second))(seqid, iprot, oprot);" << endl << - indent() << "}" << endl; + indent() << "}" << endl; // Read end of args field, the T_STOP, and the struct close f_service_ << @@ -1722,7 +1833,7 @@ void t_cpp_generator::generate_process_function(t_service* tservice, indent() << "oprot->writeMessageEnd();" << endl << indent() << "oprot->getTransport()->flush();" << endl << indent() << "oprot->getTransport()->writeEnd();" << endl; - + // Close function scope_down(f_service_); f_service_ << endl; @@ -1895,7 +2006,7 @@ void t_cpp_generator::generate_service_limited_reflector(t_service* tservice) { */ void t_cpp_generator::generate_service_skeleton(t_service* tservice) { string svcname = tservice->get_name(); - + // Service implementation file includes string f_skeleton_name = string(T_CPP_DIR)+"/"+svcname+"_server.skeleton.cpp"; @@ -1907,7 +2018,7 @@ void t_cpp_generator::generate_service_skeleton(t_service* tservice) { "// This autogenerated skeleton file illustrates how to build a server." << endl << "// You should copy it to another filename to avoid overwriting it." << endl << endl << - "#include \"" << svcname << ".h\"" << endl << + "#include \"" << svcname << ".h\"" << endl << "#include " << endl << "#include " << endl << "#include " << endl << @@ -1920,7 +2031,7 @@ void t_cpp_generator::generate_service_skeleton(t_service* tservice) { endl << "using boost::shared_ptr;" << endl << endl; - + if (!ns.empty()) { f_skeleton << "using namespace " << string(ns, 0, ns.size()-2) << ";" << endl << @@ -1971,7 +2082,7 @@ void t_cpp_generator::generate_service_skeleton(t_service* tservice) { f_skeleton << "}" << endl << endl; - + // Close the files f_skeleton.close(); } @@ -2059,16 +2170,16 @@ void t_cpp_generator::generate_deserialize_container(ofstream& out, t_type* ttype, string prefix) { scope_up(out); - + string size = tmp("_size"); string ktype = tmp("_ktype"); string vtype = tmp("_vtype"); string etype = tmp("_etype"); - + indent(out) << prefix << ".clear();" << endl << indent() << "uint32_t " << size << ";" << endl; - + // Declare variables, read header if (ttype->is_map()) { out << @@ -2094,9 +2205,9 @@ void t_cpp_generator::generate_deserialize_container(ofstream& out, out << indent() << "uint32_t " << i << ";" << endl << indent() << "for (" << i << " = 0; " << i << " < " << size << "; ++" << i << ")" << endl; - + scope_up(out); - + if (ttype->is_map()) { generate_deserialize_map_element(out, (t_map*)ttype, prefix); } else if (ttype->is_set()) { @@ -2104,7 +2215,7 @@ void t_cpp_generator::generate_deserialize_container(ofstream& out, } else if (ttype->is_list()) { generate_deserialize_list_element(out, (t_list*)ttype, prefix); } - + scope_down(out); // Read container end @@ -2192,8 +2303,8 @@ void t_cpp_generator::generate_serialize_field(ofstream& out, throw "CANNOT GENERATE SERIALIZE CODE FOR void TYPE: " + name; } - - + + if (type->is_struct() || type->is_xception()) { generate_serialize_struct(out, (t_struct*)type, @@ -2204,7 +2315,7 @@ void t_cpp_generator::generate_serialize_field(ofstream& out, indent(out) << "xfer += oprot->"; - + if (type->is_base_type()) { t_base_type::t_base tbase = ((t_base_type*)type)->get_base(); switch (tbase) { @@ -2264,7 +2375,7 @@ void t_cpp_generator::generate_serialize_container(ofstream& out, t_type* ttype, string prefix) { scope_up(out); - + if (ttype->is_map()) { indent(out) << "xfer += oprot->writeMapBegin(" << @@ -2296,7 +2407,7 @@ void t_cpp_generator::generate_serialize_container(ofstream& out, generate_serialize_list_element(out, (t_list*)ttype, iter); } scope_down(out); - + if (ttype->is_map()) { indent(out) << "xfer += oprot->writeMapEnd();" << endl; @@ -2308,7 +2419,7 @@ void t_cpp_generator::generate_serialize_container(ofstream& out, "xfer += oprot->writeListEnd();" << endl; } - scope_down(out); + scope_down(out); } /** @@ -2365,7 +2476,7 @@ string t_cpp_generator::namespace_prefix(string ns) { if (ns.size() > 0) { result += ns + "::"; } - return result; + return result; } /** @@ -2434,7 +2545,7 @@ string t_cpp_generator::type_name(t_type* ttype, bool in_typedef, bool arg) { return "const " + bname; } } - + // Check for a custom overloaded C++ name if (ttype->is_container()) { string cname; @@ -2454,7 +2565,7 @@ string t_cpp_generator::type_name(t_type* ttype, bool in_typedef, bool arg) { t_list* tlist = (t_list*) ttype; cname = "std::vector<" + type_name(tlist->get_elem_type(), in_typedef) + "> "; } - + if (arg) { return "const " + cname + "&"; } else { @@ -2471,7 +2582,7 @@ string t_cpp_generator::type_name(t_type* ttype, bool in_typedef, bool arg) { string pname; t_program* program = ttype->get_program(); if (program != NULL && program != program_) { - pname = + pname = class_prefix + namespace_prefix(program->get_cpp_namespace()) + ttype->get_name(); @@ -2624,7 +2735,7 @@ string t_cpp_generator::argument_list(t_struct* tstruct) { */ string t_cpp_generator::type_to_enum(t_type* type) { type = get_true_type(type); - + if (type->is_base_type()) { t_base_type::t_base tbase = ((t_base_type*)type)->get_base(); switch (tbase) { @@ -2661,3 +2772,40 @@ string t_cpp_generator::type_to_enum(t_type* type) { throw "INVALID TYPE IN type_to_enum: " + type->get_name(); } + +/** + * Returns the symbol name of the local reflection of a type. + */ +string t_cpp_generator::local_reflection_name(const char* prefix, t_type* ttype) { + ttype = get_true_type(ttype); + + // We have to use the program name as part of the identifier because + // if two thrift "programs" are compiled into one actual program + // you would get a symbol collison if they both defined list. + // trlo = Thrift Reflection LOcal. + string prog; + string name; + + // TODO(dreiss): Would it be better to pregenerate the base types + // and put them in Thrift.{h,cpp} ? + + if (ttype->is_base_type()) { + //name = ttype->get_name(); + prog = program_->get_name(); + name = ttype->get_ascii_fingerprint(); + } else if (ttype->is_enum()) { + //name = "enum"; + prog = program_->get_name(); + name = ttype->get_ascii_fingerprint(); + } else if (ttype->is_container()) { + prog = program_->get_name(); + name = ttype->get_ascii_fingerprint(); + } else { + assert(ttype->is_struct() || ttype->is_xception()); + assert(ttype->get_program() != NULL); + prog = ttype->get_program()->get_name(); + name = ttype->get_ascii_fingerprint(); + } + + return string() + "trlo_" + prefix + "_" + prog + "_" + name; +} diff --git a/compiler/cpp/src/generate/t_cpp_generator.h b/compiler/cpp/src/generate/t_cpp_generator.h index 7d8902c3..70549904 100644 --- a/compiler/cpp/src/generate/t_cpp_generator.h +++ b/compiler/cpp/src/generate/t_cpp_generator.h @@ -24,8 +24,9 @@ */ class t_cpp_generator : public t_oop_generator { public: - t_cpp_generator(t_program* program) : - t_oop_generator(program) {} + t_cpp_generator(t_program* program, bool gen_dense) : + t_oop_generator(program), + gen_dense_(gen_dense) {} /** * Init and close methods @@ -146,6 +147,10 @@ class t_cpp_generator : public t_oop_generator { std::string function_signature(t_function* tfunction, std::string prefix=""); std::string argument_list(t_struct* tstruct); std::string type_to_enum(t_type* ttype); + std::string local_reflection_name(const char*, t_type* ttype); + + // This handles checking gen_dense_ and checking for duplicates. + void generate_local_reflection(std::ofstream& out, t_type* ttype, bool is_definition); bool is_complex_type(t_type* ttype) { ttype = get_true_type(ttype); @@ -159,6 +164,11 @@ class t_cpp_generator : public t_oop_generator { private: + /** + * True iff we should generate local reflection metadata for TDenseProtocol. + */ + bool gen_dense_; + /** * Strings for namespace, computed once up front then used directly */ @@ -175,6 +185,11 @@ class t_cpp_generator : public t_oop_generator { std::ofstream f_types_impl_; std::ofstream f_header_; std::ofstream f_service_; + + /** + * When generating local reflections, make sure we don't generate duplicates. + */ + std::set reflected_fingerprints_; }; #endif diff --git a/compiler/cpp/src/main.cc b/compiler/cpp/src/main.cc index e46fceab..e0ff21fb 100644 --- a/compiler/cpp/src/main.cc +++ b/compiler/cpp/src/main.cc @@ -142,6 +142,7 @@ bool gen_perl = false; bool gen_ocaml = false; bool gen_erl = false; bool gen_hs = false; +bool gen_dense = false; bool gen_recurse = false; /** @@ -525,6 +526,13 @@ void generate_all_fingerprints(t_program* program) { st->generate_fingerprint(); } + const vector& xceptions = program->get_xceptions(); + vector::const_iterator x_iter; + for (x_iter = xceptions.begin(); x_iter != xceptions.end(); ++x_iter) { + t_struct* st = *x_iter; + st->generate_fingerprint(); + } + // If you want to generate fingerprints for implicit structures, start here. /* const vector& services = program->get_services(); @@ -553,8 +561,10 @@ void usage() { fprintf(stderr, " -ocaml Generate OCaml output files\n"); fprintf(stderr, " -erl Generate Erlang output files\n"); fprintf(stderr, " -hs Generate Haskell output files\n"); - fprintf(stderr, " -I dir Add a directory to the list of directories \n"); + fprintf(stderr, " -I dir Add a directory to the list of directories\n"); fprintf(stderr, " searched for include directives\n"); + fprintf(stderr, " -dense Generate metadata for TDenseProtocol (C++)\n"); + fprintf(stderr, " -rest Generate PHP REST processors (with -php)\n"); fprintf(stderr, " -nowarn Suppress all compiler warnings (BAD!)\n"); fprintf(stderr, " -strict Strict compiler warnings on\n"); fprintf(stderr, " -v[erbose] Verbose mode\n"); @@ -768,7 +778,7 @@ void generate(t_program* program) { if (gen_cpp) { pverbose("Generating C++\n"); - t_cpp_generator* cpp = new t_cpp_generator(program); + t_cpp_generator* cpp = new t_cpp_generator(program, gen_dense); cpp->generate_program(); delete cpp; } @@ -898,6 +908,8 @@ int main(int argc, char** argv) { g_verbose = 1; } else if (strcmp(arg, "-r") == 0 || strcmp(arg, "-recurse") == 0 ) { gen_recurse = true; + } else if (strcmp(arg, "-dense") == 0) { + gen_dense = true; } else if (strcmp(arg, "-cpp") == 0) { gen_cpp = true; } else if (strcmp(arg, "-javabean") == 0) { diff --git a/lib/cpp/src/TReflectionLocal.h b/lib/cpp/src/TReflectionLocal.h new file mode 100644 index 00000000..6f082e72 --- /dev/null +++ b/lib/cpp/src/TReflectionLocal.h @@ -0,0 +1,69 @@ +// Copyright (c) 2006- Facebook +// Distributed under the Thrift Software License +// +// See accompanying file LICENSE or visit the Thrift site at: +// http://developers.facebook.com/thrift/ + +#ifndef _THRIFT_TREFLECTIONLOCAL_H_ +#define _THRIFT_TREFLECTIONLOCAL_H_ 1 + +#include +#include + +namespace facebook { namespace thrift { namespace reflection { namespace local { + +using facebook::thrift::protocol::TType; + +/** + * A local reflection is a representation of a Thrift structure. + * (It is called local because it cannot be serialized by Thrift). + * + * @author David Reiss + */ + +struct TypeSpec { + // Use an anonymous union here so we can fit two TypeSpecs in one cache line. + union { + struct { + // Use parallel arrays here for denser packing (of the arrays). + int16_t* ftags; + TypeSpec** specs; + int n_fields; + } tstruct; + struct { + TypeSpec *subtype1; + TypeSpec *subtype2; + } tcontainer; + }; + + // Put this at the end so the 32-bit enum can be crammed up next to the + // 32-bit int (n_fields). + TType ttype; + + + // Static initialization of unions isn't really possible, + // so take the plunge and use constructors. + // Hopefully they'll be evaluated at compile time. + + TypeSpec(TType ttype) : ttype(ttype) {} + + TypeSpec(TType ttype, int n_fields, int16_t* ftags, TypeSpec** specs) : + ttype(ttype) + { + tstruct.n_fields = n_fields; + tstruct.ftags = ftags; + tstruct.specs = specs; + } + + TypeSpec(TType ttype, TypeSpec* subtype1, TypeSpec* subtype2) : + ttype(ttype) + { + tcontainer.subtype1 = subtype1; + tcontainer.subtype2 = subtype2; + } + +}; + +}}}} // facebook::thrift::reflection::local + +#endif // #ifndef _THRIFT_TREFLECTIONLOCAL_H_ diff --git a/lib/cpp/src/Thrift.h b/lib/cpp/src/Thrift.h index 07ccb97e..4a3df658 100644 --- a/lib/cpp/src/Thrift.h +++ b/lib/cpp/src/Thrift.h @@ -134,6 +134,12 @@ protected: }; +// Forward declare this structure used by TDenseProtocol +namespace reflection { namespace local { +struct TypeSpec; +}} + + }} // facebook::thrift #endif // #ifndef _THRIFT_THRIFT_H_ diff --git a/test/DenseLinkingTest.thrift b/test/DenseLinkingTest.thrift new file mode 100644 index 00000000..9414bd54 --- /dev/null +++ b/test/DenseLinkingTest.thrift @@ -0,0 +1,83 @@ +/* +../compiler/cpp/thrift -cpp -dense DebugProtoTest.thrift +../compiler/cpp/thrift -cpp -dense DenseLinkingTest.thrift +g++ -Wall -g -I../lib/cpp/src -I/usr/local/include/boost-1_33_1 \ + DebugProtoTest.cpp gen-cpp/DebugProtoTest_types.cpp \ + gen-cpp/DenseLinkingTest_types.cpp \ + ../lib/cpp/.libs/libthrift.a -o DebugProtoTest +./DebugProtoTest +*/ + +/* +The idea of this test is that everything is structurally identical to DebugProtoTest. +If I messed up the naming of the reflection local typespecs, +then compiling this should give errors because of doubly defined symbols. +*/ + +cpp_namespace thrift.test + +struct OneOfEachZZ { + 1: bool im_true, + 2: bool im_false, + 3: byte a_bite, + 4: i16 integer16, + 5: i32 integer32, + 6: i64 integer64, + 7: double double_precision, + 8: string some_characters, + 9: string zomg_unicode, + 10: bool what_who, +} + +struct BonkZZ { + 1: i32 type, + 2: string message, +} + +struct NestingZZ { + 1: BonkZZ my_bonk, + 2: OneOfEachZZ my_ooe, +} + +struct HolyMoleyZZ { + 1: list big, + 2: set> contain, + 3: map> bonks, +} + +struct BackwardsZZ { + 2: i32 first_tag2, + 1: i32 second_tag1, +} + +struct EmptyZZ { +} + +struct WrapperZZ { + 1: EmptyZZ foo +} + +struct RandomStuffZZ { + 1: i32 a, + 2: i32 b, + 3: i32 c, + 4: i32 d, + 5: list myintlist, + 6: map maps, + 7: i64 bigint, + 8: double triple, +} + +service Srv { + i32 Janky(i32 arg) +} + +service PartiallyReflectable { + map> returnNotReflectable(1: i32 hello), + void argNotReflectable(1: list> arg), + void arg2NotReflectable(1: i32 arg1, 2: list> argNotReflectable), + void withMap(1: map amap), + + OneOfEachZZ refl1(1: list arg1), + OneOfEachZZ refl2(2: list arg1, 1: BonkZZ arg2); +}