Thrift: Limited Reflection for C++.

Summary:
The Thrift compiler now generates static methods for every service to generate
a reflection of the methods provided by the service.  This reflection is fairly
limited, but should be enough for what we want to do with SMC.

Reviewed By: mcslee

Test Plan: test/ReflectionTest.cpp

Revert Plan: ok


git-svn-id: https://svn.apache.org/repos/asf/incubator/thrift/trunk@665226 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/cleanup.sh b/cleanup.sh
index c78c3fc..5c0046d 100755
--- a/cleanup.sh
+++ b/cleanup.sh
@@ -28,7 +28,8 @@
 .libs \
 libtool \
 ltmain.sh \
-missing
+missing \
+if/gen-*
 
 for subdir in ${subdirs}; do 
     if [ -x "${subdir}/cleanup.sh" ]; then 
diff --git a/compiler/cpp/src/generate/t_cpp_generator.cc b/compiler/cpp/src/generate/t_cpp_generator.cc
index 796c9e0..8580e2a 100644
--- a/compiler/cpp/src/generate/t_cpp_generator.cc
+++ b/compiler/cpp/src/generate/t_cpp_generator.cc
@@ -7,6 +7,7 @@
 #include <stdlib.h>
 #include <sys/stat.h>
 #include <sstream>
+#include <boost/lexical_cast.hpp>
 #include "t_cpp_generator.h"
 using namespace std;
 
@@ -42,6 +43,7 @@
   // Include base types
   f_types_ <<
     "#include <Thrift.h>" << endl <<
+    "#include <reflection_limited_types.h>" << endl <<
     "#include <protocol/TProtocol.h>" << endl <<
     "#include <transport/TTransport.h>" << endl <<
     endl;
@@ -891,6 +893,8 @@
 
     generate_function_helpers(tservice, *f_iter);
   }
