Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
184 changes: 155 additions & 29 deletions compiler/cpp/src/thrift/generate/t_cpp_generator.cc
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@ class t_cpp_generator : public t_oop_generator {
gen_templates_only_ = false;
gen_moveable_ = false;
gen_forward_setter_ = false;
gen_template_streamop_ = false;
gen_no_ostream_operators_ = false;
gen_no_skeleton_ = false;
gen_no_constructors_ = false;
Expand Down Expand Up @@ -96,6 +97,8 @@ class t_cpp_generator : public t_oop_generator {
}
} else if ( iter->first.compare("no_ostream_operators") == 0) {
gen_no_ostream_operators_ = true;
} else if ( iter->first.compare("template_streamop") == 0) {
gen_template_streamop_ = true;
} else if ( iter->first.compare("no_skeleton") == 0) {
gen_no_skeleton_ = true;
} else if ( iter->first.compare("no_constructors") == 0) {
Expand Down Expand Up @@ -130,6 +133,8 @@ class t_cpp_generator : public t_oop_generator {
void generate_enum_ostream_operator(std::ostream& out, t_enum* tenum);
void generate_enum_to_string_helper_function_decl(std::ostream& out, t_enum* tenum);
void generate_enum_to_string_helper_function(std::ostream& out, t_enum* tenum);
void generate_enum_printto_helper_function_decl(std::ostream& out, t_enum* tenum);
void generate_enum_printto_helper_function(std::ostream& out, t_enum* tenum);
void generate_forward_declaration(t_struct* tstruct) override;
void generate_struct(t_struct* tstruct) override { generate_cpp_struct(tstruct, false); }
void generate_xception(t_struct* txception) override { generate_cpp_struct(txception, true); }
Expand Down Expand Up @@ -371,6 +376,11 @@ class t_cpp_generator : public t_oop_generator {
*/
bool gen_forward_setter_;

/**
* True if we should generate operator<< and printTo with generic stream type template.
*/
bool gen_template_streamop_;

/**
* True if we should generate ostream definitions
*/
Expand Down Expand Up @@ -462,7 +472,7 @@ void t_cpp_generator::init_generator() {
string f_types_impl_name = get_out_dir() + program_name_ + "_types.cpp";
f_types_impl_.open(f_types_impl_name.c_str());

if (gen_templates_ || gen_forward_setter_) {
if (gen_templates_ || gen_forward_setter_ || gen_template_streamop_) {
// If we don't open the stream, it appears to just discard data,
// which is fine.
string f_types_tcc_name = get_out_dir() + program_name_ + "_types.tcc";
Expand Down Expand Up @@ -529,6 +539,12 @@ void t_cpp_generator::init_generator() {
f_types_impl_ << "#include <ostream>" << '\n' << '\n';
f_types_impl_ << "#include <thrift/TToString.h>" << '\n' << '\n';

// For template_streamop, we need TPrintTo.h in the .tcc file for direct streaming
// TPrintTo avoids the overhead of to_string which uses ostringstream internally
if (gen_template_streamop_) {
f_types_tcc_ << "#include <thrift/TPrintTo.h>" << '\n' << '\n';
}

// Open namespace
ns_open_ = namespace_open(program_->get_namespace("cpp"));
ns_close_ = namespace_close(program_->get_namespace("cpp"));
Expand All @@ -552,7 +568,7 @@ void t_cpp_generator::close_generator() {
// Include the types.tcc file from the types header file,
// so clients don't have to explicitly include the tcc file.
// TODO(simpkins): Make this a separate option.
if (gen_templates_ || gen_forward_setter_) {
if (gen_templates_ || gen_forward_setter_ || gen_template_streamop_) {
f_types_ << "#include \"" << get_include_prefix(*get_program()) << program_name_
<< "_types.tcc\"" << '\n' << '\n';
}
Expand Down Expand Up @@ -680,6 +696,12 @@ void t_cpp_generator::generate_enum(t_enum* tenum) {
generate_enum_to_string_helper_function_decl(f_types_, tenum);
generate_enum_to_string_helper_function(f_types_impl_, tenum);

// Generate template printTo specialization for enums when template_streamop is enabled
if (gen_template_streamop_) {
generate_enum_printto_helper_function_decl(f_types_, tenum);
generate_enum_printto_helper_function(f_types_tcc_, tenum);
}

has_members_ = true;
}

Expand Down Expand Up @@ -777,6 +799,52 @@ void t_cpp_generator::generate_enum_to_string_helper_function(std::ostream& out,
}
}

void t_cpp_generator::generate_enum_printto_helper_function_decl(std::ostream& out, t_enum* tenum) {
out << "template <typename OStream_>" << '\n';
out << "void printTo(OStream_& out, const ";
if (gen_pure_enums_) {
out << tenum->get_name();
} else {
out << tenum->get_name() << "::type&";
}
out << " val);" << '\n';
out << '\n';
}

void t_cpp_generator::generate_enum_printto_helper_function(std::ostream& out, t_enum* tenum) {
if (!has_custom_ostream(tenum)) {
out << "template <typename OStream_>" << '\n';
out << "void printTo(OStream_& out, const ";
if (gen_pure_enums_) {
out << tenum->get_name();
} else {
out << tenum->get_name() << "::type&";
}
out << " val) ";
scope_up(out);

out << indent() << "std::map<int, const char*>::const_iterator it = _"
<< tenum->get_name() << "_VALUES_TO_NAMES.find(";
if (gen_enum_class_) {
out << "static_cast<int>(val));" << '\n';
} else {
out << "val);" << '\n';
}
out << indent() << "if (it != _" << tenum->get_name() << "_VALUES_TO_NAMES.end()) {" << '\n';
indent_up();
out << indent() << "out << it->second;" << '\n';
indent_down();
out << indent() << "} else {" << '\n';
indent_up();
out << indent() << "out << static_cast<int>(val);" << '\n';
indent_down();
out << indent() << "}" << '\n';

scope_down(out);
out << '\n';
}
}

/**
* Generates a class that holds all the constants.
*/
Expand Down Expand Up @@ -1003,7 +1071,9 @@ void t_cpp_generator::generate_cpp_struct(t_struct* tstruct, bool is_exception)
}

if (!has_custom_ostream(tstruct)) {
generate_struct_print_method(f_types_impl_, tstruct);
// When template_streamop is enabled, printTo implementation goes to .tcc file
std::ostream& print_method_out = (gen_template_streamop_ ? f_types_tcc_ : f_types_impl_);
generate_struct_print_method(print_method_out, tstruct);
}

if (is_exception) {
Expand Down Expand Up @@ -1481,7 +1551,8 @@ void t_cpp_generator::generate_struct_declaration(ostream& out,

if (is_user_struct && !has_custom_ostream(tstruct)) {
out << indent();
if (!gen_templates_) out << "virtual ";
// Template methods cannot be virtual, so skip virtual keyword when using template_streamop
if (!gen_templates_ && !gen_template_streamop_) out << "virtual ";
generate_struct_print_method_decl(out, nullptr);
out << ";" << '\n';
}
Expand Down Expand Up @@ -1532,7 +1603,9 @@ void t_cpp_generator::generate_struct_declaration(ostream& out,
// When private_optional is enabled, optional members may be private.
// The generated namespace-scope operator<< needs friend access.
if (is_user_struct && gen_private_optional_) {
indent(out) << "friend ";
if (!gen_template_streamop_) {
indent(out) << "friend ";
}
generate_struct_ostream_operator_decl(out, tstruct);
}

Expand Down Expand Up @@ -1610,7 +1683,9 @@ void t_cpp_generator::generate_struct_definition(ostream& out,
}
}
if (is_user_struct) {
generate_struct_ostream_operator(out, tstruct);
// When template_streamop is enabled, operator<< implementation goes to .tcc file
std::ostream& ostream_op_out = (gen_template_streamop_ ? f_types_tcc_ : out);
generate_struct_ostream_operator(ostream_op_out, tstruct);
}
out << '\n';
}
Expand Down Expand Up @@ -1979,18 +2054,35 @@ void t_cpp_generator::generate_struct_swap_decl(std::ostream& out, t_struct* tst
}

void t_cpp_generator::generate_struct_ostream_operator_decl(std::ostream& out, t_struct* tstruct) {
out << "std::ostream& operator<<(std::ostream& out, const "
<< tstruct->get_name()
<< "& obj);" << '\n';
if (gen_template_streamop_) {
out << "template <typename OStream_>" << '\n';
if (gen_private_optional_) {
out << indent() << "friend ";
}
out << "OStream_& operator<<(OStream_& out, const "
<< tstruct->get_name()
<< "& obj);" << '\n';
} else {
out << "std::ostream& operator<<(std::ostream& out, const "
<< tstruct->get_name()
<< "& obj);" << '\n';
}
out << '\n';
}

void t_cpp_generator::generate_struct_ostream_operator(std::ostream& out, t_struct* tstruct) {
if (!has_custom_ostream(tstruct)) {
// thrift defines this behavior
out << "std::ostream& operator<<(std::ostream& out, const "
<< tstruct->get_name()
<< "& obj)" << '\n';
if (gen_template_streamop_) {
out << "template <typename OStream_>" << '\n';
out << "OStream_& operator<<(OStream_& out, const "
<< tstruct->get_name()
<< "& obj)" << '\n';
} else {
out << "std::ostream& operator<<(std::ostream& out, const "
<< tstruct->get_name()
<< "& obj)" << '\n';
}
scope_up(out);
out << indent() << "obj.printTo(out);" << '\n'
<< indent() << "return out;" << '\n';
Expand All @@ -2000,11 +2092,24 @@ void t_cpp_generator::generate_struct_ostream_operator(std::ostream& out, t_stru
}

void t_cpp_generator::generate_struct_print_method_decl(std::ostream& out, t_struct* tstruct) {
out << "void ";
if (tstruct) {
out << tstruct->get_name() << "::";
if (gen_template_streamop_) {
// For template version, the method itself is templated
if (!tstruct) {
// Declaration inside class - no "template" keyword here, will be added by caller if needed
out << "template <typename OStream_>" << '\n' << indent() << "void ";
} else {
// External implementation - needs template keyword
out << "template <typename OStream_>" << '\n' << indent() << "void ";
out << tstruct->get_name() << "::";
}
out << "printTo(OStream_& out) const";
} else {
out << "void ";
if (tstruct) {
out << tstruct->get_name() << "::";
}
out << "printTo(std::ostream& out) const";
}
out << "printTo(std::ostream& out) const";
}

void t_cpp_generator::generate_exception_what_method_decl(std::ostream& out,
Expand All @@ -2020,35 +2125,49 @@ void t_cpp_generator::generate_exception_what_method_decl(std::ostream& out,
}

namespace struct_ostream_operator_generator {
void generate_required_field_value(std::ostream& out, const t_field* field) {
void generate_required_field_value(std::ostream& out, const t_field* field, bool use_printto) {
if (use_printto) {
// For template_streamop, use printTo for direct streaming without temporary strings
// Use comma operator: out << "x=", printTo(out, x)
out << ", printTo(out, " << field->get_name() << ")";
return;
}
// For std::ostream, use to_string (backward compatible)
out << " << to_string(" << field->get_name() << ")";
}

void generate_optional_field_value(std::ostream& out, const t_field* field) {
out << "; (__isset." << field->get_name() << " ? (out";
generate_required_field_value(out, field);
out << ") : (out << \"<null>\"))";
void generate_optional_field_value(std::ostream& out, const t_field* field, bool use_printto) {
out << "; (__isset." << field->get_name() << " ? ";
if (use_printto) {
// For printTo, call directly without wrapping in (out ...)
out << "printTo(out, " << field->get_name() << ")";
} else {
// For to_string, need to wrap with (out << ...)
out << "(out << to_string(" << field->get_name() << "))";
}
out << " : (out << \"<null>\"))";
}

void generate_field_value(std::ostream& out, const t_field* field) {
void generate_field_value(std::ostream& out, const t_field* field, bool use_printto) {
if (field->get_req() == t_field::T_OPTIONAL)
generate_optional_field_value(out, field);
generate_optional_field_value(out, field, use_printto);
else
generate_required_field_value(out, field);
generate_required_field_value(out, field, use_printto);
}

void generate_field_name(std::ostream& out, const t_field* field) {
out << "\"" << field->get_name() << "=\"";
}

void generate_field(std::ostream& out, const t_field* field) {
void generate_field(std::ostream& out, const t_field* field, bool use_printto) {
generate_field_name(out, field);
generate_field_value(out, field);
generate_field_value(out, field, use_printto);
}

void generate_fields(std::ostream& out,
const vector<t_field*>& fields,
const std::string& indent) {
const std::string& indent,
bool use_printto) {
const vector<t_field*>::const_iterator beg = fields.begin();
const vector<t_field*>::const_iterator end = fields.end();

Expand All @@ -2059,7 +2178,7 @@ void generate_fields(std::ostream& out,
out << "\", \" << ";
}

generate_field(out, *it);
generate_field(out, *it, use_printto);
out << ";" << '\n';
}
}
Expand All @@ -2075,9 +2194,16 @@ void t_cpp_generator::generate_struct_print_method(std::ostream& out, t_struct*

indent_up();

bool use_printto = gen_template_streamop_;
if (use_printto) {
// For template_streamop, use printTo for direct streaming (better performance)
out << indent() << "using ::apache::thrift::printTo;" << '\n';
}
// Always include to_string as well for compatibility
out << indent() << "using ::apache::thrift::to_string;" << '\n';

out << indent() << "out << \"" << tstruct->get_name() << "(\";" << '\n';
struct_ostream_operator_generator::generate_fields(out, tstruct->get_members(), indent());
struct_ostream_operator_generator::generate_fields(out, tstruct->get_members(), indent(), use_printto);
out << indent() << "out << \")\";" << '\n';

indent_down();
Expand Down
Loading
Loading