THRIFT-5772: UUID support for c++ #2952
Client: cpp
Patch: CJCombrink

This closes #2952
diff --git a/compiler/cpp/src/thrift/generate/t_cpp_generator.cc b/compiler/cpp/src/thrift/generate/t_cpp_generator.cc
index a085ada..e21252e 100644
--- a/compiler/cpp/src/thrift/generate/t_cpp_generator.cc
+++ b/compiler/cpp/src/thrift/generate/t_cpp_generator.cc
@@ -296,7 +296,7 @@
            (ttype->annotations_.find("cpp.customostream") != ttype->annotations_.end());
   }
 
-  /** 
+  /**
    * Determine if all fields of t_struct's storage do not throw
    * Move/Copy Constructors and Assignments applicable for 'noexcept'
    * Move defaults to 'noexcept'
@@ -318,7 +318,7 @@
 
   /**
    * Returns the legal program name to use for a file generated by program, if the
-   * program name contains dots then replace it with underscores, otherwise return the 
+   * program name contains dots then replace it with underscores, otherwise return the
    * original program name.
    */
   std::string get_legal_program_name(std::string program_name);
@@ -981,7 +981,7 @@
 
   for (m_iter = members.begin(); m_iter != members.end(); ++m_iter) {
     t_type* t = get_true_type((*m_iter)->get_type());
-    if (is_reference(*m_iter) || t->is_string()) {
+    if (is_reference(*m_iter) || t->is_string() || t->is_uuid()) {
       t_const_value* cv = (*m_iter)->get_value();
       if (cv != nullptr) {
         return true;
@@ -1032,7 +1032,7 @@
       } else if (t->is_enum()) {
         dval += "static_cast<" + type_name(t) + ">(0)";
       } else {
-        dval += (t->is_string() || is_reference(*m_iter)) ? "" : "0";
+        dval += (t->is_string() || is_reference(*m_iter) || t->is_uuid()) ? "" : "0";
       }
       if (!init_ctor) {
         init_ctor = true;
@@ -1127,7 +1127,7 @@
       has_nonrequired_fields = true;
     indent(out) << (*f_iter)->get_name() << " = "
                 << maybeMove(
-                    tmp_name + "." + (*f_iter)->get_name(), 
+                    tmp_name + "." + (*f_iter)->get_name(),
                     is_move && is_complex_type((*f_iter)->get_type()))
                 << ";" << endl;
   }
@@ -1177,7 +1177,7 @@
       has_nonrequired_fields = true;
     indent(out) << (*f_iter)->get_name() << " = "
                 << maybeMove(
-                    tmp_name + "." + (*f_iter)->get_name(), 
+                    tmp_name + "." + (*f_iter)->get_name(),
                     is_move && is_complex_type((*f_iter)->get_type()))
                 << ";" << endl;
   }
@@ -1276,7 +1276,7 @@
 
     // Move constructor
     if (gen_moveable_) {
-      indent(out) << tstruct->get_name() << "(" << tstruct->get_name() << "&&) noexcept;" 
+      indent(out) << tstruct->get_name() << "(" << tstruct->get_name() << "&&) noexcept;"
                   << endl;
     }
 
@@ -1286,12 +1286,12 @@
 
     // Move assignment operator
     if (gen_moveable_) {
-      indent(out) << tstruct->get_name() << "& operator=(" << tstruct->get_name() << "&&) noexcept;" 
+      indent(out) << tstruct->get_name() << "& operator=(" << tstruct->get_name() << "&&) noexcept;"
                   << endl;
     }
 
     bool has_default_value = has_field_with_default_value(tstruct);
-    
+
     // Default constructor
     std::string clsname_ctor = tstruct->get_name() + "()";
     indent(out) << clsname_ctor << (has_default_value ? "" : " noexcept") << ";" << endl;
@@ -1732,7 +1732,7 @@
 void t_cpp_generator::generate_struct_swap(ostream& out, t_struct* tstruct) {
   if (tstruct->get_name() == "a" || tstruct->get_name() == "b") {
     out << indent() << "void swap(" << tstruct->get_name() << " &a1, " << tstruct->get_name()
-        << " &a2) {" << endl; 
+        << " &a2) {" << endl;
   } else {
     out << indent() << "void swap(" << tstruct->get_name() << " &a, " << tstruct->get_name()
         << " &b) {" << endl;
@@ -1763,7 +1763,7 @@
 
   if (has_nonrequired_fields) {
     if (tstruct->get_name() == "a" || tstruct->get_name() == "b") {
-      out << indent() << "swap(a1.__isset, a2.__isset);" << endl; 
+      out << indent() << "swap(a1.__isset, a2.__isset);" << endl;
     } else {
       out << indent() << "swap(a.__isset, b.__isset);" << endl;
     }
@@ -1942,7 +1942,7 @@
             << endl;
   if (gen_cob_style_) {
     f_header_ << "#include <thrift/transport/TBufferTransports.h>" << endl // TMemoryBuffer
-              << "#include <functional>" << endl 
+              << "#include <functional>" << endl
               << "namespace apache { namespace thrift { namespace async {" << endl
               << "class TAsyncChannel;" << endl << "}}}" << endl;
   }
@@ -2578,7 +2578,7 @@
         f_header_ << ", std::shared_ptr< ::apache::thrift::async::TConcurrentClientSyncInfo> sync";
     }
     f_header_ << ") ";
-    
+
     if (extends.empty()) {
       if (style == "Concurrent") {
         f_header_ << ": sync_(sync)" << endl;
@@ -2677,7 +2677,7 @@
   vector<t_function*>::const_iterator f_iter;
   for (f_iter = functions.begin(); f_iter != functions.end(); ++f_iter) {
     generate_java_doc(f_header_, *f_iter);
-    indent(f_header_) << function_signature(*f_iter, ifstyle) 
+    indent(f_header_) << function_signature(*f_iter, ifstyle)
                       << " override;" << endl;
     // TODO(dreiss): Use private inheritance to avoid generating thise in cob-style.
     if (style == "Concurrent" && !(*f_iter)->is_oneway()) {
@@ -3227,7 +3227,7 @@
   f_header_ << indent() << "virtual " << ret_type_ << "dispatchCall(" << finish_cob_
             << "::apache::thrift::protocol::TProtocol* iprot, "
             << "::apache::thrift::protocol::TProtocol* oprot, "
-            << "const std::string& fname, int32_t seqid" << call_context_ 
+            << "const std::string& fname, int32_t seqid" << call_context_
             << ") override;" << endl;
   if (generator_->gen_templates_) {
     f_header_ << indent() << "virtual " << ret_type_ << "dispatchCallTemplated(" << finish_cob_
@@ -4046,6 +4046,9 @@
     case t_base_type::TYPE_VOID:
       throw "compiler error: cannot serialize void field in a struct: " + name;
       break;
+    case t_base_type::TYPE_UUID:
+      out << "readUUID(" << name << ");";
+      break;
     case t_base_type::TYPE_STRING:
       if (type->is_binary()) {
         out << "readBinary(" << name << ");";
@@ -4072,13 +4075,13 @@
       out << "readDouble(" << name << ");";
       break;
     default:
-      throw "compiler error: no C++ reader for base type " + t_base_type::t_base_name(tbase) + name;
+      throw "compiler error: no C++ reader for base type " + t_base_type::t_base_name(tbase) + " " + name;
     }
     out << endl;
   } else if (type->is_enum()) {
     string t = tmp("ecast");
     out << indent() << "int32_t " << t << ";" << endl << indent() << "xfer += iprot->readI32(" << t
-        << ");" << endl << indent() << name << " = static_cast<" 
+        << ");" << endl << indent() << name << " = static_cast<"
         << type_name(type) << ">(" << t << ");" << endl;
   } else {
     printf("DO NOT KNOW HOW TO DESERIALIZE FIELD '%s' TYPE '%s'\n",
@@ -4254,6 +4257,9 @@
       case t_base_type::TYPE_VOID:
         throw "compiler error: cannot serialize void field in a struct: " + name;
         break;
+      case t_base_type::TYPE_UUID:
+        out << "writeUUID(" << name << ");";
+        break;
       case t_base_type::TYPE_STRING:
         if (type->is_binary()) {
           out << "writeBinary(" << name << ");";
@@ -4281,7 +4287,7 @@
         break;
       default:
         throw "compiler error: no C++ writer for base type " + t_base_type::t_base_name(tbase)
-            + name;
+            + " " + name;
       }
     } else if (type->is_enum()) {
       out << "writeI32(static_cast<int32_t>(" << name << "));";
@@ -4568,6 +4574,9 @@
     return "int64_t";
   case t_base_type::TYPE_DOUBLE:
     return "double";
+  case t_base_type::TYPE_UUID:
+    // TODO: discuss possibility of a class TUuid;
+    return "std::string";
   default:
     throw "compiler error: no C++ base type name for base type " + t_base_type::t_base_name(tbase);
   }
@@ -4609,6 +4618,9 @@
       case t_base_type::TYPE_VOID:
       case t_base_type::TYPE_STRING:
         break;
+      case t_base_type::TYPE_UUID:
+        result += " = std::string(\"00000000-0000-0000-0000-000000000000\")";
+        break;
       case t_base_type::TYPE_BOOL:
         result += " = false";
         break;
@@ -4735,6 +4747,8 @@
       return "::apache::thrift::protocol::T_I64";
     case t_base_type::TYPE_DOUBLE:
       return "::apache::thrift::protocol::T_DOUBLE";
+    case t_base_type::TYPE_UUID:
+      return "::apache::thrift::protocol::T_UUID";
     default:
       break;
     }
@@ -4776,6 +4790,7 @@
         continue;
       case t_base_type::TYPE_VOID:
       case t_base_type::TYPE_STRING:
+      case t_base_type::TYPE_UUID:
       default:
         return false;
     }
diff --git a/lib/cpp/CMakeLists.txt b/lib/cpp/CMakeLists.txt
index 6a66e5a..c2f15dd 100644
--- a/lib/cpp/CMakeLists.txt
+++ b/lib/cpp/CMakeLists.txt
@@ -43,6 +43,7 @@
    src/thrift/protocol/TJSONProtocol.cpp
    src/thrift/protocol/TMultiplexedProtocol.cpp
    src/thrift/protocol/TProtocol.cpp
+   src/thrift/protocol/TUuidUtils.cpp
    src/thrift/transport/TTransportException.cpp
    src/thrift/transport/TFDTransport.cpp
    src/thrift/transport/TSimpleFileTransport.cpp
diff --git a/lib/cpp/Makefile.am b/lib/cpp/Makefile.am
index 2499fdb..12b8d82 100644
--- a/lib/cpp/Makefile.am
+++ b/lib/cpp/Makefile.am
@@ -69,6 +69,7 @@
                        src/thrift/protocol/TBase64Utils.cpp \
                        src/thrift/protocol/TMultiplexedProtocol.cpp \
                        src/thrift/protocol/TProtocol.cpp \
+                       src/thrift/protocol/TUuidUtils.cpp \
                        src/thrift/transport/TTransportException.cpp \
                        src/thrift/transport/TFDTransport.cpp \
                        src/thrift/transport/TFileTransport.cpp \
diff --git a/lib/cpp/libthrift.vcxproj b/lib/cpp/libthrift.vcxproj
index 0b5e16d..1b413f8 100644
--- a/lib/cpp/libthrift.vcxproj
+++ b/lib/cpp/libthrift.vcxproj
@@ -49,6 +49,7 @@
     <ClCompile Include="src\thrift\protocol\TJSONProtocol.cpp" />
     <ClCompile Include="src\thrift\protocol\TMultiplexedProtocol.cpp" />
     <ClCompile Include="src\thrift\protocol\TProtocol.cpp" />
+    <ClCompile Include="src\thrift\protocol\TUuidUtils.cpp" />
     <ClCompile Include="src\thrift\server\TConnectedClient.cpp" />
     <ClCompile Include="src\thrift\server\TServer.cpp" />
     <ClCompile Include="src\thrift\server\TServerFramework.cpp" />
diff --git a/lib/cpp/libthrift.vcxproj.filters b/lib/cpp/libthrift.vcxproj.filters
index 98426fa..fb94f60 100644
--- a/lib/cpp/libthrift.vcxproj.filters
+++ b/lib/cpp/libthrift.vcxproj.filters
@@ -30,6 +30,9 @@
     <ClCompile Include="src\thrift\protocol\TMultiplexedProtocol.cpp">
       <Filter>protocol</Filter>
     </ClCompile>
+    <ClCompile Include="src\thrift\protocol\TUuidUtils.cpp">
+      <Filter>protocol</Filter>
+    </ClCompile>
     <ClCompile Include="src\thrift\transport\TFDTransport.cpp">
       <Filter>transport</Filter>
     </ClCompile>
diff --git a/lib/cpp/src/thrift/protocol/TBinaryProtocol.h b/lib/cpp/src/thrift/protocol/TBinaryProtocol.h
index 7b829c7..24e51f7 100644
--- a/lib/cpp/src/thrift/protocol/TBinaryProtocol.h
+++ b/lib/cpp/src/thrift/protocol/TBinaryProtocol.h
@@ -119,6 +119,8 @@
 
   inline uint32_t writeBinary(const std::string& str);
 
+  inline uint32_t writeUUID(const std::string& str);
+
   /**
    * Reading functions
    */
@@ -166,6 +168,8 @@
 
   inline uint32_t readBinary(std::string& str);
 
+  inline uint32_t readUUID(std::string& str);
+
   int getMinSerializedSize(TType type) override;
 
   void checkReadBytesAvailable(TSet& set) override
diff --git a/lib/cpp/src/thrift/protocol/TBinaryProtocol.tcc b/lib/cpp/src/thrift/protocol/TBinaryProtocol.tcc
index c448e77..6a54ece 100644
--- a/lib/cpp/src/thrift/protocol/TBinaryProtocol.tcc
+++ b/lib/cpp/src/thrift/protocol/TBinaryProtocol.tcc
@@ -21,6 +21,7 @@
 #define _THRIFT_PROTOCOL_TBINARYPROTOCOL_TCC_ 1
 
 #include <thrift/protocol/TBinaryProtocol.h>
+#include <thrift/protocol/TUuidUtils.hpp>
 #include <thrift/transport/TTransportException.h>
 
 #include <limits>
@@ -193,6 +194,20 @@
   return TBinaryProtocolT<Transport_, ByteOrder_>::writeString(str);
 }
 
+template <class Transport_, class ByteOrder_>
+uint32_t TBinaryProtocolT<Transport_, ByteOrder_>::writeUUID(const std::string& str) {
+  std::string out;
+  const bool encoded = uuid_encode(str, out);
+  if(!encoded)
+    throw TProtocolException(TProtocolException::INVALID_DATA);
+  // This should not happen, but check for now
+  if(out.size() != 16)
+    throw TProtocolException(TProtocolException::UNKNOWN);
+  // TODO: Consider endian swapping, see lib/delphi/src/Thrift.Utils.pas:377
+  this->trans_->write((uint8_t*)out.data(), 16);
+  return 16;
+}
+
 /**
  * Reading functions
  */
@@ -286,7 +301,7 @@
     throw TProtocolException(TProtocolException::SIZE_LIMIT);
   }
   size = (uint32_t)sizei;
-  
+
   TMap map(keyType, valType, size);
   checkReadBytesAvailable(map);
 
@@ -429,6 +444,14 @@
 }
 
 template <class Transport_, class ByteOrder_>
+uint32_t TBinaryProtocolT<Transport_, ByteOrder_>::readUUID(std::string& str) {
+  std::string in;
+  readStringBody(in, 16);
+  uuid_decode(in, str);
+  return 16;
+}
+
+template <class Transport_, class ByteOrder_>
 template <typename StrType>
 uint32_t TBinaryProtocolT<Transport_, ByteOrder_>::readStringBody(StrType& str, int32_t size) {
   uint32_t result = 0;
diff --git a/lib/cpp/src/thrift/protocol/TDebugProtocol.cpp b/lib/cpp/src/thrift/protocol/TDebugProtocol.cpp
index 0e6d4a2..0a2eaed 100644
--- a/lib/cpp/src/thrift/protocol/TDebugProtocol.cpp
+++ b/lib/cpp/src/thrift/protocol/TDebugProtocol.cpp
@@ -18,6 +18,7 @@
  */
 
 #include <thrift/protocol/TDebugProtocol.h>
+#include <thrift/protocol/TUuidUtils.hpp>
 
 #include <thrift/TToString.h>
 #include <cassert>
@@ -70,10 +71,8 @@
     return "set";
   case T_LIST:
     return "list";
-  case T_UTF8:
-    return "utf8";
-  case T_UTF16:
-    return "utf16";
+  case T_UUID:
+    return "uuid";
   default:
     return "unknown";
   }
@@ -388,6 +387,24 @@
   // XXX Hex?
   return TDebugProtocol::writeString(str);
 }
+
+uint32_t TDebugProtocol::writeUUID(const string& str) {
+  std::string out_raw;
+  uuid_encode(str, out_raw);
+
+  std::string out_encoded;
+  uuid_decode(out_raw, out_encoded);
+
+  size_t size = writePlain("{\n");
+  indentUp();
+  size += writeIndented("[in ] = \"" + str + "\",\n");
+  size += writeIndented("[raw] = ");
+  size += writeString(out_raw);
+  size += writeIndented("[enc] = \"" + out_encoded + "\"\n");
+  indentDown();
+  size += writeIndented("}\n");
+  return size;
+}
 }
 }
 } // apache::thrift::protocol