+
+  generate_service_limited_reflector(tservice);
 }
 
 /**
@@ -909,6 +913,20 @@
   indent_up(); 
   f_header_ <<
     indent() << "virtual ~" << service_name_ << "If() {}" << endl;
+
+  f_header_ <<
+    indent() << "static void getStaticLimitedReflection" <<
+    "(facebook::thrift::reflection::limited::Service & _return);" << endl;
+  // TODO(dreiss): Uncomment and test this if we decide we need
+  // a virtual function with this effect.
+  //f_header_ <<
+  //  indent() << "virtual void getVirtualLimitedReflection" <<
+  //  "(facebook::thrift::reflection::limited::Service & _return) ";
+  //scope_up(f_header_);
+  //f_header_ <<
+  //  indent() << "getStaticLimitedReflection(_return);" << endl;
+  //scope_down(f_header_);
+
   vector<t_function*> functions = tservice->get_functions();
   vector<t_function*>::iterator f_iter; 
   for (f_iter = functions.begin(); f_iter != functions.end(); ++f_iter) {
@@ -1671,6 +1689,166 @@
 }
 
 /**
+ * Helper function for generate_service_limited_reflector.
+ * Generates a reflection of a single simple type.
+ *
+ * @param ttype  The type to reflect
+ * @param target  The name of the lvalue to reflect onto
+ * @return  true iff the type really is simple
+ *
+ * Note: don't let this function output anything unless it is going to return true.
+ */
+bool t_cpp_generator::generate_simple_type_limited_reflection(ostream & out, t_type* ttype, string target) {
+  if (ttype->is_base_type()) {
+    string type;
+    switch (((t_base_type*)ttype)->get_base()) {
+      case t_base_type::TYPE_VOID   : type = "T_VOID;"   ; break;
+      case t_base_type::TYPE_STRING : type = "T_STRING;" ; break;
+      case t_base_type::TYPE_BOOL   : type = "T_BOOL;"   ; break;
+      case t_base_type::TYPE_BYTE   : type = "T_BYTE;"   ; break;
+      case t_base_type::TYPE_I16    : type = "T_I16;"    ; break;
+      case t_base_type::TYPE_I32    : type = "T_I32;"    ; break;
+      case t_base_type::TYPE_I64    : type = "T_I64;"    ; break;
+      case t_base_type::TYPE_DOUBLE : type = "T_DOUBLE;" ; break;
+      default: return false;
+    }
+    out << indent() << target << ".ttype = " << type << endl;
+    return true;
+  }
+
+  if (ttype->is_enum()) {
+    out <<
+      indent() << target << ".ttype = T_ENUM;" << endl <<
+      indent() << target << ".name = \"" << ttype->get_name() << "\";" << endl;
+  }
+
+  if (ttype->is_struct()) {
+    out <<
+      indent() << target << ".ttype = T_STRUCT;" << endl <<
+      indent() << target << ".name = \"" << ttype->get_name() << "\";" << endl;
+    return true;
+  }
+
+  return false;
+}
+
+/**
+ * Helper function for generate_service_limited_reflector.
+ * Generates a reflection of a single type.
+ *
+ * @param ttype  The type to reflect
+ * @param target  The name of the lvalue to reflect onto
+ */
+bool t_cpp_generator::generate_type_limited_reflection(t_type* ttype, string target) {
+  bool is_simple = generate_simple_type_limited_reflection(f_service_, ttype, target + ".simple_type");
+  if (is_simple) {
+    f_service_ <<
+      indent() << target << ".is_container = false;" << endl <<
+      indent() << target << ".__isset.simple_type = true;" << endl;
+    return true;
+  }
+
+  ostringstream out;
+
+  out <<
+    indent() << target << ".is_container = true;" << endl <<
+    indent() << target << ".__isset.container_type = true;" << endl <<
+    indent() << target << ".container_type.ttype = ";
+
+  if (ttype->is_list()) out << "T_LIST;" << endl;
+  if (ttype->is_set())  out << "T_SET;"  << endl;
+  if (ttype->is_map())  out << "T_MAP;"  << endl;
+
+  bool reflected = false;
+
+  if (ttype->is_list()) {
+    reflected = generate_simple_type_limited_reflection(
+        out, ((t_list*)ttype)->get_elem_type(), target + ".container_type.subtype1");
+  }
+  if (ttype->is_set()) {
+    reflected = generate_simple_type_limited_reflection(
+        out, ((t_set*)ttype)->get_elem_type(), target + ".container_type.subtype1");
+  }
+  if (ttype->is_map()) {
+    reflected =
+      generate_simple_type_limited_reflection(
+        out, ((t_map*)ttype)->get_key_type(), target + ".container_type.subtype1")
+      &&
+      generate_simple_type_limited_reflection(
+        out, ((t_map*)ttype)->get_val_type(), target + ".container_type.subtype2");
+    out << indent() << target << ".container_type.__isset.subtype2 = true;" << endl;
+  }
+
+  if (reflected) {
+    f_service_ << out.str();
+    return true;
+  } else {
+    f_service_ <<
+      indent() << target << ".is_container = false;" << endl <<
+      indent() << target << ".__isset.simple_type = true;" << endl;
+    f_service_ << indent() << target << ".simple_type.ttype = T_NOT_REFLECTED;" << endl;
+    return false;
+  }
+}
+
+/**
+ * Generates a service reflector definition.
+ * This uses thrift::reflection::limited.
+ *
+ * @param tservice The service to write a reflector for
+ */
+void t_cpp_generator::generate_service_limited_reflector(t_service* tservice) {
+  // Open function
+  f_service_ <<
+    indent() << "void " << tservice->get_name() << "If::getStaticLimitedReflection" <<
+    "(facebook::thrift::reflection::limited::Service & _return) ";
+  scope_up(f_service_);
+
+  f_service_ << indent() << "using namespace facebook::thrift::reflection::limited;" << endl;
+
+  f_service_ << indent() << "_return.name = \"" << tservice->get_name() << "\";" << endl;
+  f_service_ << indent() << "_return.fully_reflected = true;" << endl;
+
+  bool all_reflectable = true;
+  bool one_reflectable;
+
+  const vector<t_function*> & funcs = tservice->get_functions();
+  vector<t_function*>::const_iterator f_iter;
+  for (f_iter = funcs.begin(); f_iter != funcs.end(); ++f_iter) {
+
+    f_service_ << indent() << "_return.methods.resize(_return.methods.size() + 1);" << endl;
+    f_service_ << indent() << "_return.methods.back().name = \"" << (*f_iter)->get_name() << "\";" << endl;
+    one_reflectable = generate_type_limited_reflection(
+        (*f_iter)->get_returntype(), "_return.methods.back().return_type");
+    all_reflectable = all_reflectable && one_reflectable;
+
+    t_struct* arglist = (*f_iter)->get_arglist();
+    const vector<t_field*> & args = arglist->get_members();
+    vector<t_field*>::const_iterator a_iter;
+    for (a_iter = args.begin(); a_iter != args.end(); ++a_iter) {
+      f_service_ <<
+        indent() << "_return.methods.back().arguments.resize("
+          "_return.methods.back().arguments.size() + 1);" << endl <<
+        indent() << "_return.methods.back().arguments.back().name = \"" <<
+          (*a_iter)->get_name() << "\";" << endl <<
+        indent() << "_return.methods.back().arguments.back().key = " <<
+          (*a_iter)->get_key() << ";" << endl;
+      one_reflectable = generate_type_limited_reflection(
+          (*a_iter)->get_type(), "_return.methods.back().arguments.back().type");
+      all_reflectable = all_reflectable && one_reflectable;
+    }
+  }
+
+  if (!all_reflectable) {
+    f_service_ << indent() << "_return.fully_reflected = false;" << endl;
+  }
+
+  // Close function
+  scope_down(f_service_);
+  f_service_ << endl;
+}
+
+/**
  * Generates a skeleton file of a server
  *
  * @param tservice The service to generate a server for.
diff --git a/compiler/cpp/src/generate/t_cpp_generator.h b/compiler/cpp/src/generate/t_cpp_generator.h
index acc75f6..302f270 100644
--- a/compiler/cpp/src/generate/t_cpp_generator.h
+++ b/compiler/cpp/src/generate/t_cpp_generator.h
@@ -70,10 +70,14 @@
   void generate_service_helpers   (t_service* tservice);
   void generate_service_client    (t_service* tservice);
   void generate_service_processor (t_service* tservice);
-  void generate_service_skeleton  (t_service*  tservice);
+  void generate_service_skeleton  (t_service* tservice);
   void generate_process_function  (t_service* tservice, t_function* tfunction);
   void generate_function_helpers  (t_service* tservice, t_function* tfunction);
 
+  void generate_service_limited_reflector(t_service* tservice);
+  bool generate_type_limited_reflection(t_type* ttype, std::string target);
+  bool generate_simple_type_limited_reflection(std::ostream& out, t_type* ttype, std::string target);
+
   /**
    * Serialization constructs
    */
