Minor Erlang generator improvements

Changes:
* Comment unions with 'union' in Erlang
* Add string type definition strategy
* Add support for new sets implementation
* Respect new string and set strategy in constant and default values
diff --git a/compiler/cpp/src/thrift/generate/t_erl_generator.cc b/compiler/cpp/src/thrift/generate/t_erl_generator.cc
index f28c7fd..7dbe9f1 100644
--- a/compiler/cpp/src/thrift/generate/t_erl_generator.cc
+++ b/compiler/cpp/src/thrift/generate/t_erl_generator.cc
@@ -40,6 +40,16 @@
 using std::vector;
 
 /**
+ * Helper enum for string mapping
+ */
+enum class StringTo {String, Binary, Both};
+
+/**
+ * Helper enum for sets mapping
+ */
+enum class SetsTo {V1, V2};
+
+/**
  * Erlang code generator.
  *
  */
@@ -58,6 +68,8 @@
     maps_ = false;
     export_lines_first_ = true;
     export_types_lines_first_ = true;
+    string_to_ = StringTo::Both;
+    sets_to_ = SetsTo::V1;
 
     for( iter = parsed_options.begin(); iter != parsed_options.end(); ++iter) {
       if( iter->first.compare("legacynames") == 0) {
@@ -68,6 +80,26 @@
         delimiter_ = iter->second;
       } else if( iter->first.compare("app_prefix") == 0) {
         app_prefix_ = iter->second;
+      } else if( iter->first.compare("string") == 0) {
+        auto string_to_value = iter->second;
+        if( string_to_value.compare("string") == 0) {
+          string_to_ = StringTo::String;
+        } else if( string_to_value.compare("binary") == 0) {
+          string_to_ = StringTo::Binary;
+        } else if( string_to_value.compare("both") == 0) {
+          string_to_ = StringTo::Both;
+        } else {
+          throw "unknown string option value:" + string_to_value;
+        }
+      } else if( iter->first.compare("set") == 0) {
+        auto set_to_value = iter->second;
+        if( set_to_value.compare("v1") == 0) {
+          sets_to_ = SetsTo::V1;
+        } else if( set_to_value.compare("v2") == 0) {
+          sets_to_ = SetsTo::V2;
+        } else {
+          throw "unknown set option value:" + set_to_value;
+        }
       } else {
         throw "unknown option erl:" + iter->first;
       }
@@ -100,11 +132,13 @@
   std::string render_member_type(t_field* field);
   std::string render_member_value(t_field* field);
   std::string render_member_requiredness(t_field* field);
+  std::string render_string_type();
 
   //  std::string render_default_value(t_type* type);
   std::string render_default_value(t_field* field);
   std::string render_const_value(t_type* type, t_const_value* value);
   std::string render_type_term(t_type* ttype, bool expand_structs, bool extended_info = false);
+  std::string render_default_sets_value();
 
   /**
    * Struct generation code
@@ -140,6 +174,7 @@
   std::string render_includes();
   std::string type_name(t_type* ttype);
   std::string render_const_list_values(t_type* type, t_const_value* value);
+  std::string render_const_string_value(t_const_value* value);
 
   std::string function_signature(t_function* tfunction, std::string prefix = "");
 
@@ -188,6 +223,12 @@
   /* used to avoid module name clashes for different applications */
   std::string app_prefix_;
 
+  /* string type definition strategy */
+  StringTo string_to_;
+
+  /* sets type definition strategy */
+  SetsTo sets_to_;
+
   /**
    * add function to export list
    */
@@ -593,7 +634,7 @@
     t_base_type::t_base tbase = ((t_base_type*)type)->get_base();
     switch (tbase) {
     case t_base_type::TYPE_STRING:
-      out << '"' << get_escaped_string(value) << '"';
+      out << render_const_string_value(value);
       break;
     case t_base_type::TYPE_BOOL:
       out << (value->get_integer() > 0 ? "true" : "false");
@@ -676,7 +717,11 @@
         out << ",";
       }
     }
-    out << "])";
+    out << "]";
+    if (sets_to_ == SetsTo::V2) {
+      out << ", [{version, 2}]";
+    }
+    out << ")";
   } else if (type->is_list()) {
     out << "[" << render_const_list_values(type, value) << "]";
   } else {
@@ -703,6 +748,13 @@
   return out.str();
 }
 
+string t_erl_generator::render_const_string_value(t_const_value* constval) {
+  if (string_to_ == StringTo::Binary) {
+    return "<<\"" + get_escaped_string(constval) + "\">>";
+  }
+  return '"' + get_escaped_string(constval) + '"';
+}
+
 
 string t_erl_generator::render_default_value(t_field* field) {
   t_type* type = field->get_type();
@@ -715,7 +767,7 @@
       return "dict:new()";
     }
   } else if (type->is_set()) {
-    return "sets:new()";
+    return render_default_sets_value();
   } else if (type->is_list()) {
     return "[]";
   } else {
@@ -723,13 +775,24 @@
   }
 }
 
+string t_erl_generator::render_default_sets_value() {
+  switch (sets_to_) {
+  case SetsTo::V1:
+    return "sets:new()";
+  case SetsTo::V2:
+    return "sets:new([{version,2}])";
+  default:
+    throw "compiler error: unsupported set type";
+  }
+}
+
 string t_erl_generator::render_member_type(t_field* field) {
   t_type* type = get_true_type(field->get_type());
   if (type->is_base_type()) {
     t_base_type::t_base tbase = ((t_base_type*)type)->get_base();
     switch (tbase) {
     case t_base_type::TYPE_STRING:
-      return "string() | binary()";
+      return render_string_type();
     case t_base_type::TYPE_BOOL:
       return "boolean()";
     case t_base_type::TYPE_I8:
@@ -761,6 +824,19 @@
   }
 }
 
+string t_erl_generator::render_string_type() {
+  switch (string_to_) {
+  case StringTo::String:
+    return "string()";
+  case StringTo::Binary:
+    return "binary()";
+  case StringTo::Both:
+    return "string() | binary()";
+  default:
+    throw "compiler error: unsupported string type";
+  }
+}
+
 string t_erl_generator::render_member_requiredness(t_field* field) {
   switch (field->get_req()) {
   case t_field::T_REQUIRED:
@@ -807,7 +883,13 @@
  * @param tstruct The struct definition
  */
 void t_erl_generator::generate_erl_struct_definition(ostream& out, t_struct* tstruct) {
-  indent(out) << "%% struct " << type_name(tstruct) << '\n' << '\n';
+  indent(out) << "%% ";
+  if (tstruct->is_union()) {
+    out << "union ";
+  } else {
+    out << "struct ";
+  }
+  out << type_name(tstruct) << '\n' << '\n';
 
   std::stringstream buf;
   buf << indent() << "-record(" << type_name(tstruct) << ", {";
@@ -1290,4 +1372,6 @@
     "    legacynames:     Output files retain naming conventions of Thrift 0.9.1 and earlier.\n"
     "    delimiter=       Delimiter between namespace prefix and record name. Default is '.'.\n"
     "    app_prefix=      Application prefix for generated Erlang files.\n"
-    "    maps:            Generate maps instead of dicts.\n")
+    "    maps:            Generate maps instead of dicts.\n"
+    "    string=          Define string as 'string', 'binary' or 'both'. Default is 'both'.\n"
+    "    set=             Define sets implementation, supported 'v1' and 'v2'. Default is 'v1'.\n")