diff --git a/lib/cpp/src/thrift/protocol/TDebugProtocol.h b/lib/cpp/src/thrift/protocol/TDebugProtocol.h
index 41bb0d4..af89cc5 100644
--- a/lib/cpp/src/thrift/protocol/TDebugProtocol.h
+++ b/lib/cpp/src/thrift/protocol/TDebugProtocol.h
@@ -110,6 +110,8 @@
 
   uint32_t writeBinary(const std::string& str);
 
+  uint32_t writeUUID(const std::string& str);
+
 private:
   void indentUp();
   void indentDown();
diff --git a/lib/cpp/src/thrift/protocol/TEnum.h b/lib/cpp/src/thrift/protocol/TEnum.h
index 9636785..bbd1247 100644
--- a/lib/cpp/src/thrift/protocol/TEnum.h
+++ b/lib/cpp/src/thrift/protocol/TEnum.h
@@ -18,7 +18,7 @@
  */
 
 #ifndef _THRIFT_ENUM_H_
-#define _THRIFT_ENUM_H_ 
+#define _THRIFT_ENUM_H_
 
 namespace apache {
 namespace thrift {
@@ -46,8 +46,7 @@
   T_MAP        = 13,
   T_SET        = 14,
   T_LIST       = 15,
-  T_UTF8       = 16,
-  T_UTF16      = 17
+  T_UUID       = 16,
 };
 
 /**
@@ -63,4 +62,4 @@
 
 }}} // apache::thrift::protocol
 
-#endif // #define _THRIFT_ENUM_H_ 
+#endif // #define _THRIFT_ENUM_H_
diff --git a/lib/cpp/src/thrift/protocol/TJSONProtocol.cpp b/lib/cpp/src/thrift/protocol/TJSONProtocol.cpp
index 6e4e8ef..0899f00 100644
--- a/lib/cpp/src/thrift/protocol/TJSONProtocol.cpp
+++ b/lib/cpp/src/thrift/protocol/TJSONProtocol.cpp
@@ -18,6 +18,7 @@
  */
 
 #include <thrift/protocol/TJSONProtocol.h>
+#include <thrift/protocol/TUuidUtils.hpp>
 
 #include <boost/locale.hpp>
 
@@ -68,6 +69,7 @@
 static const std::string kTypeNameMap("map");
 static const std::string kTypeNameList("lst");
 static const std::string kTypeNameSet("set");
+static const std::string kTypeNameUuid("uid");
 
 static const std::string& getTypeNameForTypeID(TType typeID) {
   switch (typeID) {
@@ -93,6 +95,8 @@
     return kTypeNameSet;
   case T_LIST:
     return kTypeNameList;
+  case T_UUID:
+    return kTypeNameUuid;
   default:
     throw TProtocolException(TProtocolException::NOT_IMPLEMENTED, "Unrecognized type");
   }
@@ -140,6 +144,9 @@
     case 't':
       result = T_BOOL;
       break;
+    case 'u':
+      result = T_UUID;
+      break;
     }
   }
   if (result == T_STOP) {
@@ -710,6 +717,16 @@
   return writeJSONBase64(str);
 }
 
