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 2fa1ccd..10127fc 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 <stdlib.h>
+#include <cstdlib>
+#include <cassert>
 #include <sys/stat.h>
 #include <sstream>
 #include <boost/lexical_cast.hpp>
@@ -39,7 +40,7 @@
     "#ifndef " << program_name_ << "_TYPES_H" << endl <<
     "#define " << program_name_ << "_TYPES_H" << endl <<
     endl;
-  
+
   // Include base types
   f_types_ <<
     "#include <Thrift.h>" << endl <<
@@ -64,12 +65,20 @@
   }
   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 <TReflectionLocal.h>" << endl <<
+      endl;
+  }
+
   // Open namespace
   ns_open_ = namespace_open(program_->get_cpp_namespace());
   ns_close_ = namespace_close(program_->get_cpp_namespace());
@@ -97,7 +106,7 @@
   // Close ifndef
   f_types_ <<
     "#endif" << endl;
- 
+
   // Close output file
   f_types_.close();
   f_types_impl_.close();
@@ -199,7 +208,7 @@
   }
   indent_down();
   f_consts <<
-    "};" << endl;  
+    "};" << endl;
 
   f_consts_impl <<
     "const " << program_name_ << "Constants g_" << program_name_ << "_constants;" << endl <<
@@ -350,6 +359,8 @@
 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 @@
   generate_struct_fingerprint(out, tstruct, false);
 
   // Get members
-  vector<t_field*>::const_iterator m_iter; 
+  vector<t_field*>::const_iterator m_iter;
   const vector<t_field*>& members = tstruct->get_members();
-  
+
   if (!pointers) {
     // Default constructor
     indent(out) <<
@@ -429,7 +440,7 @@
     }
     scope_down(out);
   }
-  
+
   out <<
     endl <<
     indent() << "virtual ~" << tstruct->get_name() << "() throw() {}" << endl << endl;
@@ -439,7 +450,7 @@
     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 @@
       endl <<
       indent() << "struct __isset {" << endl;
     indent_up();
-      
+
       indent(out) <<
         "__isset() : ";
       bool first = true;
@@ -470,7 +481,7 @@
         }
       }
       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 @@
 
     indent_down();
     indent(out) <<
-      "} __isset;" << endl;  
+      "} __isset;" << endl;
   }
 
   out << endl;
@@ -527,7 +538,7 @@
   }
   out << endl;
 
-  indent_down(); 
+  indent_down();
   indent(out) <<
     "};" << endl <<
     endl;
@@ -570,6 +581,106 @@
 }
 
 /**
+ * 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<t_field*>& members = ((t_struct*)ttype)->get_members();
+    vector<t_field*>::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.
  *
  * @param out Stream to write to
@@ -587,7 +698,7 @@
 
   // 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 @@
       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 @@
           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 @@
   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 @@
     }
 
     out << "(this->__isset." << (*f_iter)->get_name() << ") {" << endl;
-    
+
     indent_up();
 
     // Write field header
@@ -849,7 +960,7 @@
  */
 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 @@
   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_helpers(t_service* tservice) {
   vector<t_function*> functions = tservice->get_functions();
-  vector<t_function*>::iterator f_iter; 
+  vector<t_function*>::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 @@
   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 @@
   //scope_down(f_header_);
 
   vector<t_function*> functions = tservice->get_functions();
-  vector<t_function*>::iterator f_iter; 
+  vector<t_function*>::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 @@
   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<t_function*> functions = tservice->get_functions();
-  vector<t_function*>::iterator f_iter; 
+  vector<t_function*>::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_multiface(t_service* tservice) {
   // Generate the dispatch methods
   vector<t_function*> functions = tservice->get_functions();
-  vector<t_function*>::iterator f_iter; 
+  vector<t_function*>::iterator f_iter;
 
   string extends = "";
   string extends_multiface = "";
@@ -1053,7 +1164,7 @@
     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 @@
 
     f_header_ <<
       indent() << "}" << endl;
-    
+
     indent_down();
     f_header_ <<
       indent() << "}" << endl <<
@@ -1168,7 +1279,7 @@
     "class " << service_name_ << "Client : " <<
     "virtual public " << service_name_ << "If" <<
     extends_client << " {" << endl <<
-    " public:" << endl; 
+    " public:" << endl;
 
   indent_up();
   f_header_ <<
@@ -1200,7 +1311,7 @@
   }
 
   vector<t_function*> functions = tservice->get_functions();
-  vector<t_function*>::const_iterator f_iter; 
+  vector<t_function*>::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 @@
     }
   }
   indent_down();
