Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature/thrift 5682 cherry picked from tinloaf cpp move to implementation #13

Open
wants to merge 6 commits into
base: master
Choose a base branch
from
Open
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
3 changes: 3 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -207,4 +207,7 @@ To run the cross-language test suite, please run:
This will run a set of tests that use different language clients and
servers.

Changes
=======

Bringing in this PR for C++ compatibility / https://github.com/apache/thrift/pull/2755
240 changes: 157 additions & 83 deletions compiler/cpp/src/thrift/generate/t_cpp_generator.cc
Original file line number Diff line number Diff line change
Expand Up @@ -143,14 +143,17 @@ class t_cpp_generator : public t_oop_generator {
std::ostream& force_cpp_out,
t_struct* tstruct,
bool setters = true,
bool is_user_struct = false);
bool is_user_struct = false,
bool pointers = false);
void generate_copy_constructor(std::ostream& out, t_struct* tstruct, bool is_exception);
void generate_move_constructor(std::ostream& out, t_struct* tstruct, bool is_exception);
void generate_default_constructor(std::ostream& out, t_struct* tstruct, bool is_exception);
void generate_constructor_helper(std::ostream& out,
t_struct* tstruct,
bool is_excpetion,
bool is_move);
void generate_assignment_operator(std::ostream& out, t_struct* tstruct);
void generate_equality_operator(std::ostream& out, t_struct* tstruct);
void generate_move_assignment_operator(std::ostream& out, t_struct* tstruct);
void generate_assignment_helper(std::ostream& out, t_struct* tstruct, bool is_move);
void generate_struct_reader(std::ostream& out, t_struct* tstruct, bool pointers = false);
Expand Down Expand Up @@ -300,6 +303,12 @@ class t_cpp_generator : public t_oop_generator {
*/
bool is_struct_storage_not_throwing(t_struct* tstruct) const;

/**
* Helper function to determine whether any of the members of our struct
* has a default value.
*/
bool has_field_with_default_value(t_struct* tstruct);

private:
/**
* Returns the include prefix to use for a file generated by program, or the
Expand Down Expand Up @@ -908,12 +917,15 @@ void t_cpp_generator::generate_forward_declaration(t_struct* tstruct) {
*/
void t_cpp_generator::generate_cpp_struct(t_struct* tstruct, bool is_exception) {
generate_struct_declaration(f_types_, tstruct, is_exception, false, true, true, true, true);
generate_struct_definition(f_types_impl_, f_types_impl_, tstruct, true, true);
generate_struct_definition(f_types_impl_, f_types_impl_, tstruct, true, true, false);

std::ostream& out = (gen_templates_ ? f_types_tcc_ : f_types_impl_);
generate_struct_reader(out, tstruct);
generate_struct_writer(out, tstruct);
generate_struct_swap(f_types_impl_, tstruct);
if (!gen_no_default_operators_) {
generate_equality_operator(f_types_impl_, tstruct);
}
generate_copy_constructor(f_types_impl_, tstruct, is_exception);
if (gen_moveable_) {
generate_move_constructor(f_types_impl_, tstruct, is_exception);
Expand All @@ -934,6 +946,130 @@ void t_cpp_generator::generate_cpp_struct(t_struct* tstruct, bool is_exception)
has_members_ = true;
}

void t_cpp_generator::generate_equality_operator(std::ostream& out, t_struct* tstruct) {
// Get members
vector<t_field*>::const_iterator m_iter;
const vector<t_field*>& members = tstruct->get_members();

out << indent() << "bool " << tstruct->get_name()
<< "::operator==(const " << tstruct->get_name() << " & "
<< (members.size() > 0 ? "rhs" : "/* rhs */") << ") const" << endl;
scope_up(out);
for (m_iter = members.begin(); m_iter != members.end(); ++m_iter) {
// Most existing Thrift code does not use isset or optional/required,
// so we treat "default" fields as required.
if ((*m_iter)->get_req() != t_field::T_OPTIONAL) {
out << indent() << "if (!(" << (*m_iter)->get_name() << " == rhs."
<< (*m_iter)->get_name() << "))" << endl << indent() << " return false;" << endl;
} else {
out << indent() << "if (__isset." << (*m_iter)->get_name() << " != rhs.__isset."
<< (*m_iter)->get_name() << ")" << endl << indent() << " return false;" << endl
<< indent() << "else if (__isset." << (*m_iter)->get_name() << " && !("
<< (*m_iter)->get_name() << " == rhs." << (*m_iter)->get_name() << "))" << endl
<< indent() << " return false;" << endl;
}
}
indent(out) << "return true;" << endl;
scope_down(out);
out << "\n";
}

bool t_cpp_generator::has_field_with_default_value(t_struct* tstruct)
{
vector<t_field*>::const_iterator m_iter;
const vector<t_field*>& members = tstruct->get_members();

for (m_iter = members.begin(); m_iter != members.end(); ++m_iter) {
t_type* t = get_true_type((*m_iter)->get_type());
if (is_reference(*m_iter) || t->is_string()) {
t_const_value* cv = (*m_iter)->get_value();
if (cv != nullptr) {
return true;
}
}
}

return false;
}

void t_cpp_generator::generate_default_constructor(ostream& out,
t_struct* tstruct,
bool is_exception) {
// Get members
vector<t_field*>::const_iterator m_iter;
const vector<t_field*>& members = tstruct->get_members();

bool has_default_value = has_field_with_default_value(tstruct);

std::string clsname_ctor = tstruct->get_name() + "::" + tstruct->get_name() + "()";
indent(out) << clsname_ctor << (has_default_value ? "" : " noexcept");

//
// Start generating initializer list
//

bool init_ctor = false;
std::string args_indent(" ");

// Default-initialize TException, if it is our base type
if (is_exception)
{
out << "\n";
indent(out) << " : ";
out << "TException()";
init_ctor = true;
}

// Default-initialize all members that should be initialized in
// the initializer block
for (m_iter = members.begin(); m_iter != members.end(); ++m_iter) {
t_type* t = get_true_type((*m_iter)->get_type());
if (t->is_base_type() || t->is_enum() || is_reference(*m_iter)) {
string dval;
t_const_value* cv = (*m_iter)->get_value();
if (cv != nullptr) {
dval += render_const_value(out, (*m_iter)->get_name(), t, cv);
} else if (t->is_enum()) {
dval += "static_cast<" + type_name(t) + ">(0)";
} else {
dval += (t->is_string() || is_reference(*m_iter)) ? "" : "0";
}
if (!init_ctor) {
init_ctor = true;
if(has_default_value) {
out << " : ";
} else {
out << '\n' << args_indent << ": ";
args_indent.append(" ");
}
} else {
out << ",\n" << args_indent;
}

out << (*m_iter)->get_name() << "(" << dval << ")";
}
}

//
// Start generating body
//

out << " {" << endl;
indent_up();
// TODO(dreiss): When everything else in Thrift is perfect,
// do more of these in the initializer list.
for (m_iter = members.begin(); m_iter != members.end(); ++m_iter) {
t_type* t = get_true_type((*m_iter)->get_type());
if (!t->is_base_type() && !t->is_enum() && !is_reference(*m_iter)) {
t_const_value* cv = (*m_iter)->get_value();
if (cv != nullptr) {
print_const_value(out, (*m_iter)->get_name(), t, cv);
}
}
}
scope_down(out);
}

void t_cpp_generator::generate_copy_constructor(ostream& out,
t_struct* tstruct,
bool is_exception) {
Expand Down Expand Up @@ -1154,66 +1290,11 @@ void t_cpp_generator::generate_struct_declaration(ostream& out,
<< endl;
}

bool has_default_value = false;
for (m_iter = members.begin(); m_iter != members.end(); ++m_iter) {
t_type* t = get_true_type((*m_iter)->get_type());
if (is_reference(*m_iter) || t->is_string()) {
t_const_value* cv = (*m_iter)->get_value();
if (cv != nullptr) {
has_default_value = true;
break;
}
}
}

bool has_default_value = has_field_with_default_value(tstruct);

// Default constructor
std::string clsname_ctor = tstruct->get_name() + "()";
indent(out) << clsname_ctor << (has_default_value ? "" : " noexcept");

bool init_ctor = false;
std::string args_indent(
indent().size() + clsname_ctor.size() + (has_default_value ? 3 : -1), ' ');

for (m_iter = members.begin(); m_iter != members.end(); ++m_iter) {
t_type* t = get_true_type((*m_iter)->get_type());
if (t->is_base_type() || t->is_enum() || is_reference(*m_iter)) {
string dval;
t_const_value* cv = (*m_iter)->get_value();
if (cv != nullptr) {
dval += render_const_value(out, (*m_iter)->get_name(), t, cv);
} else if (t->is_enum()) {
dval += "static_cast<" + type_name(t) + ">(0)";
} else {
dval += (t->is_string() || is_reference(*m_iter)) ? "" : "0";
}
if (!init_ctor) {
init_ctor = true;
if(has_default_value) {
out << " : ";
} else {
out << '\n' << args_indent << ": ";
args_indent.append(" ");
}
} else {
out << ",\n" << args_indent;
}
out << (*m_iter)->get_name() << "(" << dval << ")";
}
}
out << " {" << endl;
indent_up();
// TODO(dreiss): When everything else in Thrift is perfect,
// do more of these in the initializer list.
for (m_iter = members.begin(); m_iter != members.end(); ++m_iter) {
t_type* t = get_true_type((*m_iter)->get_type());
if (!t->is_base_type() && !t->is_enum() && !is_reference(*m_iter)) {
t_const_value* cv = (*m_iter)->get_value();
if (cv != nullptr) {
print_const_value(out, (*m_iter)->get_name(), t, cv);
}
}
}
scope_down(out);
indent(out) << clsname_ctor << (has_default_value ? "" : " noexcept") << ";" << endl;
}

if (tstruct->annotations_.find("final") == tstruct->annotations_.end()) {
Expand Down Expand Up @@ -1254,27 +1335,10 @@ void t_cpp_generator::generate_struct_declaration(ostream& out,
if (!pointers) {
// Should we generate default operators?
if (!gen_no_default_operators_) {
// Generate an equality testing operator. Make it inline since the compiler
// will do a better job than we would when deciding whether to inline it.
// Generate an equality testing operator.
out << indent() << "bool operator == (const " << tstruct->get_name() << " & "
<< (members.size() > 0 ? "rhs" : "/* rhs */") << ") const" << endl;
scope_up(out);
for (m_iter = members.begin(); m_iter != members.end(); ++m_iter) {
// Most existing Thrift code does not use isset or optional/required,
// so we treat "default" fields as required.
if ((*m_iter)->get_req() != t_field::T_OPTIONAL) {
out << indent() << "if (!(" << (*m_iter)->get_name() << " == rhs."
<< (*m_iter)->get_name() << "))" << endl << indent() << " return false;" << endl;
} else {
out << indent() << "if (__isset." << (*m_iter)->get_name() << " != rhs.__isset."
<< (*m_iter)->get_name() << ")" << endl << indent() << " return false;" << endl
<< indent() << "else if (__isset." << (*m_iter)->get_name() << " && !("
<< (*m_iter)->get_name() << " == rhs." << (*m_iter)->get_name() << "))" << endl
<< indent() << " return false;" << endl;
}
}
indent(out) << "return true;" << endl;
scope_down(out);
<< (members.size() > 0 ? "rhs" : "/* rhs */") << ") const;" << endl;

out << indent() << "bool operator != (const " << tstruct->get_name() << " &rhs) const {"
<< endl << indent() << " return !(*this == rhs);" << endl << indent() << "}" << endl
<< endl;
Expand Down Expand Up @@ -1350,7 +1414,8 @@ void t_cpp_generator::generate_struct_definition(ostream& out,
ostream& force_cpp_out,
t_struct* tstruct,
bool setters,
bool is_user_struct) {
bool is_user_struct,
bool pointers) {
// Get members
vector<t_field*>::const_iterator m_iter;
const vector<t_field*>& members = tstruct->get_members();
Expand All @@ -1365,6 +1430,14 @@ void t_cpp_generator::generate_struct_definition(ostream& out,
force_cpp_out << indent() << "}" << endl << endl;
}

if (!pointers)
{
// 'force_cpp_out' always goes into the .cpp file, and never into a .tcc
// file in case templates are involved. Since the constructor is not templated,
// putting it into the (later included) .tcc file would cause ODR violations.
generate_default_constructor(force_cpp_out, tstruct, false);
}

// Create a setter function for each field
if (setters) {
for (m_iter = members.begin(); m_iter != members.end(); ++m_iter) {
Expand Down Expand Up @@ -2000,9 +2073,10 @@ void t_cpp_generator::generate_service_helpers(t_service* tservice) {
generate_struct_definition(out, f_service_, ts, false);
generate_struct_reader(out, ts);
generate_struct_writer(out, ts);

ts->set_name(tservice->get_name() + "_" + (*f_iter)->get_name() + "_pargs");
generate_struct_declaration(f_header_, ts, false, true, false, true);
generate_struct_definition(out, f_service_, ts, false);
generate_struct_definition(out, f_service_, ts, false, false, true);
generate_struct_writer(out, ts, true);
ts->set_name(name_orig);

Expand Down Expand Up @@ -3450,7 +3524,7 @@ void t_cpp_generator::generate_function_helpers(t_service* tservice, t_function*

result.set_name(tservice->get_name() + "_" + tfunction->get_name() + "_presult");
generate_struct_declaration(f_header_, &result, false, true, true, gen_cob_style_);
generate_struct_definition(out, f_service_, &result, false);
generate_struct_definition(out, f_service_, &result, false, false, true);
generate_struct_reader(out, &result, true);
if (gen_cob_style_) {
generate_struct_writer(out, &result, true);
Expand Down