You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@thrift.apache.org by ro...@apache.org on 2014/09/04 00:03:37 UTC

git commit: THRIFT-2067 C++: all generated objects provide ostream operator<<

Repository: thrift
Updated Branches:
  refs/heads/master d0bd17e72 -> b3f6ea100


THRIFT-2067 C++: all generated objects provide ostream operator<<


Project: http://git-wip-us.apache.org/repos/asf/thrift/repo
Commit: http://git-wip-us.apache.org/repos/asf/thrift/commit/b3f6ea10
Tree: http://git-wip-us.apache.org/repos/asf/thrift/tree/b3f6ea10
Diff: http://git-wip-us.apache.org/repos/asf/thrift/diff/b3f6ea10

Branch: refs/heads/master
Commit: b3f6ea100fae38a568e1844923c4c945cab5b536
Parents: d0bd17e
Author: Konrad Grochowski <hc...@minions.org.pl>
Authored: Tue Sep 2 16:00:47 2014 +0200
Committer: Roger Meier <ro...@apache.org>
Committed: Wed Sep 3 23:41:32 2014 +0200

----------------------------------------------------------------------
 compiler/cpp/src/generate/t_cpp_generator.cc | 104 +++++++++++++++-
 lib/cpp/Makefile.am                          |   3 +-
 lib/cpp/src/thrift/TToString.h               |  89 ++++++++++++++
 lib/cpp/test/Makefile.am                     |   3 +-
 lib/cpp/test/ToStringTest.cpp                | 137 ++++++++++++++++++++++
 5 files changed, 333 insertions(+), 3 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/thrift/blob/b3f6ea10/compiler/cpp/src/generate/t_cpp_generator.cc