+uint32_t TJSONProtocol::writeUUID(const std::string& str) {
+  std::string out_raw;
+  uuid_encode(str, out_raw);
+
+  std::string out_encoded;
+  uuid_decode(out_raw, out_encoded);
+
+  return writeJSONString(out_encoded);
+}
+
 /**
  * Reading functions
  */
@@ -1106,6 +1123,10 @@
   return readJSONBase64(str);
 }
 
+uint32_t TJSONProtocol::readUUID(std::string& str) {
+  return readJSONString(str);
+}
+
 // Return the minimum number of bytes a type will consume on the wire
 int TJSONProtocol::getMinSerializedSize(TType type)
 {
@@ -1113,7 +1134,7 @@
   {
     case T_STOP: return 0;
     case T_VOID: return 0;
-    case T_BOOL: return 1;  // written as int  
+    case T_BOOL: return 1;  // written as int
     case T_BYTE: return 1;
     case T_DOUBLE: return 1;
     case T_I16: return 1;
@@ -1124,6 +1145,7 @@
     case T_MAP: return 2;  // empty map
     case T_SET: return 2;  // empty set
     case T_LIST: return 2;  // empty list
+    case T_UUID: return 16;  // empty UUID
     default: throw TProtocolException(TProtocolException::UNKNOWN, "unrecognized type code");
   }
 }