diff --git a/if/reflection_limited.thrift b/if/reflection_limited.thrift
new file mode 100644
index 0000000..7769817
--- /dev/null
+++ b/if/reflection_limited.thrift
@@ -0,0 +1,70 @@
+#!/usr/local/bin/thrift -php -java -cpp -py
+
+// NOTICE!!!
+// DO NOT FORGET to run regen.sh if you change this file
+// (or if you change the compiler).
+
+// This interface is deprecated.
+// There is no replacement yet, but I hate it so much that
+// I'm deprecating it before it's done.
+// @author I'm too ashamed to say.
+
+// dreiss naively thinks he knows how to do this better,
+// so talk to him if you are interested in taking it on,
+// or if you just want someone to make it better for you.
+
+cpp_namespace facebook.thrift.reflection.limited
+java_package com.facebook.thrift.reflection.limited
+
+enum TTypeTag {
+  T_VOID   = 1,
+  T_BOOL   = 2,
+  T_BYTE   = 3,
+  T_I16    = 6,
+  T_I32    = 8,
+  T_I64    = 10,
+  T_DOUBLE = 4,
+  T_STRING = 11,
+  T_STRUCT = 12,
+  T_MAP    = 13,
+  T_SET    = 14,
+  T_LIST   = 15,
+  // This doesn't exist in TBinaryProtocol, but it could be useful for reflection.
+  T_ENUM   = 101,
+  T_NOT_REFLECTED = 102,
+}
+
+struct SimpleType {
+  1: TTypeTag ttype,
+  2: string name,  // For structs and emums.
+}
+
+struct ContainerType {
+  1: TTypeTag ttype,
+  2:          SimpleType subtype1,
+  3: optional SimpleType subtype2,
+}
+
+struct ThriftType {
+  1: bool is_container,
+  2: optional SimpleType simple_type,
+  3: optional ContainerType container_type,
+}
+
+struct Argument {
+  1: i16 key,
+  2: string name,
+  3: ThriftType type,
+}
+
+struct Method {
+  1: string name,
+  2: ThriftType return_type,
+  3: list<Argument> arguments,
+}
+
+struct Service {
+  1: string name,
+  2: list<Method> methods,
+  3: bool fully_reflected,
+}
diff --git a/if/regen.sh b/if/regen.sh
new file mode 100755
index 0000000..03dad02
--- /dev/null
+++ b/if/regen.sh
@@ -0,0 +1,5 @@
+#!/bin/sh
+cd "`dirname $0`"
+../compiler/cpp/thrift -cpp reflection_limited.thrift
+cp gen-cpp/reflection_limited_types.h   ../lib/cpp/src/
+cp gen-cpp/reflection_limited_types.cpp ../lib/cpp/src/
diff --git a/lib/cpp/Makefile.am b/lib/cpp/Makefile.am
index b8d3cea..fa48e3e 100644
--- a/lib/cpp/Makefile.am
+++ b/lib/cpp/Makefile.am
@@ -6,6 +6,7 @@
 # Define the source file for the module
 
 libthrift_sources = src/Thrift.cpp \
+                    src/reflection_limited_types.cpp \
                     src/concurrency/Mutex.cpp \
                     src/concurrency/Monitor.cpp \
                     src/concurrency/PosixThreadFactory.cpp \
@@ -40,6 +41,7 @@
 include_thrift_HEADERS = \
                          config.h \
                          src/Thrift.h \
+                         src/reflection_limited_types.h \
                          src/TProcessor.h \
                          src/TLogging.h
 
diff --git a/lib/cpp/src/TLogging.h b/lib/cpp/src/TLogging.h
index 15c9c06..4d3060d 100644
--- a/lib/cpp/src/TLogging.h
+++ b/lib/cpp/src/TLogging.h
@@ -4,8 +4,8 @@
 // See accompanying file LICENSE or visit the Thrift site at:
 // http://developers.facebook.com/thrift/
 
-#ifndef _THRIFT_LOGGING_H
-#define _THRIFT_LOGGING_H 1
+#ifndef _THRIFT_TLOGGING_H_
+#define _THRIFT_TLOGGING_H_ 1
 
 #ifdef HAVE_CONFIG_H
 #include "config.h"
@@ -148,4 +148,4 @@
   #define T_LOG_OPER(format_string,...)
 #endif
 
