@@ -66,6 +66,7 @@ class t_cpp_generator : public t_oop_generator {
6666 gen_templates_only_ = false ;
6767 gen_moveable_ = false ;
6868 gen_forward_setter_ = false ;
69+ gen_template_streamop_ = false ;
6970 gen_no_ostream_operators_ = false ;
7071 gen_no_skeleton_ = false ;
7172 gen_no_constructors_ = false ;
@@ -96,6 +97,8 @@ class t_cpp_generator : public t_oop_generator {
9697 }
9798 } else if ( iter->first .compare (" no_ostream_operators" ) == 0 ) {
9899 gen_no_ostream_operators_ = true ;
100+ } else if ( iter->first .compare (" template_streamop" ) == 0 ) {
101+ gen_template_streamop_ = true ;
99102 } else if ( iter->first .compare (" no_skeleton" ) == 0 ) {
100103 gen_no_skeleton_ = true ;
101104 } else if ( iter->first .compare (" no_constructors" ) == 0 ) {
@@ -130,6 +133,8 @@ class t_cpp_generator : public t_oop_generator {
130133 void generate_enum_ostream_operator (std::ostream& out, t_enum* tenum);
131134 void generate_enum_to_string_helper_function_decl (std::ostream& out, t_enum* tenum);
132135 void generate_enum_to_string_helper_function (std::ostream& out, t_enum* tenum);
136+ void generate_enum_printto_helper_function_decl (std::ostream& out, t_enum* tenum);
137+ void generate_enum_printto_helper_function (std::ostream& out, t_enum* tenum);
133138 void generate_forward_declaration (t_struct* tstruct) override ;
134139 void generate_struct (t_struct* tstruct) override { generate_cpp_struct (tstruct, false ); }
135140 void generate_xception (t_struct* txception) override { generate_cpp_struct (txception, true ); }
@@ -371,6 +376,11 @@ class t_cpp_generator : public t_oop_generator {
371376 */
372377 bool gen_forward_setter_;
373378
379+ /* *
380+ * True if we should generate operator<< and printTo with generic stream type template.
381+ */
382+ bool gen_template_streamop_;
383+
374384 /* *
375385 * True if we should generate ostream definitions
376386 */
@@ -462,7 +472,7 @@ void t_cpp_generator::init_generator() {
462472 string f_types_impl_name = get_out_dir () + program_name_ + " _types.cpp" ;
463473 f_types_impl_.open (f_types_impl_name.c_str ());
464474
465- if (gen_templates_ || gen_forward_setter_) {
475+ if (gen_templates_ || gen_forward_setter_ || gen_template_streamop_ ) {
466476 // If we don't open the stream, it appears to just discard data,
467477 // which is fine.
468478 string f_types_tcc_name = get_out_dir () + program_name_ + " _types.tcc" ;
@@ -529,6 +539,12 @@ void t_cpp_generator::init_generator() {
529539 f_types_impl_ << " #include <ostream>" << ' \n ' << ' \n ' ;
530540 f_types_impl_ << " #include <thrift/TToString.h>" << ' \n ' << ' \n ' ;
531541
542+ // For template_streamop, we need TPrintTo.h in the .tcc file for direct streaming
543+ // TPrintTo avoids the overhead of to_string which uses ostringstream internally
544+ if (gen_template_streamop_) {
545+ f_types_tcc_ << " #include <thrift/TPrintTo.h>" << ' \n ' << ' \n ' ;
546+ }
547+
532548 // Open namespace
533549 ns_open_ = namespace_open (program_->get_namespace (" cpp" ));
534550 ns_close_ = namespace_close (program_->get_namespace (" cpp" ));
@@ -552,7 +568,7 @@ void t_cpp_generator::close_generator() {
552568 // Include the types.tcc file from the types header file,
553569 // so clients don't have to explicitly include the tcc file.
554570 // TODO(simpkins): Make this a separate option.
555- if (gen_templates_ || gen_forward_setter_) {
571+ if (gen_templates_ || gen_forward_setter_ || gen_template_streamop_ ) {
556572 f_types_ << " #include \" " << get_include_prefix (*get_program ()) << program_name_
557573 << " _types.tcc\" " << ' \n ' << ' \n ' ;
558574 }
@@ -680,6 +696,12 @@ void t_cpp_generator::generate_enum(t_enum* tenum) {
680696 generate_enum_to_string_helper_function_decl (f_types_, tenum);
681697 generate_enum_to_string_helper_function (f_types_impl_, tenum);
682698
699+ // Generate template printTo specialization for enums when template_streamop is enabled
700+ if (gen_template_streamop_) {
701+ generate_enum_printto_helper_function_decl (f_types_, tenum);
702+ generate_enum_printto_helper_function (f_types_tcc_, tenum);
703+ }
704+
683705 has_members_ = true ;
684706}
685707
@@ -777,6 +799,52 @@ void t_cpp_generator::generate_enum_to_string_helper_function(std::ostream& out,
777799 }
778800}
779801
802+ void t_cpp_generator::generate_enum_printto_helper_function_decl (std::ostream& out, t_enum* tenum) {
803+ out << " template <typename OStream_>" << ' \n ' ;
804+ out << " void printTo(OStream_& out, const " ;
805+ if (gen_pure_enums_) {
806+ out << tenum->get_name ();
807+ } else {
808+ out << tenum->get_name () << " ::type&" ;
809+ }
810+ out << " val);" << ' \n ' ;
811+ out << ' \n ' ;
812+ }
813+
814+ void t_cpp_generator::generate_enum_printto_helper_function (std::ostream& out, t_enum* tenum) {
815+ if (!has_custom_ostream (tenum)) {
816+ out << " template <typename OStream_>" << ' \n ' ;
817+ out << " void printTo(OStream_& out, const " ;
818+ if (gen_pure_enums_) {
819+ out << tenum->get_name ();
820+ } else {
821+ out << tenum->get_name () << " ::type&" ;
822+ }
823+ out << " val) " ;
824+ scope_up (out);
825+
826+ out << indent () << " std::map<int, const char*>::const_iterator it = _"
827+ << tenum->get_name () << " _VALUES_TO_NAMES.find(" ;
828+ if (gen_enum_class_) {
829+ out << " static_cast<int>(val));" << ' \n ' ;
830+ } else {
831+ out << " val);" << ' \n ' ;
832+ }
833+ out << indent () << " if (it != _" << tenum->get_name () << " _VALUES_TO_NAMES.end()) {" << ' \n ' ;
834+ indent_up ();
835+ out << indent () << " out << it->second;" << ' \n ' ;
836+ indent_down ();
837+ out << indent () << " } else {" << ' \n ' ;
838+ indent_up ();
839+ out << indent () << " out << static_cast<int>(val);" << ' \n ' ;
840+ indent_down ();
841+ out << indent () << " }" << ' \n ' ;
842+
843+ scope_down (out);
844+ out << ' \n ' ;
845+ }
846+ }
847+
780848/* *
781849 * Generates a class that holds all the constants.
782850 */
@@ -1003,7 +1071,9 @@ void t_cpp_generator::generate_cpp_struct(t_struct* tstruct, bool is_exception)
10031071 }
10041072
10051073 if (!has_custom_ostream (tstruct)) {
1006- generate_struct_print_method (f_types_impl_, tstruct);
1074+ // When template_streamop is enabled, printTo implementation goes to .tcc file
1075+ std::ostream& print_method_out = (gen_template_streamop_ ? f_types_tcc_ : f_types_impl_);
1076+ generate_struct_print_method (print_method_out, tstruct);
10071077 }
10081078
10091079 if (is_exception) {
@@ -1481,7 +1551,8 @@ void t_cpp_generator::generate_struct_declaration(ostream& out,
14811551
14821552 if (is_user_struct && !has_custom_ostream (tstruct)) {
14831553 out << indent ();
1484- if (!gen_templates_) out << " virtual " ;
1554+ // Template methods cannot be virtual, so skip virtual keyword when using template_streamop
1555+ if (!gen_templates_ && !gen_template_streamop_) out << " virtual " ;
14851556 generate_struct_print_method_decl (out, nullptr );
14861557 out << " ;" << ' \n ' ;
14871558 }
@@ -1532,7 +1603,9 @@ void t_cpp_generator::generate_struct_declaration(ostream& out,
15321603 // When private_optional is enabled, optional members may be private.
15331604 // The generated namespace-scope operator<< needs friend access.
15341605 if (is_user_struct && gen_private_optional_) {
1535- indent (out) << " friend " ;
1606+ if (!gen_template_streamop_) {
1607+ indent (out) << " friend " ;
1608+ }
15361609 generate_struct_ostream_operator_decl (out, tstruct);
15371610 }
15381611
@@ -1610,7 +1683,9 @@ void t_cpp_generator::generate_struct_definition(ostream& out,
16101683 }
16111684 }
16121685 if (is_user_struct) {
1613- generate_struct_ostream_operator (out, tstruct);
1686+ // When template_streamop is enabled, operator<< implementation goes to .tcc file
1687+ std::ostream& ostream_op_out = (gen_template_streamop_ ? f_types_tcc_ : out);
1688+ generate_struct_ostream_operator (ostream_op_out, tstruct);
16141689 }
16151690 out << ' \n ' ;
16161691}
@@ -1979,18 +2054,35 @@ void t_cpp_generator::generate_struct_swap_decl(std::ostream& out, t_struct* tst
19792054}
19802055
19812056void t_cpp_generator::generate_struct_ostream_operator_decl (std::ostream& out, t_struct* tstruct) {
1982- out << " std::ostream& operator<<(std::ostream& out, const "
1983- << tstruct->get_name ()
1984- << " & obj);" << ' \n ' ;
2057+ if (gen_template_streamop_) {
2058+ out << " template <typename OStream_>" << ' \n ' ;
2059+ if (gen_private_optional_) {
2060+ out << indent () << " friend " ;
2061+ }
2062+ out << " OStream_& operator<<(OStream_& out, const "
2063+ << tstruct->get_name ()
2064+ << " & obj);" << ' \n ' ;
2065+ } else {
2066+ out << " std::ostream& operator<<(std::ostream& out, const "
2067+ << tstruct->get_name ()
2068+ << " & obj);" << ' \n ' ;
2069+ }
19852070 out << ' \n ' ;
19862071}
19872072
19882073void t_cpp_generator::generate_struct_ostream_operator (std::ostream& out, t_struct* tstruct) {
19892074 if (!has_custom_ostream (tstruct)) {
19902075 // thrift defines this behavior
1991- out << " std::ostream& operator<<(std::ostream& out, const "
1992- << tstruct->get_name ()
1993- << " & obj)" << ' \n ' ;
2076+ if (gen_template_streamop_) {
2077+ out << " template <typename OStream_>" << ' \n ' ;
2078+ out << " OStream_& operator<<(OStream_& out, const "
2079+ << tstruct->get_name ()
2080+ << " & obj)" << ' \n ' ;
2081+ } else {
2082+ out << " std::ostream& operator<<(std::ostream& out, const "
2083+ << tstruct->get_name ()
2084+ << " & obj)" << ' \n ' ;
2085+ }
19942086 scope_up (out);
19952087 out << indent () << " obj.printTo(out);" << ' \n '
19962088 << indent () << " return out;" << ' \n ' ;
@@ -2000,11 +2092,24 @@ void t_cpp_generator::generate_struct_ostream_operator(std::ostream& out, t_stru
20002092}
20012093
20022094void t_cpp_generator::generate_struct_print_method_decl (std::ostream& out, t_struct* tstruct) {
2003- out << " void " ;
2004- if (tstruct) {
2005- out << tstruct->get_name () << " ::" ;
2095+ if (gen_template_streamop_) {
2096+ // For template version, the method itself is templated
2097+ if (!tstruct) {
2098+ // Declaration inside class - no "template" keyword here, will be added by caller if needed
2099+ out << " template <typename OStream_>" << ' \n ' << indent () << " void " ;
2100+ } else {
2101+ // External implementation - needs template keyword
2102+ out << " template <typename OStream_>" << ' \n ' << indent () << " void " ;
2103+ out << tstruct->get_name () << " ::" ;
2104+ }
2105+ out << " printTo(OStream_& out) const" ;
2106+ } else {
2107+ out << " void " ;
2108+ if (tstruct) {
2109+ out << tstruct->get_name () << " ::" ;
2110+ }
2111+ out << " printTo(std::ostream& out) const" ;
20062112 }
2007- out << " printTo(std::ostream& out) const" ;
20082113}
20092114
20102115void t_cpp_generator::generate_exception_what_method_decl (std::ostream& out,
@@ -2020,35 +2125,49 @@ void t_cpp_generator::generate_exception_what_method_decl(std::ostream& out,
20202125}
20212126
20222127namespace struct_ostream_operator_generator {
2023- void generate_required_field_value (std::ostream& out, const t_field* field) {
2128+ void generate_required_field_value (std::ostream& out, const t_field* field, bool use_printto) {
2129+ if (use_printto) {
2130+ // For template_streamop, use printTo for direct streaming without temporary strings
2131+ // Use comma operator: out << "x=", printTo(out, x)
2132+ out << " , printTo(out, " << field->get_name () << " )" ;
2133+ return ;
2134+ }
2135+ // For std::ostream, use to_string (backward compatible)
20242136 out << " << to_string(" << field->get_name () << " )" ;
20252137}
20262138
2027- void generate_optional_field_value (std::ostream& out, const t_field* field) {
2028- out << " ; (__isset." << field->get_name () << " ? (out" ;
2029- generate_required_field_value (out, field);
2030- out << " ) : (out << \" <null>\" ))" ;
2139+ void generate_optional_field_value (std::ostream& out, const t_field* field, bool use_printto) {
2140+ out << " ; (__isset." << field->get_name () << " ? " ;
2141+ if (use_printto) {
2142+ // For printTo, call directly without wrapping in (out ...)
2143+ out << " printTo(out, " << field->get_name () << " )" ;
2144+ } else {
2145+ // For to_string, need to wrap with (out << ...)
2146+ out << " (out << to_string(" << field->get_name () << " ))" ;
2147+ }
2148+ out << " : (out << \" <null>\" ))" ;
20312149}
20322150
2033- void generate_field_value (std::ostream& out, const t_field* field) {
2151+ void generate_field_value (std::ostream& out, const t_field* field, bool use_printto ) {
20342152 if (field->get_req () == t_field::T_OPTIONAL)
2035- generate_optional_field_value (out, field);
2153+ generate_optional_field_value (out, field, use_printto );
20362154 else
2037- generate_required_field_value (out, field);
2155+ generate_required_field_value (out, field, use_printto );
20382156}
20392157
20402158void generate_field_name (std::ostream& out, const t_field* field) {
20412159 out << " \" " << field->get_name () << " =\" " ;
20422160}
20432161
2044- void generate_field (std::ostream& out, const t_field* field) {
2162+ void generate_field (std::ostream& out, const t_field* field, bool use_printto ) {
20452163 generate_field_name (out, field);
2046- generate_field_value (out, field);
2164+ generate_field_value (out, field, use_printto );
20472165}
20482166
20492167void generate_fields (std::ostream& out,
20502168 const vector<t_field*>& fields,
2051- const std::string& indent) {
2169+ const std::string& indent,
2170+ bool use_printto) {
20522171 const vector<t_field*>::const_iterator beg = fields.begin ();
20532172 const vector<t_field*>::const_iterator end = fields.end ();
20542173
@@ -2059,7 +2178,7 @@ void generate_fields(std::ostream& out,
20592178 out << " \" , \" << " ;
20602179 }
20612180
2062- generate_field (out, *it);
2181+ generate_field (out, *it, use_printto );
20632182 out << " ;" << ' \n ' ;
20642183 }
20652184}
@@ -2075,9 +2194,16 @@ void t_cpp_generator::generate_struct_print_method(std::ostream& out, t_struct*
20752194
20762195 indent_up ();
20772196
2197+ bool use_printto = gen_template_streamop_;
2198+ if (use_printto) {
2199+ // For template_streamop, use printTo for direct streaming (better performance)
2200+ out << indent () << " using ::apache::thrift::printTo;" << ' \n ' ;
2201+ }
2202+ // Always include to_string as well for compatibility
20782203 out << indent () << " using ::apache::thrift::to_string;" << ' \n ' ;
2204+
20792205 out << indent () << " out << \" " << tstruct->get_name () << " (\" ;" << ' \n ' ;
2080- struct_ostream_operator_generator::generate_fields (out, tstruct->get_members (), indent ());
2206+ struct_ostream_operator_generator::generate_fields (out, tstruct->get_members (), indent (), use_printto );
20812207 out << indent () << " out << \" )\" ;" << ' \n ' ;
20822208
20832209 indent_down ();
0 commit comments