diff --git a/lib/cpp/src/thrift/protocol/TJSONProtocol.h b/lib/cpp/src/thrift/protocol/TJSONProtocol.h
index d01bdf8..069a990 100644
--- a/lib/cpp/src/thrift/protocol/TJSONProtocol.h
+++ b/lib/cpp/src/thrift/protocol/TJSONProtocol.h
@@ -198,6 +198,8 @@
 
   uint32_t writeBinary(const std::string& str);
 
+  uint32_t writeUUID(const std::string& str);
+
   /**
    * Reading functions
    */
@@ -245,6 +247,8 @@
 
   uint32_t readBinary(std::string& str);
 
+  uint32_t readUUID(std::string& str);
+
   int getMinSerializedSize(TType type) override;
 
   void checkReadBytesAvailable(TSet& set) override
diff --git a/lib/cpp/src/thrift/protocol/TProtocol.h b/lib/cpp/src/thrift/protocol/TProtocol.h
index 237c1e5..d29df1c 100644
--- a/lib/cpp/src/thrift/protocol/TProtocol.h
+++ b/lib/cpp/src/thrift/protocol/TProtocol.h
@@ -275,6 +275,8 @@
 
   virtual uint32_t writeBinary_virt(const std::string& str) = 0;
 
+  virtual uint32_t writeUUID_virt(const std::string& str) = 0;
+
   uint32_t writeMessageBegin(const std::string& name,
                              const TMessageType messageType,
                              const int32_t seqid) {
@@ -382,6 +384,11 @@
     return writeBinary_virt(str);
   }
 
+  uint32_t writeUUID(const std::string& str) {
+    T_VIRTUAL_CALL();
+    return writeUUID_virt(str);
+  }
+
   /**
    * Reading functions
    */
@@ -430,6 +437,8 @@
 
   virtual uint32_t readBinary_virt(std::string& str) = 0;
 
+  virtual uint32_t readUUID_virt(std::string& str) = 0;
+
   uint32_t readMessageBegin(std::string& name, TMessageType& messageType, int32_t& seqid) {
     T_VIRTUAL_CALL();
     return readMessageBegin_virt(name, messageType, seqid);
@@ -530,6 +539,11 @@
     return readBinary_virt(str);
   }
 