-#endif	// _THRIFT_LOGGING_H
+#endif // #ifndef _THRIFT_TLOGGING_H_
diff --git a/lib/cpp/src/reflection_limited_types.cpp b/lib/cpp/src/reflection_limited_types.cpp
new file mode 100644
index 0000000..d5c6153
--- /dev/null
+++ b/lib/cpp/src/reflection_limited_types.cpp
@@ -0,0 +1,489 @@
+/**
+ * Autogenerated by Thrift
+ *
+ * DO NOT EDIT UNLESS YOU ARE SURE THAT YOU KNOW WHAT YOU ARE DOING
+ */
+#include "reflection_limited_types.h"
+
+namespace facebook { namespace thrift { namespace reflection { namespace limited {
+
+uint32_t SimpleType::read(facebook::thrift::protocol::TProtocol* iprot) {
+
+  uint32_t xfer = 0;
+  std::string fname;
+  facebook::thrift::protocol::TType ftype;
+  int16_t fid;
+
+  xfer += iprot->readStructBegin(fname);
+
+  using facebook::thrift::protocol::TProtocolException;
+
+
+  while (true)
+  {
+    xfer += iprot->readFieldBegin(fname, ftype, fid);
+    if (ftype == facebook::thrift::protocol::T_STOP) {
+      break;
+    }
+    switch (fid)
+    {
+      case 1:
+        if (ftype == facebook::thrift::protocol::T_I32) {
+          int32_t ecast0;
+          xfer += iprot->readI32(ecast0);
+          this->ttype = (TTypeTag)ecast0;
+          this->__isset.ttype = true;
+        } else {
+          xfer += iprot->skip(ftype);
+        }
+        break;
+      case 2:
+        if (ftype == facebook::thrift::protocol::T_STRING) {
+          xfer += iprot->readString(this->name);
+          this->__isset.name = true;
+        } else {
+          xfer += iprot->skip(ftype);
+        }
+        break;
+      default:
+        xfer += iprot->skip(ftype);
+        break;
+    }
+    xfer += iprot->readFieldEnd();
+  }
+
+  xfer += iprot->readStructEnd();
+
+  return xfer;
+}
+
+uint32_t SimpleType::write(facebook::thrift::protocol::TProtocol* oprot) const {
+  uint32_t xfer = 0;
+  xfer += oprot->writeStructBegin("SimpleType");
+  xfer += oprot->writeFieldBegin("ttype", facebook::thrift::protocol::T_I32, 1);
+  xfer += oprot->writeI32((int32_t)this->ttype);
+  xfer += oprot->writeFieldEnd();
+  xfer += oprot->writeFieldBegin("name", facebook::thrift::protocol::T_STRING, 2);
+  xfer += oprot->writeString(this->name);
+  xfer += oprot->writeFieldEnd();
+  xfer += oprot->writeFieldStop();
+  xfer += oprot->writeStructEnd();
+  return xfer;
+}
+
+uint32_t ContainerType::read(facebook::thrift::protocol::TProtocol* iprot) {
+
+  uint32_t xfer = 0;
+  std::string fname;
+  facebook::thrift::protocol::TType ftype;
+  int16_t fid;
+
+  xfer += iprot->readStructBegin(fname);
+
+  using facebook::thrift::protocol::TProtocolException;
+
+
+  while (true)
+  {
+    xfer += iprot->readFieldBegin(fname, ftype, fid);
+    if (ftype == facebook::thrift::protocol::T_STOP) {
+      break;
+    }
+    switch (fid)
+    {
+      case 1:
+        if (ftype == facebook::thrift::protocol::T_I32) {
+          int32_t ecast1;
+          xfer += iprot->readI32(ecast1);
+          this->ttype = (TTypeTag)ecast1;
+          this->__isset.ttype = true;
+        } else {
+          xfer += iprot->skip(ftype);
+        }
+        break;
+      case 2:
+        if (ftype == facebook::thrift::protocol::T_STRUCT) {
+          xfer += this->subtype1.read(iprot);
+          this->__isset.subtype1 = true;
+        } else {
+          xfer += iprot->skip(ftype);
+        }
+        break;
+      case 3:
+        if (ftype == facebook::thrift::protocol::T_STRUCT) {
+          xfer += this->subtype2.read(iprot);
+          this->__isset.subtype2 = true;
+        } else {
+          xfer += iprot->skip(ftype);
+        }
+        break;
+      default:
+        xfer += iprot->skip(ftype);
+        break;
+    }
+    xfer += iprot->readFieldEnd();
+  }
+
+  xfer += iprot->readStructEnd();
+
+  return xfer;
+}
+
+uint32_t ContainerType::write(facebook::thrift::protocol::TProtocol* oprot) const {
+  uint32_t xfer = 0;
+  xfer += oprot->writeStructBegin("ContainerType");
+  xfer += oprot->writeFieldBegin("ttype", facebook::thrift::protocol::T_I32, 1);
+  xfer += oprot->writeI32((int32_t)this->ttype);
+  xfer += oprot->writeFieldEnd();
+  xfer += oprot->writeFieldBegin("subtype1", facebook::thrift::protocol::T_STRUCT, 2);
+  xfer += this->subtype1.write(oprot);
+  xfer += oprot->writeFieldEnd();
+  if (this->__isset.subtype2) {
+    xfer += oprot->writeFieldBegin("subtype2", facebook::thrift::protocol::T_STRUCT, 3);
+    xfer += this->subtype2.write(oprot);
+    xfer += oprot->writeFieldEnd();
+  }
+  xfer += oprot->writeFieldStop();
+  xfer += oprot->writeStructEnd();
+  return xfer;
+}
+
+uint32_t ThriftType::read(facebook::thrift::protocol::TProtocol* iprot) {
+
+  uint32_t xfer = 0;
+  std::string fname;
+  facebook::thrift::protocol::TType ftype;
+  int16_t fid;
+
+  xfer += iprot->readStructBegin(fname);
+
+  using facebook::thrift::protocol::TProtocolException;
+
+
+  while (true)
+  {
+    xfer += iprot->readFieldBegin(fname, ftype, fid);
+    if (ftype == facebook::thrift::protocol::T_STOP) {
+      break;
+    }
+    switch (fid)
+    {
+      case 1:
+        if (ftype == facebook::thrift::protocol::T_BOOL) {
+          xfer += iprot->readBool(this->is_container);
+          this->__isset.is_container = true;
+        } else {
+          xfer += iprot->skip(ftype);
+        }
+        break;
+      case 2:
+        if (ftype == facebook::thrift::protocol::T_STRUCT) {
+          xfer += this->simple_type.read(iprot);
+          this->__isset.simple_type = true;
+        } else {
+          xfer += iprot->skip(ftype);
+        }
+        break;
+      case 3:
+        if (ftype == facebook::thrift::protocol::T_STRUCT) {
+          xfer += this->container_type.read(iprot);
+          this->__isset.container_type = true;
+        } else {
+          xfer += iprot->skip(ftype);
+        }
+        break;
+      default:
+        xfer += iprot->skip(ftype);
+        break;
+    }
+    xfer += iprot->readFieldEnd();
+  }
+
+  xfer += iprot->readStructEnd();
+
+  return xfer;
+}
+
+uint32_t ThriftType::write(facebook::thrift::protocol::TProtocol* oprot) const {
+  uint32_t xfer = 0;
+  xfer += oprot->writeStructBegin("ThriftType");
+  xfer += oprot->writeFieldBegin("is_container", facebook::thrift::protocol::T_BOOL, 1);
+  xfer += oprot->writeBool(this->is_container);
+  xfer += oprot->writeFieldEnd();
+  if (this->__isset.simple_type) {
+    xfer += oprot->writeFieldBegin("simple_type", facebook::thrift::protocol::T_STRUCT, 2);
+    xfer += this->simple_type.write(oprot);
+    xfer += oprot->writeFieldEnd();
+  }
+  if (this->__isset.container_type) {
+    xfer += oprot->writeFieldBegin("container_type", facebook::thrift::protocol::T_STRUCT, 3);
+    xfer += this->container_type.write(oprot);
+    xfer += oprot->writeFieldEnd();
+  }
+  xfer += oprot->writeFieldStop();
+  xfer += oprot->writeStructEnd();
+  return xfer;
+}
+
+uint32_t Argument::read(facebook::thrift::protocol::TProtocol* iprot) {
+
+  uint32_t xfer = 0;
+  std::string fname;
+  facebook::thrift::protocol::TType ftype;
+  int16_t fid;
+
+  xfer += iprot->readStructBegin(fname);
+
+  using facebook::thrift::protocol::TProtocolException;
+
+
+  while (true)
+  {
+    xfer += iprot->readFieldBegin(fname, ftype, fid);
+    if (ftype == facebook::thrift::protocol::T_STOP) {
+      break;
+    }
+    switch (fid)
+    {
+      case 1:
+        if (ftype == facebook::thrift::protocol::T_I16) {
+          xfer += iprot->readI16(this->key);
+          this->__isset.key = true;
+        } else {
+          xfer += iprot->skip(ftype);
+        }
+        break;
+      case 2:
+        if (ftype == facebook::thrift::protocol::T_STRING) {
+          xfer += iprot->readString(this->name);
+          this->__isset.name = true;
+        } else {
+          xfer += iprot->skip(ftype);
+        }
+        break;
+      case 3:
+        if (ftype == facebook::thrift::protocol::T_STRUCT) {
+          xfer += this->type.read(iprot);
+          this->__isset.type = true;
+        } else {
+          xfer += iprot->skip(ftype);
+        }
+        break;
+      default:
+        xfer += iprot->skip(ftype);
+        break;
+    }
+    xfer += iprot->readFieldEnd();
+  }
+
+  xfer += iprot->readStructEnd();
+
+  return xfer;
+}
+
+uint32_t Argument::write(facebook::thrift::protocol::TProtocol* oprot) const {
+  uint32_t xfer = 0;
+  xfer += oprot->writeStructBegin("Argument");
+  xfer += oprot->writeFieldBegin("key", facebook::thrift::protocol::T_I16, 1);
+  xfer += oprot->writeI16(this->key);
+  xfer += oprot->writeFieldEnd();
+  xfer += oprot->writeFieldBegin("name", facebook::thrift::protocol::T_STRING, 2);
+  xfer += oprot->writeString(this->name);
+  xfer += oprot->writeFieldEnd();
+  xfer += oprot->writeFieldBegin("type", facebook::thrift::protocol::T_STRUCT, 3);
+  xfer += this->type.write(oprot);
+  xfer += oprot->writeFieldEnd();
+  xfer += oprot->writeFieldStop();
+  xfer += oprot->writeStructEnd();
+  return xfer;
+}
+
+uint32_t Method::read(facebook::thrift::protocol::TProtocol* iprot) {
+
+  uint32_t xfer = 0;
+  std::string fname;
+  facebook::thrift::protocol::TType ftype;
+  int16_t fid;
+
+  xfer += iprot->readStructBegin(fname);
+
+  using facebook::thrift::protocol::TProtocolException;
+
+
+  while (true)
+  {
+    xfer += iprot->readFieldBegin(fname, ftype, fid);
+    if (ftype == facebook::thrift::protocol::T_STOP) {
+      break;
+    }
+    switch (fid)
+    {
+      case 1:
+        if (ftype == facebook::thrift::protocol::T_STRING) {
+          xfer += iprot->readString(this->name);
+          this->__isset.name = true;
+        } else {
+          xfer += iprot->skip(ftype);
+        }
+        break;
+      case 2:
+        if (ftype == facebook::thrift::protocol::T_STRUCT) {
+          xfer += this->return_type.read(iprot);
+          this->__isset.return_type = true;
+        } else {
+          xfer += iprot->skip(ftype);
+        }
+        break;
+      case 3:
+        if (ftype == facebook::thrift::protocol::T_LIST) {
+          {
+            this->arguments.clear();
+            uint32_t _size2;
+            facebook::thrift::protocol::TType _etype5;
+            iprot->readListBegin(_etype5, _size2);
+            uint32_t _i6;
+            for (_i6 = 0; _i6 < _size2; ++_i6)
+            {
+              Argument _elem7;
+              xfer += _elem7.read(iprot);
+              this->arguments.push_back(_elem7);
+            }
+            iprot->readListEnd();
+          }
+          this->__isset.arguments = true;
+        } else {
+          xfer += iprot->skip(ftype);
+        }
+        break;
+      default:
+        xfer += iprot->skip(ftype);
+        break;
+    }
+    xfer += iprot->readFieldEnd();
+  }
+
+  xfer += iprot->readStructEnd();
+
+  return xfer;
+}
+
+uint32_t Method::write(facebook::thrift::protocol::TProtocol* oprot) const {
+  uint32_t xfer = 0;
+  xfer += oprot->writeStructBegin("Method");
+  xfer += oprot->writeFieldBegin("name", facebook::thrift::protocol::T_STRING, 1);
+  xfer += oprot->writeString(this->name);
+  xfer += oprot->writeFieldEnd();
+  xfer += oprot->writeFieldBegin("return_type", facebook::thrift::protocol::T_STRUCT, 2);
+  xfer += this->return_type.write(oprot);
+  xfer += oprot->writeFieldEnd();
+  xfer += oprot->writeFieldBegin("arguments", facebook::thrift::protocol::T_LIST, 3);
+  {
+    xfer += oprot->writeListBegin(facebook::thrift::protocol::T_STRUCT, this->arguments.size());
+    std::vector<Argument> ::const_iterator _iter8;
+    for (_iter8 = this->arguments.begin(); _iter8 != this->arguments.end(); ++_iter8)
+    {
+      xfer += (*_iter8).write(oprot);
+    }
+    xfer += oprot->writeListEnd();
+  }
+  xfer += oprot->writeFieldEnd();
+  xfer += oprot->writeFieldStop();
+  xfer += oprot->writeStructEnd();
+  return xfer;
+}
+
+uint32_t Service::read(facebook::thrift::protocol::TProtocol* iprot) {
+
+  uint32_t xfer = 0;
+  std::string fname;
+  facebook::thrift::protocol::TType ftype;
+  int16_t fid;
+
+  xfer += iprot->readStructBegin(fname);
+
+  using facebook::thrift::protocol::TProtocolException;
+
+
+  while (true)
+  {
+    xfer += iprot->readFieldBegin(fname, ftype, fid);
+    if (ftype == facebook::thrift::protocol::T_STOP) {
+      break;
+    }
+    switch (fid)
+    {
+      case 1:
+        if (ftype == facebook::thrift::protocol::T_STRING) {
+          xfer += iprot->readString(this->name);
+          this->__isset.name = true;
+        } else {
+          xfer += iprot->skip(ftype);
+        }
+        break;
+      case 2:
+        if (ftype == facebook::thrift::protocol::T_LIST) {
+          {
+            this->methods.clear();
+            uint32_t _size9;
+            facebook::thrift::protocol::TType _etype12;
+            iprot->readListBegin(_etype12, _size9);
+            uint32_t _i13;
+            for (_i13 = 0; _i13 < _size9; ++_i13)
+            {
+              Method _elem14;
+              xfer += _elem14.read(iprot);
+              this->methods.push_back(_elem14);
+            }
+            iprot->readListEnd();
+          }
+          this->__isset.methods = true;
+        } else {
+          xfer += iprot->skip(ftype);
+        }
+        break;
+      case 3:
+        if (ftype == facebook::thrift::protocol::T_BOOL) {
+          xfer += iprot->readBool(this->fully_reflected);
+          this->__isset.fully_reflected = true;
+        } else {
+          xfer += iprot->skip(ftype);
+        }
+        break;
+      default:
+        xfer += iprot->skip(ftype);
+        break;
+    }
+    xfer += iprot->readFieldEnd();
+  }
+
+  xfer += iprot->readStructEnd();
+
+  return xfer;
+}
+
+uint32_t Service::write(facebook::thrift::protocol::TProtocol* oprot) const {
+  uint32_t xfer = 0;
+  xfer += oprot->writeStructBegin("Service");
+  xfer += oprot->writeFieldBegin("name", facebook::thrift::protocol::T_STRING, 1);
+  xfer += oprot->writeString(this->name);
+  xfer += oprot->writeFieldEnd();
+  xfer += oprot->writeFieldBegin("methods", facebook::thrift::protocol::T_LIST, 2);
+  {
+    xfer += oprot->writeListBegin(facebook::thrift::protocol::T_STRUCT, this->methods.size());
+    std::vector<Method> ::const_iterator _iter15;
+    for (_iter15 = this->methods.begin(); _iter15 != this->methods.end(); ++_iter15)
+    {
+      xfer += (*_iter15).write(oprot);
+    }
+    xfer += oprot->writeListEnd();
+  }
+  xfer += oprot->writeFieldEnd();
+  xfer += oprot->writeFieldBegin("fully_reflected", facebook::thrift::protocol::T_BOOL, 3);
+  xfer += oprot->writeBool(this->fully_reflected);
+  xfer += oprot->writeFieldEnd();
+  xfer += oprot->writeFieldStop();
+  xfer += oprot->writeStructEnd();
+  return xfer;
+}
+
+}}}} // namespace
diff --git a/lib/cpp/src/reflection_limited_types.h b/lib/cpp/src/reflection_limited_types.h
new file mode 100644
index 0000000..843c691
--- /dev/null
+++ b/lib/cpp/src/reflection_limited_types.h
@@ -0,0 +1,266 @@
+/**
+ * Autogenerated by Thrift
+ *
+ * DO NOT EDIT UNLESS YOU ARE SURE THAT YOU KNOW WHAT YOU ARE DOING
+ */
+#ifndef reflection_limited_TYPES_H
+#define reflection_limited_TYPES_H
+
+#include <Thrift.h>
+#include <protocol/TProtocol.h>
+#include <transport/TTransport.h>
+
+
+
+namespace facebook { namespace thrift { namespace reflection { namespace limited {
+
+enum TTypeTag {
+  T_VOID = 1,
+  T_BOOL = 2,
+  T_BYTE = 3,
+  T_I16 = 6,
+  T_I32 = 8,
+  T_I64 = 10,
+  T_DOUBLE = 4,
+  T_STRING = 11,
+  T_STRUCT = 12,
+  T_MAP = 13,
+  T_SET = 14,
+  T_LIST = 15,
+  T_ENUM = 101,
+  T_NOT_REFLECTED = 102
+};
+
+class SimpleType {
+ public:
+
+  SimpleType() : name("") {
+  }
+
+  virtual ~SimpleType() throw() {}
+
+  TTypeTag ttype;
+  std::string name;
+
+  struct __isset {
+    __isset() : ttype(false), name(false) {}
+    bool ttype;
+    bool name;
+  } __isset;
+
+  bool operator == (const SimpleType & rhs) const
+  {
+    if (!(ttype == rhs.ttype))
+      return false;
+    if (!(name == rhs.name))
+      return false;
+    return true;
+  }
+  bool operator != (const SimpleType &rhs) const {
+    return !(*this == rhs);
+  }
+
+  uint32_t read(facebook::thrift::protocol::TProtocol* iprot);
+  uint32_t write(facebook::thrift::protocol::TProtocol* oprot) const;
+
+};
+
+class ContainerType {
+ public:
+
+  ContainerType() {
+  }
+
+  virtual ~ContainerType() throw() {}
+
+  TTypeTag ttype;
+  SimpleType subtype1;
+  SimpleType subtype2;
+
+  struct __isset {
+    __isset() : ttype(false), subtype1(false), subtype2(false) {}
+    bool ttype;
+    bool subtype1;
+    bool subtype2;
+  } __isset;
+
+  bool operator == (const ContainerType & rhs) const
+  {
+    if (!(ttype == rhs.ttype))
+      return false;
+    if (!(subtype1 == rhs.subtype1))
+      return false;
+    if (__isset.subtype2 != rhs.__isset.subtype2)
+      return false;
+    else if (__isset.subtype2 && !(subtype2 == rhs.subtype2))
+      return false;
+    return true;
+  }
+  bool operator != (const ContainerType &rhs) const {
+    return !(*this == rhs);
+  }
+
+  uint32_t read(facebook::thrift::protocol::TProtocol* iprot);
+  uint32_t write(facebook::thrift::protocol::TProtocol* oprot) const;
+
+};
+
+class ThriftType {
+ public:
+
+  ThriftType() : is_container(0) {
+  }
+
+  virtual ~ThriftType() throw() {}
+
+  bool is_container;
+  SimpleType simple_type;
+  ContainerType container_type;
+
+  struct __isset {
+    __isset() : is_container(false), simple_type(false), container_type(false) {}
+    bool is_container;
+    bool simple_type;
+    bool container_type;
+  } __isset;
+
+  bool operator == (const ThriftType & rhs) const
+  {
+    if (!(is_container == rhs.is_container))
+      return false;
+    if (__isset.simple_type != rhs.__isset.simple_type)
+      return false;
+    else if (__isset.simple_type && !(simple_type == rhs.simple_type))
+      return false;
+    if (__isset.container_type != rhs.__isset.container_type)
+      return false;
+    else if (__isset.container_type && !(container_type == rhs.container_type))
+      return false;
+    return true;
+  }
+  bool operator != (const ThriftType &rhs) const {
+    return !(*this == rhs);
+  }
+
+  uint32_t read(facebook::thrift::protocol::TProtocol* iprot);
+  uint32_t write(facebook::thrift::protocol::TProtocol* oprot) const;
+
+};
+
+class Argument {
+ public:
+
+  Argument() : key(0), name("") {
+  }
+
+  virtual ~Argument() throw() {}
+
+  int16_t key;
+  std::string name;
+  ThriftType type;
+
+  struct __isset {
+    __isset() : key(false), name(false), type(false) {}
+    bool key;
+    bool name;
+    bool type;
+  } __isset;
+
+  bool operator == (const Argument & rhs) const
+  {
+    if (!(key == rhs.key))
+      return false;
+    if (!(name == rhs.name))
+      return false;
+    if (!(type == rhs.type))
+      return false;
+    return true;
+  }
+  bool operator != (const Argument &rhs) const {
+    return !(*this == rhs);
+  }
+
+  uint32_t read(facebook::thrift::protocol::TProtocol* iprot);
+  uint32_t write(facebook::thrift::protocol::TProtocol* oprot) const;
+
+};
+
+class Method {
+ public:
+
+  Method() : name("") {
+  }
+
+  virtual ~Method() throw() {}
+
+  std::string name;
+  ThriftType return_type;
+  std::vector<Argument>  arguments;
+
+  struct __isset {
+    __isset() : name(false), return_type(false), arguments(false) {}
+    bool name;
+    bool return_type;
+    bool arguments;
+  } __isset;
+
+  bool operator == (const Method & rhs) const
+  {
+    if (!(name == rhs.name))
+      return false;
+    if (!(return_type == rhs.return_type))
+      return false;
+    if (!(arguments == rhs.arguments))
+      return false;
+    return true;
+  }
+  bool operator != (const Method &rhs) const {
+    return !(*this == rhs);
+  }
+
+  uint32_t read(facebook::thrift::protocol::TProtocol* iprot);
+  uint32_t write(facebook::thrift::protocol::TProtocol* oprot) const;
+
+};
+
+class Service {
+ public:
+
+  Service() : name(""), fully_reflected(0) {
+  }
+
+  virtual ~Service() throw() {}
+
+  std::string name;
+  std::vector<Method>  methods;
+  bool fully_reflected;
+
+  struct __isset {
+    __isset() : name(false), methods(false), fully_reflected(false) {}
+    bool name;
+    bool methods;
+    bool fully_reflected;
+  } __isset;
+
+  bool operator == (const Service & rhs) const
+  {
+    if (!(name == rhs.name))
+      return false;
+    if (!(methods == rhs.methods))
+      return false;
+    if (!(fully_reflected == rhs.fully_reflected))
+      return false;
+    return true;
+  }
+  bool operator != (const Service &rhs) const {
+    return !(*this == rhs);
+  }
+
+  uint32_t read(facebook::thrift::protocol::TProtocol* iprot);
+  uint32_t write(facebook::thrift::protocol::TProtocol* oprot) const;
+
+};
+
+}}}} // namespace
+
+#endif
diff --git a/test/DebugProtoTest.thrift b/test/DebugProtoTest.thrift
index bbd86df..090418c 100644
--- a/test/DebugProtoTest.thrift
+++ b/test/DebugProtoTest.thrift
@@ -63,3 +63,13 @@
 service Srv {
   i32 Janky(i32 arg)
 }
