Allow the specification of custom container types in Thrift IDL files

Summary: If you want your map to be a hash_map instead of an stl::map, we now have a directive in Thrift to let you do that.

Instead of:
map<i32,i32>

You can do:
map[cpp:hash_map<int32_t,int32_t>]<i32,i32>

This tells the Thrift compiler to explicitly use whatever type was specified in the brackets when generating C++ code, instead of the implied Thrift type.

Reviewed By: aditya


git-svn-id: https://svn.apache.org/repos/asf/incubator/thrift/trunk@664828 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 1a2c8ae..46f71c1 100644
--- a/compiler/cpp/src/generate/t_cpp_generator.cc
+++ b/compiler/cpp/src/generate/t_cpp_generator.cc
@@ -1498,20 +1498,32 @@
 string t_cpp_generator::type_name(t_type* ttype) {
   if (ttype->is_base_type()) {
     return base_type_name(((t_base_type*)ttype)->get_base());
-  } else if (ttype->is_map()) {
+  }
+  
+  // Check for a custom overloaded C++ name
+  if (ttype->is_container()) {
+    t_container* tcontainer = (t_container*) ttype;
+    if (tcontainer->has_cpp_name()) {
+      return tcontainer->get_cpp_name();
+    }
+  }
+  // Use std:: types for containers
+  if (ttype->is_map()) {
     t_map* tmap = (t_map*) ttype;
     return "std::map<" +
       type_name(tmap->get_key_type()) + ", " +
       type_name(tmap->get_val_type()) + "> ";
-  } else if (ttype->is_set()) {
+  }
+  if (ttype->is_set()) {
     t_set* tset = (t_set*) ttype;
     return "std::set<" + type_name(tset->get_elem_type()) + "> ";
-  } else if (ttype->is_list()) {
+  }
+  if (ttype->is_list()) {
     t_list* tlist = (t_list*) ttype;
     return "std::vector<" + type_name(tlist->get_elem_type()) + "> ";
-  } else {
-    return ttype->get_name();
   }
+
+  return ttype->get_name();
 }
 
 /**
diff --git a/compiler/cpp/src/parse/t_container.h b/compiler/cpp/src/parse/t_container.h
new file mode 100644
index 0000000..3b9f132
--- /dev/null
+++ b/compiler/cpp/src/parse/t_container.h
@@ -0,0 +1,37 @@
+#ifndef T_CONTAINER_H
+#define T_CONTAINER_H
+
+#include "t_type.h"
+
+class t_container : public t_type {
+ public:
+  t_container() :
+    cpp_name_(),
+    has_cpp_name_(false) {}
+
+  virtual ~t_container() {}
+
+  void set_cpp_name(std::string cpp_name) {
+    cpp_name_ = cpp_name;
+    has_cpp_name_ = true;
+  }
+
+  bool has_cpp_name() {
+    return has_cpp_name_;
+  }
+
+  std::string get_cpp_name() {
+    return cpp_name_;
+  }
+
+  bool is_container() const {
+    return true;
+  }
+
+ private:
+  std::string cpp_name_;
+  bool has_cpp_name_;
+
+};
+
+#endif
diff --git a/compiler/cpp/src/parse/t_list.h b/compiler/cpp/src/parse/t_list.h
index 5f9b5aa..bce5c0d 100644
--- a/compiler/cpp/src/parse/t_list.h
+++ b/compiler/cpp/src/parse/t_list.h
@@ -1,14 +1,14 @@
 #ifndef T_LIST_H
 #define T_LIST_H
 
-#include "t_type.h"
+#include "t_container.h"
 
 /**
  * A list is a lightweight container type that just wraps another data type.
  *
  * @author Mark Slee <mcslee@facebook.com>
  */