+  uint32_t readUUID(std::string& str) {
+    T_VIRTUAL_CALL();
+    return readUUID_virt(str);
+  }
+
   /*
    * std::vector is specialized for bool, and its elements are individual bits
    * rather than bools.   We need to define a different version of readBool()
diff --git a/lib/cpp/src/thrift/protocol/TProtocolDecorator.h b/lib/cpp/src/thrift/protocol/TProtocolDecorator.h
index 5258159..9eb1566 100644
--- a/lib/cpp/src/thrift/protocol/TProtocolDecorator.h
+++ b/lib/cpp/src/thrift/protocol/TProtocolDecorator.h
@@ -92,6 +92,7 @@
   uint32_t writeDouble_virt(const double dub) override { return protocol->writeDouble(dub); }
   uint32_t writeString_virt(const std::string& str) override { return protocol->writeString(str); }
   uint32_t writeBinary_virt(const std::string& str) override { return protocol->writeBinary(str); }
+  uint32_t writeUUID_virt(const std::string& str) override { return protocol->writeUUID(str); }
 
   uint32_t readMessageBegin_virt(std::string& name,
                                          TMessageType& messageType,
@@ -140,6 +141,7 @@
 
   uint32_t readString_virt(std::string& str) override { return protocol->readString(str); }
   uint32_t readBinary_virt(std::string& str) override { return protocol->readBinary(str); }
+  uint32_t readUUID_virt(std::string& str) override { return protocol->readUUID(str); }
 
 private:
   shared_ptr<TProtocol> protocol;
diff --git a/lib/cpp/src/thrift/protocol/TUuidUtils.cpp b/lib/cpp/src/thrift/protocol/TUuidUtils.cpp
new file mode 100644
index 0000000..e1eab22
--- /dev/null
+++ b/lib/cpp/src/thrift/protocol/TUuidUtils.cpp
@@ -0,0 +1,56 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+#include <thrift/protocol/TUuidUtils.hpp>
+
+#include <boost/uuid/string_generator.hpp>
+#include <boost/uuid/uuid.hpp>
+#include <boost/uuid/uuid_io.hpp>
+
+namespace apache {
+namespace thrift {
+namespace protocol {
+
+bool uuid_encode(const std::string& in, std::string& out) {
+  static const boost::uuids::string_generator gen;
+  static const std::string empty_uuid(boost::uuids::uuid::static_size(), '\0');
+  out = empty_uuid;
+  if (in.empty()) {
+    return true;
+  }
+  try {
+    const boost::uuids::uuid uuid{gen(in)};
+    std::copy(uuid.begin(), uuid.end(), out.begin());
+    return true;
+  } catch (const std::runtime_error&) {
+    // Invalid string most probably
+    return false;
+  }
+}
+
+void uuid_decode(const std::string& in, std::string& out) {
+  boost::uuids::uuid uuid{};
+  const size_t to_copy = std::min(in.size(), uuid.size());
+  std::copy(in.begin(), in.begin() + to_copy, uuid.begin());
+  out = boost::uuids::to_string(uuid);
+}
+
+}
+}
+} // apache::thrift::protocol
diff --git a/lib/cpp/src/thrift/protocol/TUuidUtils.hpp b/lib/cpp/src/thrift/protocol/TUuidUtils.hpp
new file mode 100644
index 0000000..583147f
--- /dev/null
+++ b/lib/cpp/src/thrift/protocol/TUuidUtils.hpp
@@ -0,0 +1,45 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+#ifndef _THRIFT_PROTOCOL_TUUIDUTILS_H_
+#define _THRIFT_PROTOCOL_TUUIDUTILS_H_
+
+#include <string>
+
+namespace apache {
+namespace thrift {
+namespace protocol {
+
+// Encode canonical UUID string to a 16 char representation
+// Supported formats for in:
+//   - "hhhhhhhh-hhhh-hhhh-hhhh-hhhhhhhhhhhh"
+//   - "{hhhhhhhh-hhhh-hhhh-hhhh-hhhhhhhhhhhh}"
+//   - "hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh"
+//   - "{hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh}"
+// Returns false if the string was invalid and the value was not encoded.
+bool uuid_encode(const std::string& in, std::string& out);
+
+// Decode 16 char UUID buffer to 36 characted string
+void uuid_decode(const std::string& in, std::string& out);
+
+}
+}
+} // apache::thrift::protocol
+
+#endif // #define _THRIFT_PROTOCOL_TUUIDUTILS_H_
diff --git a/lib/cpp/src/thrift/protocol/TVirtualProtocol.h b/lib/cpp/src/thrift/protocol/TVirtualProtocol.h
index b7fe929..4698081 100644
--- a/lib/cpp/src/thrift/protocol/TVirtualProtocol.h
+++ b/lib/cpp/src/thrift/protocol/TVirtualProtocol.h
@@ -393,6 +393,10 @@
     return static_cast<Protocol_*>(this)->writeBinary(str);
   }
 
+  uint32_t writeUUID_virt(const std::string& str) override {
+    return static_cast<Protocol_*>(this)->writeUUID(str);
+  }
+
   /**
    * Reading functions
    */
@@ -471,6 +475,10 @@
     return static_cast<Protocol_*>(this)->readBinary(str);
   }
 