+
+service PartiallyReflectable {
+  map<i32,map<i32,i32>> returnNotReflectable(1: i32 hello),
+  void argNotReflectable(1: list<set<i32>> arg),
+  void arg2NotReflectable(1: i32 arg1, 2: list<set<i32>> argNotReflectable),
+  void withMap(1: map<i32, string> amap),
+
+  OneOfEach refl1(1: list<Bonk> arg1),
+  OneOfEach refl2(2: list<string> arg1, 1: Bonk arg2);
+}
diff --git a/test/ReflectionTest.cpp b/test/ReflectionTest.cpp
new file mode 100644
index 0000000..614c613
--- /dev/null
+++ b/test/ReflectionTest.cpp
@@ -0,0 +1,30 @@
+/*
+../compiler/cpp/thrift -cpp DebugProtoTest.thrift
+../compiler/cpp/thrift -cpp StressTest.thrift
+g++ -Wall -I../lib/cpp/src -I/usr/local/include/boost-1_33_1 \
+  ReflectionTest.cpp \
+  gen-cpp/StressTest_types.cpp gen-cpp/DebugProtoTest_types.cpp \
+  gen-cpp/Service.cpp gen-cpp/PartiallyReflectable.cpp \
+  ../lib/cpp/.libs/libthrift.a -o ReflectionTest
+./ReflectionTest
+*/
+
+#include <iostream>
+#include "gen-cpp/PartiallyReflectable.h"
+#include "gen-cpp/Service.h"
+#include "../lib/cpp/src/protocol/TDebugProtocol.h"
+
+int main() {
+  using std::cout;
+  using std::endl;
+
+  facebook::thrift::reflection::limited::Service srv1;
+  thrift::test::PartiallyReflectableIf::getStaticLimitedReflection(srv1);
+  cout << facebook::thrift::ThriftDebugString(srv1) << endl << endl;
+
+  facebook::thrift::reflection::limited::Service srv2;
+  test::stress::ServiceIf::getStaticLimitedReflection(srv2);
+  cout << facebook::thrift::ThriftDebugString(srv2) << endl << endl;
+
+  return 0;
+}