Thrift: Merging external patch.

Summary:
Merging a patch from Andy Lutomirsky.
- Allow fields to be marked "required" or "optional" (only affects C++).
- Thrift structs now have operator ==.

Reviewed By: mcslee

Test Plan: test/OptionalRequiredTest.cpp

Revert Plan: ok


git-svn-id: https://svn.apache.org/repos/asf/incubator/thrift/trunk@665202 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/test/OptionalRequiredTest.cpp b/test/OptionalRequiredTest.cpp
new file mode 100644
index 0000000..73574ee
--- /dev/null
+++ b/test/OptionalRequiredTest.cpp
@@ -0,0 +1,231 @@
+/*
+../compiler/cpp/thrift -cpp OptionalRequiredTest.thrift
+g++ -Wall -I../lib/cpp/src -I/usr/local/include/boost-1_33_1 \
+  OptionalRequiredTest.cpp gen-cpp/OptionalRequiredTest_types.cpp \
+  ../lib/cpp/.libs/libthrift.a -o OptionalRequiredTest
+./OptionalRequiredTest
+*/
+
+#include <cassert>
+#include <map>
+#include <iostream>
+#include <protocol/TDebugProtocol.h>
+#include <protocol/TBinaryProtocol.h>
+#include <transport/TTransportUtils.h>
+#include "gen-cpp/OptionalRequiredTest_types.h"
+
+using std::cout;
+using std::endl;
+using std::map;
+using std::string;
+using namespace thrift::test;
+using namespace facebook::thrift;
+using namespace facebook::thrift::transport;
+using namespace facebook::thrift::protocol;
+
+
+/*
+template<typename Struct>
+void trywrite(const Struct& s, bool should_work) {
+  bool worked;
+  try {
+    TBinaryProtocol protocol(boost::shared_ptr<TTransport>(new TMemoryBuffer));
+    s.write(&protocol);
+    worked = true;
+  } catch (TProtocolException & ex) {
+    worked = false;
+  }
+  assert(worked == should_work);
+}
+*/
+
+template <typename Struct1, typename Struct2>
+void write_to_read(const Struct1 & w, Struct2 & r) {
+  TBinaryProtocol protocol(boost::shared_ptr<TTransport>(new TMemoryBuffer));
+  w.write(&protocol);
+  r.read(&protocol);
+}
+
+
+int main() {
+
+  cout << "This old school struct should have three fields." << endl;
+  {
+    OldSchool o;
+    cout << ThriftDebugString(o) << endl;
+  }
+  cout << endl;
+
+  cout << "Setting a value before setting isset." << endl;
+  {
+    Simple s;
+    cout << ThriftDebugString(s) << endl;
+    s.im_optional = 10;
+    cout << ThriftDebugString(s) << endl;
+    s.__isset.im_optional = true;
+    cout << ThriftDebugString(s) << endl;
+  }
+  cout << endl;
+
+  cout << "Setting isset before setting a value." << endl;
+  {
+    Simple s;
+    cout << ThriftDebugString(s) << endl;
+    s.__isset.im_optional = true;
+    cout << ThriftDebugString(s) << endl;
+    s.im_optional = 10;
+    cout << ThriftDebugString(s) << endl;
+  }
+  cout << endl;
+
+  // Write-to-read with optional fields.
+  {
+    Simple s1, s2, s3;
+    s1.im_optional = 10;
+    assert(!s1.__isset.im_default);
+  //assert(!s1.__isset.im_required);  // Compile error.
+    assert(!s1.__isset.im_optional);
+
+    write_to_read(s1, s2);
+
+    assert( s2.__isset.im_default);
+  //assert( s2.__isset.im_required);  // Compile error.
+    assert(!s2.__isset.im_optional);
+    assert(s3.im_optional == 0);
+
+    s1.__isset.im_optional = true;
+    write_to_read(s1, s3);
+
+    assert( s3.__isset.im_default);
+  //assert( s3.__isset.im_required);  // Compile error.
+    assert( s3.__isset.im_optional);
+    assert(s3.im_optional == 10);
+  }
+
+  // Writing between optional and default.
+  {
+    Tricky1 t1;
+    Tricky2 t2;
+
+    t2.im_optional = 10;
+    write_to_read(t2, t1);
+    write_to_read(t1, t2);
+    assert(!t1.__isset.im_default);
+    assert( t2.__isset.im_optional);
+    assert(t1.im_default == t2.im_optional);
+    assert(t1.im_default == 0);
+  }
+
+  // Writing between default and required.
+  {
+    Tricky1 t1;
+    Tricky3 t3;
+    write_to_read(t1, t3);
+    write_to_read(t3, t1);
+    assert(t1.__isset.im_default);
+  }
+
+  // Writing between optional and required.
+  {
+    Tricky2 t2;
+    Tricky3 t3;
+    t2.__isset.im_optional = true;
+    write_to_read(t2, t3);
+    write_to_read(t3, t2);
+  }
+
+  // Mu-hu-ha-ha-ha!
+  {
+    Tricky2 t2;
+    Tricky3 t3;
+    try {
+      write_to_read(t2, t3);
+      abort();
+    }
+    catch (TProtocolException& ex) {}
+
+    write_to_read(t3, t2);
+    assert(t2.__isset.im_optional);
+  }
+
+  cout << "Complex struct, simple test." << endl;
+  {
+    Complex c;
+    cout << ThriftDebugString(c) << endl;
+  }
+
+
+  {
+    Tricky1 t1;
+    Tricky2 t2;
+    // Compile error.
+    //(void)(t1 == t2);
+  }
+
+  {
+    OldSchool o1, o2, o3;
+    assert(o1 == o2);
+    o1.im_int = o2.im_int = 10;
+    assert(o1 == o2);
+    o1.__isset.im_int = true;
+    o2.__isset.im_int = false;
+    assert(o1 == o2);
+    o1.im_int = 20;
+    o1.__isset.im_int = false;
+    assert(o1 != o2);
+    o1.im_int = 10;
+    assert(o1 == o2);
+    o1.im_str = o2.im_str = "foo";
+    assert(o1 == o2);
+    o1.__isset.im_str = o2.__isset.im_str = true;
+    assert(o1 == o2);
+    map<int32_t,string> mymap;
+    mymap[1] = "bar";
+    mymap[2] = "baz";
+    o1.im_big.push_back(map<int32_t,string>());
+    assert(o1 != o2);
+    o2.im_big.push_back(map<int32_t,string>());
+    assert(o1 == o2);
+    o2.im_big.push_back(mymap);
+    assert(o1 != o2);
+    o1.im_big.push_back(mymap);
+    assert(o1 == o2);
+
+    TBinaryProtocol protocol(boost::shared_ptr<TTransport>(new TMemoryBuffer));
+    o1.write(&protocol);
+
+    o1.im_big.push_back(mymap);
+    mymap[3] = "qux";
+    o2.im_big.push_back(mymap);
+    assert(o1 != o2);
+    o1.im_big.back()[3] = "qux";
+    assert(o1 == o2);
+
+    o3.read(&protocol);
+    o3.im_big.push_back(mymap);
+    assert(o1 == o3);
+
+    //cout << ThriftDebugString(o3) << endl;
+  }
+
+  {
+    Tricky2 t1, t2;
+    assert(t1.__isset.im_optional == false);
+    assert(t2.__isset.im_optional == false);
+    assert(t1 == t2);
+    t1.im_optional = 5;
+    assert(t1 == t2);
+    t2.im_optional = 5;
+    assert(t1 == t2);
+    t1.__isset.im_optional = true;
+    assert(t1 != t2);
+    t2.__isset.im_optional = true;
+    assert(t1 == t2);
+    t1.im_optional = 10;
+    assert(t1 != t2);
+    t2.__isset.im_optional = false;
+    assert(t1 != t2);
+  }
+
+  return 0;
+}
diff --git a/test/OptionalRequiredTest.thrift b/test/OptionalRequiredTest.thrift
new file mode 100644
index 0000000..9738bd6
--- /dev/null
+++ b/test/OptionalRequiredTest.thrift
@@ -0,0 +1,42 @@
+/*
+../compiler/cpp/thrift -cpp OptionalRequiredTest.thrift
+g++ -Wall -I../lib/cpp/src -I/usr/local/include/boost-1_33_1 \
+  OptionalRequiredTest.cpp gen-cpp/OptionalRequiredTest_types.cpp \
+  ../lib/cpp/.libs/libthrift.a -o OptionalRequiredTest
+./OptionalRequiredTest
+*/
+
+cpp_namespace thrift.test
+
+struct OldSchool {
+  1: i16    im_int;
+  2: string im_str;
+  3: list<map<i32,string>> im_big;
+}
+
+struct Simple {
+  1: /* :) */ i16 im_default;
+  2: required i16 im_required;
+  3: optional i16 im_optional;
+}
+
+struct Tricky1 {
+  1: /* :) */ i16 im_default;
+}
+
+struct Tricky2 {
+  1: optional i16 im_optional;
+}
+
+struct Tricky3 {
+  1: required i16 im_required;
+}
+
+struct Complex {
+  1:          i16 cp_default;
+  2: required i16 cp_required;
+  3: optional i16 cp_optional;
+  4:          map<i16,Simple> the_map;
+  5: required Simple req_simp;
+  6: optional Simple opt_simp;
+}