+  uint32_t readUUID_virt(std::string& str) override {
+    return static_cast<Protocol_*>(this)->readUUID(str);
+  }
+
   uint32_t skip_virt(TType type) override { return static_cast<Protocol_*>(this)->skip(type); }
 
   /*
diff --git a/lib/cpp/test/Benchmark.cpp b/lib/cpp/test/Benchmark.cpp
index 56adac0..97a5317 100644
--- a/lib/cpp/test/Benchmark.cpp
+++ b/lib/cpp/test/Benchmark.cpp
@@ -66,6 +66,7 @@
   ooe.some_characters = "JSON THIS! \"\1";
   ooe.zomg_unicode = "\xd7\n\a\t";
   ooe.base64 = "\1\2\3\255";
+  ooe.rfc4122_uuid = "{5e2ab188-1726-4e75-a04f-1ed9a6a89c4c}";
 
   int num = 100000;
   std::shared_ptr<TMemoryBuffer> buf(new TMemoryBuffer(num*1000));
diff --git a/lib/cpp/test/CMakeLists.txt b/lib/cpp/test/CMakeLists.txt
index 1117cd9..87ed109 100644
--- a/lib/cpp/test/CMakeLists.txt
+++ b/lib/cpp/test/CMakeLists.txt
@@ -360,7 +360,7 @@
 )
 
 add_custom_command(OUTPUT gen-cpp/DebugProtoTest_types.cpp gen-cpp/DebugProtoTest_types.h gen-cpp/EmptyService.cpp gen-cpp/EmptyService.h
-    COMMAND ${THRIFT_COMPILER} --gen cpp ${PROJECT_SOURCE_DIR}/test/v0.16/DebugProtoTest.thrift
+    COMMAND ${THRIFT_COMPILER} --gen cpp ${PROJECT_SOURCE_DIR}/test/DebugProtoTest.thrift
 )
 
 add_custom_command(OUTPUT gen-cpp/EnumTest_types.cpp gen-cpp/EnumTest_types.h
@@ -384,7 +384,7 @@
 )
 
 add_custom_command(OUTPUT gen-cpp/SecondService.cpp gen-cpp/ThriftTest_constants.cpp gen-cpp/ThriftTest.cpp gen-cpp/ThriftTest_types.cpp gen-cpp/ThriftTest_types.h
-    COMMAND ${THRIFT_COMPILER} --gen cpp ${PROJECT_SOURCE_DIR}/test/v0.16/ThriftTest.thrift
+    COMMAND ${THRIFT_COMPILER} --gen cpp ${PROJECT_SOURCE_DIR}/test/ThriftTest.thrift
 )
 
 add_custom_command(OUTPUT gen-cpp/OneWayService.cpp gen-cpp/OneWayTest_types.h gen-cpp/OneWayService.h
diff --git a/lib/cpp/test/DebugProtoTest.cpp b/lib/cpp/test/DebugProtoTest.cpp
index 060f354..cc4e5ff 100644
--- a/lib/cpp/test/DebugProtoTest.cpp
+++ b/lib/cpp/test/DebugProtoTest.cpp
@@ -74,6 +74,11 @@
     "    [1] = 2,\n"
     "    [2] = 3,\n"
     "  },\n"
+    "  15: rfc4122_uuid (uuid) = {\n"
+    "    [in ] = \"\",\n"
+    "    [raw] = \"\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\",\n"
+    "    [enc] = \"00000000-0000-0000-0000-000000000000\"\n"
+    "  }\n"
     "}");
   const std::string result(apache::thrift::ThriftDebugString(*ooe));
 
@@ -98,6 +103,7 @@
                                "\xb0\xcf\x81\xe2\x84\x8e\x20\xce\x91\x74\x74"
                                "\xce\xb1\xe2\x85\xbd\xce\xba\xc7\x83\xe2\x80"
                                "\xbc";
+  n->my_ooe.rfc4122_uuid = "{5e2ab188-1726-4e75-a04f-1ed9a6a89c4c}";
   n->my_bonk.type = 31337;
   n->my_bonk.message = "I am a bonk... xor!";
 }
@@ -141,6 +147,11 @@
     "      [1] = 2,\n"
     "      [2] = 3,\n"
     "    },\n"
+    "    15: rfc4122_uuid (uuid) = {\n"
+    "      [in ] = \"{5e2ab188-1726-4e75-a04f-1ed9a6a89c4c}\",\n"
+    "      [raw] = \"^*\\xb1\\x88\\x17&Nu\\xa0O\\x1e\\xd9\\xa6\\xa8\\x9cL\",\n"
+    "      [enc] = \"5e2ab188-1726-4e75-a04f-1ed9a6a89c4c\"\n"
+    "    }\n"
     "  },\n"
     "}");
   const std::string result(apache::thrift::ThriftDebugString(*n));
@@ -228,6 +239,11 @@
     "        [1] = 2,\n"
     "        [2] = 3,\n"
     "      },\n"
+    "      15: rfc4122_uuid (uuid) = {\n"
+    "        [in ] = \"\",\n"
+    "        [raw] = \"\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\",\n"
+    "        [enc] = \"00000000-0000-0000-0000-000000000000\"\n"
+    "      }\n"
     "    },\n"
     "    [1] = OneOfEach {\n"
     "      01: im_true (bool) = true,\n"
@@ -259,6 +275,11 @@
     "        [1] = 2,\n"
     "        [2] = 3,\n"
     "      },\n"
+    "      15: rfc4122_uuid (uuid) = {\n"
+    "        [in ] = \"{5e2ab188-1726-4e75-a04f-1ed9a6a89c4c}\",\n"
+    "        [raw] = \"^*\\xb1\\x88\\x17&Nu\\xa0O\\x1e\\xd9\\xa6\\xa8\\x9cL\",\n"
+    "        [enc] = \"5e2ab188-1726-4e75-a04f-1ed9a6a89c4c\"\n"
+    "      }\n"
     "    },\n"
     "  },\n"
     "  02: contain (set) = set<list>[3] {\n"
diff --git a/lib/cpp/test/JSONProtoTest.cpp b/lib/cpp/test/JSONProtoTest.cpp
index 082c8a2..fedf99e 100644
--- a/lib/cpp/test/JSONProtoTest.cpp
+++ b/lib/cpp/test/JSONProtoTest.cpp
@@ -48,6 +48,7 @@
   ooe->some_characters = "JSON THIS! \"\1";
   ooe->zomg_unicode = "\xd7\n\a\t";
   ooe->base64 = "\1\2\3\255";
+  ooe->rfc4122_uuid = "00000000-0000-0000-0000-000000000000";
 }
 
 BOOST_AUTO_TEST_CASE(test_json_proto_1) {
@@ -59,7 +60,7 @@
   "535897931},\"8\":{\"str\":\"JSON THIS! \\\"\\u0001\"},\"9\":{\"str\":\"\xd7\\"
   "n\\u0007\\t\"},\"10\":{\"tf\":0},\"11\":{\"str\":\"AQIDrQ\"},\"12\":{\"lst\""
   ":[\"i8\",3,1,2,3]},\"13\":{\"lst\":[\"i16\",3,1,2,3]},\"14\":{\"lst\":[\"i64"
-  "\",3,1,2,3]}}");
+  "\",3,1,2,3]},\"15\":{\"uid\":\"00000000-0000-0000-0000-000000000000\"}}");
 
   const std::string result(apache::thrift::ThriftJSONString(*ooe));
 
@@ -84,6 +85,7 @@
                                "\xb0\xcf\x81\xe2\x84\x8e\x20\xce\x91\x74\x74"
                                "\xce\xb1\xe2\x85\xbd\xce\xba\xc7\x83\xe2\x80"
                                "\xbc";
+  n->my_ooe.rfc4122_uuid = "5e2ab188-1726-4e75-a04f-1ed9a6a89c4c";
   n->my_bonk.type = 31337;
   n->my_bonk.message = "I am a bonk... xor!";
 }
@@ -98,7 +100,8 @@
     "1.6180339887498949},\"8\":{\"str\":\":R (me going \\\"rrrr\\\")\"},\"9\":{"
     "\"str\":\"ӀⅮΝ Нοⅿоɡгаρℎ Αttαⅽκǃ‼\"},\"10\":{\"tf\":0},\"11\":{\"str\":\""
     "AQIDrQ\"},\"12\":{\"lst\":[\"i8\",3,1,2,3]},\"13\":{\"lst\":[\"i16\",3,1,2"
-    ",3]},\"14\":{\"lst\":[\"i64\",3,1,2,3]}}}}"
+    ",3]},\"14\":{\"lst\":[\"i64\",3,1,2,3]},\"15\":{\"uid\":\"5e2ab188-1726-"
+    "4e75-a04f-1ed9a6a89c4c\"}}}}"
   );
 
   const std::string result(apache::thrift::ThriftJSONString(*n));
@@ -162,12 +165,14 @@
   "},\"7\":{\"dbl\":3.1415926535897931},\"8\":{\"str\":\"JSON THIS! \\\"\\u0001"
   "\"},\"9\":{\"str\":\"\xd7\\n\\u0007\\t\"},\"10\":{\"tf\":0},\"11\":{\"str\":"
   "\"AQIDrQ\"},\"12\":{\"lst\":[\"i8\",3,1,2,3]},\"13\":{\"lst\":[\"i16\",3,1,2"
-  ",3]},\"14\":{\"lst\":[\"i64\",3,1,2,3]}},{\"1\":{\"tf\":1},\"2\":{\"tf\":0},"
+  ",3]},\"14\":{\"lst\":[\"i64\",3,1,2,3]},\"15\":{\"uid\":\"00000000-0000-0000"
+  "-0000-000000000000\"}},{\"1\":{\"tf\":1},\"2\":{\"tf\":0},"
   "\"3\":{\"i8\":51},\"4\":{\"i16\":16},\"5\":{\"i32\":32},\"6\":{\"i64\":64},"
   "\"7\":{\"dbl\":1.6180339887498949},\"8\":{\"str\":\":R (me going \\\"rrrr\\\""
   ")\"},\"9\":{\"str\":\"ӀⅮΝ Нοⅿоɡгаρℎ Αttαⅽκǃ‼\"},\"10\":{\"tf\":0},\"11\":{"
   "\"str\":\"AQIDrQ\"},\"12\":{\"lst\":[\"i8\",3,1,2,3]},\"13\":{\"lst\":[\"i16"
-  "\",3,1,2,3]},\"14\":{\"lst\":[\"i64\",3,1,2,3]}}]},\"2\":{\"set\":[\"lst\",3"
+  "\",3,1,2,3]},\"14\":{\"lst\":[\"i64\",3,1,2,3]},\"15\":{\"uid\":\"5e2ab188-"
+  "1726-4e75-a04f-1ed9a6a89c4c\"}}]},\"2\":{\"set\":[\"lst\",3"
   ",[\"str\",0],[\"str\",2,\"and a one\",\"and a two\"],[\"str\",3,\"then a one"
   ", two\",\"three!\",\"FOUR!!\"]]},\"3\":{\"map\":[\"str\",\"lst\",3,{\"nothin"
   "g\":[\"rec\",0],\"poe\":[\"rec\",3,{\"1\":{\"i32\":3},\"2\":{\"str\":\"quoth"
@@ -192,6 +197,8 @@
   OneOfEach ooe2;
   ooe2.read(proto.get());
 
+  BOOST_TEST_INFO("written: " << *ooe);
+  BOOST_TEST_INFO("read   : " << ooe2);
   BOOST_CHECK(*ooe == ooe2);
 }
 
@@ -205,6 +212,8 @@
   HolyMoley hm2;
   hm2.read(proto.get());
 
+  BOOST_TEST_INFO("written: " << *hm);
+  BOOST_TEST_INFO("read   : " << hm2);
   BOOST_CHECK(*hm == hm2);
 
   hm2.big[0].a_bite = 0x00;
@@ -230,14 +239,14 @@
   );
 
   std::shared_ptr<TMemoryBuffer> buffer(new TMemoryBuffer());
-  std::shared_ptr<TJSONProtocol> proto(new TJSONProtocol(buffer)); 
+  std::shared_ptr<TJSONProtocol> proto(new TJSONProtocol(buffer));
   dub.write(proto.get());
   Doubles dub_1;
   dub_1.read(proto.get());
 
   const std::string result(apache::thrift::ThriftJSONString(dub));
   const std::string result_1(apache::thrift::ThriftJSONString(dub_1));
-  
+
   BOOST_CHECK_MESSAGE(!expected_result.compare(result),
     "Expected:\n" << expected_result << "\nGotten:\n" << result);
   BOOST_CHECK_MESSAGE(!expected_result.compare(result_1),
diff --git a/lib/cpp/test/SpecializationTest.cpp b/lib/cpp/test/SpecializationTest.cpp
index 008837d..0976112 100644
--- a/lib/cpp/test/SpecializationTest.cpp
+++ b/lib/cpp/test/SpecializationTest.cpp
@@ -26,6 +26,7 @@
   ooe.some_characters = "JSON THIS! \"\1";
   ooe.zomg_unicode = "\xd7\n\a\t";
   ooe.base64 = "\1\2\3\255";
+  ooe.rfc4122_uuid = "00000000-0000-0000-0000-000000000000";
 
   Nesting n;
   n.my_ooe = ooe;
@@ -89,6 +90,8 @@
   OneOfEach ooe2;
   ooe2.read(proto.get());
 
+  BOOST_TEST_INFO("Write: " << ooe);
+  BOOST_TEST_INFO("Read : " << ooe2);
   BOOST_CHECK(ooe == ooe2);
 
   hm.write(proto.get());
diff --git a/lib/cpp/test/ToStringTest.cpp b/lib/cpp/test/ToStringTest.cpp
index 736b33c..68c82ad 100644
--- a/lib/cpp/test/ToStringTest.cpp
+++ b/lib/cpp/test/ToStringTest.cpp
@@ -160,4 +160,13 @@
                     "ListBonks(bonk=[Bonk(message=a, type=0), Bonk(message=b, type=0)])");
 }
 
+BOOST_AUTO_TEST_CASE(generated_uuid_to_string) {
+  thrift::test::CrazyNesting l;
+  l.uuid_field = "{4b686716-5f20-4deb-8ce0-9eaf379e8a3d}";
+
+  BOOST_CHECK_EQUAL(to_string(l),
+                    "CrazyNesting(string_field=, set_field=<null>, list_field=[], binary_field=, "
+                    "uuid_field={4b686716-5f20-4deb-8ce0-9eaf379e8a3d})");
+}
+
 BOOST_AUTO_TEST_SUITE_END()
diff --git a/test/cpp/CMakeLists.txt b/test/cpp/CMakeLists.txt
index f693a27..a6c1fd5 100644
--- a/test/cpp/CMakeLists.txt
+++ b/test/cpp/CMakeLists.txt
@@ -100,7 +100,7 @@
 #
 
 add_custom_command(OUTPUT gen-cpp/SecondService.cpp gen-cpp/SecondService.h gen-cpp/ThriftTest.cpp gen-cpp/ThriftTest.h gen-cpp/ThriftTest_types.cpp gen-cpp/ThriftTest_constants.cpp
-    COMMAND ${THRIFT_COMPILER} --gen cpp:templates,cob_style -r ${PROJECT_SOURCE_DIR}/test/v0.16/ThriftTest.thrift
+    COMMAND ${THRIFT_COMPILER} --gen cpp:templates,cob_style -r ${PROJECT_SOURCE_DIR}/test/ThriftTest.thrift
 )
 
 add_custom_command(OUTPUT gen-cpp/Service.cpp
diff --git a/test/cpp/src/TestClient.cpp b/test/cpp/src/TestClient.cpp
index c4146cc..fd04ed8 100644
--- a/test/cpp/src/TestClient.cpp
+++ b/test/cpp/src/TestClient.cpp
@@ -70,7 +70,7 @@
 //
 
 template<typename Proto>
-class TPedanticProtocol : public Proto 
+class TPedanticProtocol : public Proto
 {
     public:
         TPedanticProtocol(std::shared_ptr<TTransport>& transport)
@@ -178,6 +178,18 @@
     return_code |= ERR_BASETYPES;                                                                  \
   }
 
+#define UUID_TEST(func, value, expected)                                                           \
+  cout << #func "(" << value << ") = ";                                                            \
+  try {                                                                                            \
+    if (!print_eq(expected, testClient.func(value)))                                               \
+      return_code |= ERR_BASETYPES;                                                                \
+  } catch (TTransportException&) {                                                                 \
+    throw;                                                                                         \
+  } catch (exception & ex) {                                                                       \
+    cout << "*** FAILED ***" << endl << ex.what() << endl;                                         \
+    return_code |= ERR_BASETYPES;                                                                  \
+  }
+
 int binary_test(ThriftTestClient& testClient, string::size_type siz);
 
 BOOST_CONSTEXPR_OR_CONST int ERR_BASETYPES = 1;
@@ -638,6 +650,15 @@
       if (i > 0) { i *= 2; } else { ++i; }
     }
 
+    /**
+     * UUID TEST
+     */
+    const std::string expected_uuid{"5e2ab188-1726-4e75-a04f-1ed9a6a89c4c"};
+    UUID_TEST(testUuid, std::string{"{5e2ab188-1726-4e75-a04f-1ed9a6a89c4c}"}, expected_uuid);
+    UUID_TEST(testUuid, std::string{"5e2ab188-1726-4e75-a04f-1ed9a6a89c4c"}, expected_uuid);
+    UUID_TEST(testUuid, std::string{"5e2ab18817264e75a04f1ed9a6a89c4c"}, expected_uuid);
+    UUID_TEST(testUuid, std::string{"{5e2ab18817264e75a04f1ed9a6a89c4c}"}, expected_uuid);
+    UUID_TEST(testUuid, std::string{}, std::string{"00000000-0000-0000-0000-000000000000"});
 
     /**
      * STRUCT TEST
diff --git a/test/cpp/src/TestServer.cpp b/test/cpp/src/TestServer.cpp
index 65317f8..6c61f40 100644
--- a/test/cpp/src/TestServer.cpp
+++ b/test/cpp/src/TestServer.cpp
@@ -132,6 +132,11 @@
     _return = thing;
   }
 
+  std::string testUuid(const std::string thing) override {
+    printf("testUuid(\"{%s}\")\n", thing.c_str());
+    return thing;
+  }
+
   void testStruct(Xtruct& out, const Xtruct& thing) override {
     printf("testStruct({\"%s\", %d, %d, %" PRId64 "})\n",
            thing.string_thing.c_str(),
@@ -442,6 +447,11 @@
     cob(res);
   }
 
+  void testUuid(::std::function<void(std::string const& _return)> cob, const std::string thing) override {
+    std::string res = _delegate->testUuid(thing);
+    cob(res);
+  }
+
   void testStruct(std::function<void(Xtruct const& _return)> cob, const Xtruct& thing) override {
     Xtruct res;
     _delegate->testStruct(res, thing);