-  
+
   if (extends.empty()) {
     f_header_ <<
       " protected:" << endl;
@@ -1226,13 +1337,13 @@
       indent() << "boost::shared_ptr<facebook::thrift::protocol::TProtocol> 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 @@
 
     // Get the struct of function call params
     t_struct* arg_struct = (*f_iter)->get_arglist();
-    
+
     // Declare the function arguments
     const vector<t_field*>& fields = arg_struct->get_members();
     vector<t_field*>::const_iterator fld_iter;
@@ -1299,18 +1410,18 @@
       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 @@
         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_processor(t_service* tservice) {
   // Generate the dispatch methods
   vector<t_function*> functions = tservice->get_functions();
-  vector<t_function*>::iterator f_iter; 
+  vector<t_function*>::iterator f_iter;
 
   string extends = "";
   string extends_processor = "";
@@ -1435,7 +1546,7 @@
   // 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 @@
   }
   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 @@
   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 @@
   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<std::string, void (" << service_name_ << "Processor::*)(int32_t, facebook::thrift::protocol::TProtocol*, facebook::thrift::protocol::TProtocol*)>::iterator pfn;" << endl <<
@@ -1559,7 +1670,7 @@
   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 @@
     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_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 @@
     "// 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 <protocol/TBinaryProtocol.h>" << endl <<
     "#include <server/TSimpleServer.h>" << endl <<
     "#include <transport/TServerSocket.h>" << endl <<
@@ -1920,7 +2031,7 @@
     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 @@
   f_skeleton <<
     "}" << endl <<
     endl;
-  
+
   // Close the files
   f_skeleton.close();
 }
@@ -2059,16 +2170,16 @@
                                                      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 @@
   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 @@
     } else if (ttype->is_list()) {
       generate_deserialize_list_element(out, (t_list*)ttype, prefix);
     }
-    
+
     scope_down(out);
 
   // Read container end
@@ -2192,8 +2303,8 @@
     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 @@
 
     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 @@
                                                    t_type* ttype,
                                                    string prefix) {
   scope_up(out);
-  
+
   if (ttype->is_map()) {
     indent(out) <<
       "xfer += oprot->writeMapBegin(" <<
@@ -2296,7 +2407,7 @@
       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 @@
       "xfer += oprot->writeListEnd();" << endl;
   }
 
-  scope_down(out);  
+  scope_down(out);
 }
 
 /**
@@ -2365,7 +2476,7 @@
   if (ns.size() > 0) {
     result += ns + "::";
   }
-  return result;  
+  return result;
 }
 
 /**
@@ -2434,7 +2545,7 @@
       return "const " + bname;
     }
   }
- 
+
   // Check for a custom overloaded C++ name
   if (ttype->is_container()) {
     string cname;
@@ -2454,7 +2565,7 @@
       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 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::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 @@
 
   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<i32>.
+  // 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 7d8902c..7054990 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 @@
   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);
@@ -160,6 +165,11 @@
  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 @@
   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<std::string> reflected_fingerprints_;
 };
 
 #endif
diff --git a/compiler/cpp/src/main.cc b/compiler/cpp/src/main.cc
index e46fcea..e0ff21f 100644
--- a/compiler/cpp/src/main.cc
+++ b/compiler/cpp/src/main.cc
@@ -142,6 +142,7 @@
 bool gen_ocaml = false;
 bool gen_erl = false;
 bool gen_hs = false;
+bool gen_dense = false;
 bool gen_recurse = false;
 
 /**
@@ -525,6 +526,13 @@
     st->generate_fingerprint();
   }
 
+  const vector<t_struct*>& xceptions = program->get_xceptions();
+  vector<t_struct*>::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<t_service*>& services = program->get_services();
@@ -553,8 +561,10 @@
   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 @@
 
     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 @@
         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) {