-class t_list : public t_type {
+class t_list : public t_container {
  public:
   t_list(t_type* elem_type) :
     elem_type_(elem_type) {}
diff --git a/compiler/cpp/src/parse/t_map.h b/compiler/cpp/src/parse/t_map.h
index ff1c569..fd55932 100644
--- a/compiler/cpp/src/parse/t_map.h
+++ b/compiler/cpp/src/parse/t_map.h
@@ -1,13 +1,15 @@
 #ifndef T_MAP_H
 #define T_MAP_H
 
+#include "t_container.h"
+
 /**
  * A map is a lightweight container type that just wraps another two data
  * types.
  *
  * @author Mark Slee <mcslee@facebook.com>
  */
-class t_map : public t_type {
+class t_map : public t_container {
  public:
   t_map(t_type* key_type, t_type* val_type) :
     key_type_(key_type),
diff --git a/compiler/cpp/src/parse/t_set.h b/compiler/cpp/src/parse/t_set.h
index e39e420..239868f 100644
--- a/compiler/cpp/src/parse/t_set.h
+++ b/compiler/cpp/src/parse/t_set.h
@@ -1,14 +1,14 @@
 #ifndef T_SET_H
 #define T_SET_H
 
-#include "t_type.h"
+#include "t_container.h"
 
 /**
  * A set is a lightweight container type that just wraps another data type.
  *
  * @author Mark Slee <mcslee@facebook.com>
  */
-class t_set : public t_type {
+class t_set : public t_container {
  public:
   t_set(t_type* elem_type) :
     elem_type_(elem_type) {}
diff --git a/compiler/cpp/src/parse/t_type.h b/compiler/cpp/src/parse/t_type.h
index 27b85a5..1d0a2ed 100644
--- a/compiler/cpp/src/parse/t_type.h
+++ b/compiler/cpp/src/parse/t_type.h
@@ -24,14 +24,11 @@
   virtual bool is_enum()      const { return false; }
   virtual bool is_struct()    const { return false; }
   virtual bool is_xception()  const { return false; }
+  virtual bool is_container() const { return false; }
   virtual bool is_list()      const { return false; }
   virtual bool is_set()       const { return false; }
   virtual bool is_map()       const { return false; }
 
-  bool is_container() const {
-    return is_map() || is_set() || is_list();
-  }
-
  protected:
   t_type() {}
 
diff --git a/compiler/cpp/src/thrift.l b/compiler/cpp/src/thrift.l
index 64a2bf8..e72565e 100644
--- a/compiler/cpp/src/thrift.l
+++ b/compiler/cpp/src/thrift.l
@@ -34,6 +34,7 @@
 comment      ("//"[^\n]*)
 unixcomment  ("#"[^\n]*)
 symbol       ([\,\{\}\(\)\=<>])
+cpptype      ("[cpp:".*"]")
 
 %%
 
@@ -64,8 +65,21 @@
 "service"     { return tok_service;   }
 "enum"        { return tok_enum;      }
 
-{intconstant} { yylval.iconst = atoi(yytext); return tok_int_constant; }
+{intconstant} {
+  yylval.iconst = atoi(yytext);
+  return tok_int_constant;
+}
 
-{identifier}  { yylval.id = strdup(yytext); return tok_identifier; }
+{cpptype} {
+  yylval.id = strdup(yytext+5);
+  yylval.id[strlen(yylval.id)-1] = '\0';
+  return tok_cpptype;
+}
+
+{identifier} {
+  yylval.id = strdup(yytext);
+  return tok_identifier;
+}
+
 
 %%
diff --git a/compiler/cpp/src/thrift.y b/compiler/cpp/src/thrift.y
index 404c80b..3bf8b53 100644
--- a/compiler/cpp/src/thrift.y
+++ b/compiler/cpp/src/thrift.y
@@ -44,6 +44,7 @@
  * Strings identifier
  */
 %token<id>     tok_identifier
+%token<id>     tok_cpptype
 
 /**
  * Integer constant value
@@ -122,6 +123,7 @@
 
 %type<tstruct>   ThrowsOptional
 %type<tbool>     AsyncOptional
+%type<id>        CppTypeOptional
 
 %%
 
@@ -449,24 +451,43 @@
     }
 
 MapType:
-  tok_map '<' FieldType ',' FieldType '>'
+  tok_map CppTypeOptional '<' FieldType ',' FieldType '>'
     {
       pdebug("MapType -> tok_map <FieldType, FieldType>");
-      $$ = new t_map($3, $5);
+      $$ = new t_map($4, $6);
+      if ($2 != NULL) {
+        ((t_container*)$$)->set_cpp_name(std::string($2));
+      }
     }
 
 SetType:
-  tok_set '<' FieldType '>'
+  tok_set CppTypeOptional '<' FieldType '>'
     {
       pdebug("SetType -> tok_set<FieldType>");
-      $$ = new t_set($3);
+      $$ = new t_set($4);
+      if ($2 != NULL) {
+        ((t_container*)$$)->set_cpp_name(std::string($2));
+      }
     }
 
 ListType:
-  tok_list '<' FieldType '>'
+  tok_list CppTypeOptional '<' FieldType '>'
     {
       pdebug("ListType -> tok_list<FieldType>");
-      $$ = new t_list($3);
+      $$ = new t_list($4);
+      if ($2 != NULL) {
+        ((t_container*)$$)->set_cpp_name(std::string($2));
+      }
+    }
+
+CppTypeOptional:
+  tok_cpptype
+    {
+      $$ = $1;
+    }
+|
+    {
+      $$ = NULL;
     }
 
 %%