----------------------------------------------------------------------
diff --git a/compiler/cpp/src/generate/t_cpp_generator.cc b/compiler/cpp/src/generate/t_cpp_generator.cc
index 3a190f5..85eeeef 100755
--- a/compiler/cpp/src/generate/t_cpp_generator.cc
+++ b/compiler/cpp/src/generate/t_cpp_generator.cc
@@ -127,6 +127,7 @@ class t_cpp_generator : public t_oop_generator {
   void generate_struct_writer        (std::ofstream& out, t_struct* tstruct, bool pointers=false);
   void generate_struct_result_writer (std::ofstream& out, t_struct* tstruct, bool pointers=false);
   void generate_struct_swap          (std::ofstream& out, t_struct* tstruct);
+  void generate_struct_ostream_operator(std::ofstream& out, t_struct* tstruct);
 
   /**
    * Service-level generation functions
@@ -230,6 +231,8 @@ class t_cpp_generator : public t_oop_generator {
                                    const char* suffix,
                                    bool include_values);
 
+  void generate_struct_ostream_operator_decl(std::ofstream& f, t_struct* tstruct);
+
   // These handles checking gen_dense_ and checking for duplicates.
   void generate_local_reflection(std::ofstream& out, t_type* ttype, bool is_definition);
   void generate_local_reflection_pointer(std::ofstream& out, t_type* ttype);
@@ -376,6 +379,7 @@ void t_cpp_generator::init_generator() {
 
   // Include base types
   f_types_ <<
+    "#include <iosfwd>" << endl << endl <<
     "#include <thrift/Thrift.h>" << endl <<
     "#include <thrift/TApplicationException.h>" << endl <<
     "#include <thrift/protocol/TProtocol.h>" << endl <<
@@ -432,7 +436,10 @@ void t_cpp_generator::init_generator() {
   }
 
   // The swap() code needs <algorithm> for std::swap()
-  f_types_impl_ << "#include <algorithm>" << endl << endl;
+  f_types_impl_ << "#include <algorithm>" << endl;
+  // for operator<<
+  f_types_impl_ << "#include <ostream>" << endl << endl;
+  f_types_impl_ << "#include <thrift/TToString.h>" << endl << endl;
 
   // Open namespace
   ns_open_ = namespace_open(program_->get_namespace("cpp"));
@@ -817,6 +824,7 @@ void t_cpp_generator::generate_cpp_struct(t_struct* tstruct, bool is_exception)
   generate_struct_swap(f_types_impl_, tstruct);
   generate_copy_constructor(f_types_impl_, tstruct);
   generate_assignment_operator(f_types_impl_, tstruct);
+  generate_struct_ostream_operator(f_types_impl_, tstruct);
 }
 
 void t_cpp_generator::generate_copy_constructor(
@@ -1123,6 +1131,11 @@ void t_cpp_generator::generate_struct_declaration(ofstream& out,
   }
   out << endl;
 
+  // ostream operator<<
+  out << indent() << "friend ";
+  generate_struct_ostream_operator_decl(out, tstruct);
+  out << ";" << endl;
+
   indent_down();
   indent(out) <<
     "};" << endl <<
@@ -1724,6 +1737,95 @@ void t_cpp_generator::generate_struct_swap(ofstream& out, t_struct* tstruct) {
   out << endl;
 }
 
+void t_cpp_generator::generate_struct_ostream_operator_decl(std::ofstream& out,
+                                                            t_struct* tstruct) {
+  out << "std::ostream& operator<<(std::ostream& out, const "
+      << tstruct->get_name() << "& obj)";
+}
+
+namespace struct_ostream_operator_generator
+{
+void generate_required_field_value(std::ofstream& out, const t_field* field)
+{
+  out << " << to_string(obj." << field->get_name() << ")";
+}
+
+void generate_optional_field_value(std::ofstream& out, const t_field* field)
+{
+  out << "; (obj.__isset." << field->get_name() << " ? (out";
+  generate_required_field_value(out, field);
+  out << ") : (out << \"<null>\"))";
+}
+
+void generate_field_value(std::ofstream& out, const t_field* field)
+{
+  if (field->get_req() == t_field::T_OPTIONAL)
+    generate_optional_field_value(out, field);
+  else
+    generate_required_field_value(out, field);
+}
+
+void generate_field_name(std::ofstream& out, const t_field* field)
+{
+  out << "\"" << field->get_name() << "=\"";
+}
+
+void generate_field(std::ofstream& out, const t_field* field)
+{
+  generate_field_name(out, field);
+  generate_field_value(out, field);
+}
+
+void generate_fields(std::ofstream& out,
+                     const vector<t_field*>& fields,
+                     const std::string& indent)
+{
+  const vector<t_field*>::const_iterator beg = fields.begin();
+  const vector<t_field*>::const_iterator end = fields.end();
+
+  for (vector<t_field*>::const_iterator it = beg; it != end; ++it) {
+    out << indent << "out << ";
+
+    if (it != beg) {
+      out << "\", \" << ";
+    }
+
+    generate_field(out, *it);
+    out << ";" << endl;
+  }
+}
+
+
+}
+
+/**
+ * Generates operator<<
+ */
+void t_cpp_generator::generate_struct_ostream_operator(std::ofstream& out,
+                                                       t_struct* tstruct) {
+  out << indent();
+  generate_struct_ostream_operator_decl(out, tstruct);
+  out << " {" << endl;
+
+  indent_up();
+
+  out <<
+    indent() << "using apache::thrift::to_string;" << endl <<
+    indent() << "out << \"" << tstruct->get_name() << "(\";" << endl;
+
+  struct_ostream_operator_generator::generate_fields(out,
+                                                     tstruct->get_members(),
+                                                     indent());
+
+  out <<
+    indent() << "out << \")\";" << endl <<
+    indent() << "return out;" << endl;
+
+  indent_down();
+  out << "}" << endl << endl;
+}
+
+
 /**
  * Generates a thrift service. In C++, this comprises an entirely separate
  * header and source file. The header file defines the methods and includes

http://git-wip-us.apache.org/repos/asf/thrift/blob/b3f6ea10/lib/cpp/Makefile.am
----------------------------------------------------------------------
diff --git a/lib/cpp/Makefile.am b/lib/cpp/Makefile.am
index ab9108a..4bd40fb 100755
--- a/lib/cpp/Makefile.am
+++ b/lib/cpp/Makefile.am
@@ -131,7 +131,8 @@ include_thrift_HEADERS = \
                          src/thrift/TProcessor.h \
                          src/thrift/TApplicationException.h \
                          src/thrift/TLogging.h \
-                         src/thrift/cxxfunctional.h
+                         src/thrift/cxxfunctional.h \
+                         src/thrift/TToString.h
 
 include_concurrencydir = $(include_thriftdir)/concurrency
 include_concurrency_HEADERS = \

http://git-wip-us.apache.org/repos/asf/thrift/blob/b3f6ea10/lib/cpp/src/thrift/TToString.h
----------------------------------------------------------------------
diff --git a/lib/cpp/src/thrift/TToString.h b/lib/cpp/src/thrift/TToString.h
new file mode 100644
index 0000000..c160e09
--- /dev/null
+++ b/lib/cpp/src/thrift/TToString.h
@@ -0,0 +1,89 @@
+/*
+ * 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_TOSTRING_H_
+#define _THRIFT_TOSTRING_H_ 1
+
+#include <boost/lexical_cast.hpp>
+
+#include <vector>
+#include <map>
+#include <set>
+#include <string>
+#include <sstream>
+
+namespace apache { namespace thrift {
+
+template <typename T>
+std::string to_string(const T& t) {
+  return boost::lexical_cast<std::string>(t);
+}
+
+template <typename K, typename V>
+std::string to_string(const std::map<K, V>& m);
+
+template <typename T>
+std::string to_string(const std::set<T>& s);
+
+template <typename T>
+std::string to_string(const std::vector<T>& t);
+
+template <typename K, typename V>
+std::string to_string(const typename std::pair<K, V>& v) {
+  std::ostringstream o;
+  o << to_string(v.first) << ": " << to_string(v.second);
+  return o.str();
+}
+
+template <typename T>
+std::string to_string(const T& beg, const T& end)
+{
+  std::ostringstream o;
+  for (T it = beg; it != end; ++it) {
+    if (it != beg)
+      o << ", ";
+    o << to_string(*it);
+  }
+  return o.str();
+}
+
+template <typename T>
+std::string to_string(const std::vector<T>& t) {
+  std::ostringstream o;
+  o << "[" << to_string(t.begin(), t.end()) << "]";
+  return o.str();
+}
+
+template <typename K, typename V>
+std::string to_string(const std::map<K, V>& m) {
+  std::ostringstream o;
+  o << "{" << to_string(m.begin(), m.end()) << "}";
+  return o.str();
+}
+
+template <typename T>
+std::string to_string(const std::set<T>& s) {
+  std::ostringstream o;
+  o << "{" << to_string(s.begin(), s.end()) << "}";
+  return o.str();
+}
+
+}} // apache::thrift
+
+#endif // _THRIFT_TOSTRING_H_

http://git-wip-us.apache.org/repos/asf/thrift/blob/b3f6ea10/lib/cpp/test/Makefile.am
----------------------------------------------------------------------
diff --git a/lib/cpp/test/Makefile.am b/lib/cpp/test/Makefile.am
index 6779ac6..c1fad3e 100755
--- a/lib/cpp/test/Makefile.am
+++ b/lib/cpp/test/Makefile.am
@@ -82,7 +82,8 @@ UnitTests_SOURCES = \
 	UnitTestMain.cpp \
 	TMemoryBufferTest.cpp \
 	TBufferBaseTest.cpp \
-	Base64Test.cpp
+	Base64Test.cpp \
+	ToStringTest.cpp
 
 if !WITH_BOOSTTHREADS
 UnitTests_SOURCES += \

http://git-wip-us.apache.org/repos/asf/thrift/blob/b3f6ea10/lib/cpp/test/ToStringTest.cpp
----------------------------------------------------------------------
diff --git a/lib/cpp/test/ToStringTest.cpp b/lib/cpp/test/ToStringTest.cpp
new file mode 100644
index 0000000..1a89c11
--- /dev/null
+++ b/lib/cpp/test/ToStringTest.cpp
@@ -0,0 +1,137 @@
+/*
+ * 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 <vector>
+#include <map>
+
+#include <boost/test/auto_unit_test.hpp>
+
+#include <thrift/TToString.h>
+
+#include "gen-cpp/ThriftTest_types.h"
+#include "gen-cpp/OptionalRequiredTest_types.h"
+#include "gen-cpp/DebugProtoTest_types.h"
+
+using apache::thrift::to_string;
+
+BOOST_AUTO_TEST_SUITE( ToStringTest )
+
+BOOST_AUTO_TEST_CASE( base_types_to_string ) {
+  BOOST_CHECK_EQUAL(to_string(10), "10");
+  BOOST_CHECK_EQUAL(to_string(true), "1");
+  BOOST_CHECK_EQUAL(to_string('a'), "a");
+  BOOST_CHECK_EQUAL(to_string(1.2), "1.2");
+  BOOST_CHECK_EQUAL(to_string("abc"), "abc");
+}
+
+BOOST_AUTO_TEST_CASE( empty_vector_to_string ) {
+  std::vector<int> l;
+  BOOST_CHECK_EQUAL(to_string(l), "[]");
+}
+
+BOOST_AUTO_TEST_CASE( single_item_vector_to_string ) {
+  std::vector<int> l;
+  l.push_back(100);
+  BOOST_CHECK_EQUAL(to_string(l), "[100]");
+}
+
+BOOST_AUTO_TEST_CASE( multiple_item_vector_to_string ) {
+  std::vector<int> l;
+  l.push_back(100);
+  l.push_back(150);
+  BOOST_CHECK_EQUAL(to_string(l), "[100, 150]");
+}
+
+BOOST_AUTO_TEST_CASE( empty_map_to_string ) {
+  std::map<int, std::string> m;
+  BOOST_CHECK_EQUAL(to_string(m), "{}");
+}
+
+BOOST_AUTO_TEST_CASE( single_item_map_to_string ) {
+  std::map<int, std::string> m;
+  m[12] = "abc";
+  BOOST_CHECK_EQUAL(to_string(m), "{12: abc}");
+}
+
+BOOST_AUTO_TEST_CASE( multi_item_map_to_string ) {
+  std::map<int, std::string> m;
+  m[12] = "abc";
+  m[31] = "xyz";
+  BOOST_CHECK_EQUAL(to_string(m), "{12: abc, 31: xyz}");
+}
+
+BOOST_AUTO_TEST_CASE( empty_set_to_string ) {
+  std::set<char> s;
+  BOOST_CHECK_EQUAL(to_string(s), "{}");
+}
+
+BOOST_AUTO_TEST_CASE( single_item_set_to_string ) {
+  std::set<char> s;
+  s.insert('c');
+  BOOST_CHECK_EQUAL(to_string(s), "{c}");
+}
+
+BOOST_AUTO_TEST_CASE( multi_item_set_to_string ) {
+  std::set<char> s;
+  s.insert('a');
+  s.insert('z');
+  BOOST_CHECK_EQUAL(to_string(s), "{a, z}");
+}
+
+BOOST_AUTO_TEST_CASE( generated_empty_object_to_string ) {
+  thrift::test::EmptyStruct e;
+  BOOST_CHECK_EQUAL(to_string(e), "EmptyStruct()");
+}
+
+BOOST_AUTO_TEST_CASE( generated_single_basic_field_object_to_string ) {
+  thrift::test::StructA a;
+  a.__set_s("abcd");
+  BOOST_CHECK_EQUAL(to_string(a), "StructA(s=abcd)");
+}
+
+BOOST_AUTO_TEST_CASE( generated_two_basic_fields_object_to_string ) {
+  thrift::test::Bonk a;
+  a.__set_message("abcd");
+  a.__set_type(1234);
+  BOOST_CHECK_EQUAL(to_string(a), "Bonk(message=abcd, type=1234)");
+}
+
+BOOST_AUTO_TEST_CASE( generated_optional_fields_object_to_string ) {
+  thrift::test::Tricky2 a;
+  BOOST_CHECK_EQUAL(to_string(a), "Tricky2(im_optional=<null>)");
+  a.__set_im_optional(123);
+  BOOST_CHECK_EQUAL(to_string(a), "Tricky2(im_optional=123)");
+}
+
+BOOST_AUTO_TEST_CASE( generated_nested_object_to_string ) {
+  thrift::test::OneField a;
+  BOOST_CHECK_EQUAL(to_string(a), "OneField(field=EmptyStruct())");
+}
+
+BOOST_AUTO_TEST_CASE( generated_nested_list_object_to_string ) {
+  thrift::test::ListBonks l;
+  l.bonk.assign(2, thrift::test::Bonk());
+  l.bonk[0].__set_message("a");
+  l.bonk[1].__set_message("b");
+
+  BOOST_CHECK_EQUAL(to_string(l),
+                    "ListBonks(bonk=[Bonk(message=a, type=0), Bonk(message=b, type=0)])");
+}
+
+BOOST_AUTO_TEST_SUITE_END()