THRIFT-5341 Old-Style-Cast & missing Override
Client: cpp
Patch: Kashirin Alex
This closes #2318
diff --git a/compiler/cpp/src/thrift/generate/t_cpp_generator.cc b/compiler/cpp/src/thrift/generate/t_cpp_generator.cc
index ceaa11f..4ef6acf 100644
--- a/compiler/cpp/src/thrift/generate/t_cpp_generator.cc
+++ b/compiler/cpp/src/thrift/generate/t_cpp_generator.cc
@@ -292,6 +292,13 @@
(ttype->annotations_.find("cpp.customostream") != ttype->annotations_.end());
}
+ /**
+ * Determine if all fields of t_struct's storage do not throw
+ * Move/Copy Constructors and Assignments applicable for 'noexcept'
+ * Move defaults to 'noexcept'
+ */
+ bool is_struct_storage_not_throwing(t_struct* tstruct) const;
+
private:
/**
* Returns the include prefix to use for a file generated by program, or the
@@ -954,11 +961,13 @@
indent(out) << tstruct->get_name() << "::" << tstruct->get_name();
if (is_move) {
- out << "( " << tstruct->get_name() << "&& ";
+ out << "(" << tstruct->get_name() << "&& ";
} else {
out << "(const " << tstruct->get_name() << "& ";
}
out << tmp_name << ") ";
+ if(is_move || is_struct_storage_not_throwing(tstruct))
+ out << "noexcept ";
if (is_exception)
out << ": TException() ";
out << "{" << endl;
@@ -976,11 +985,14 @@
if ((*f_iter)->get_req() != t_field::T_REQUIRED)
has_nonrequired_fields = true;
indent(out) << (*f_iter)->get_name() << " = "
- << maybeMove(tmp_name + "." + (*f_iter)->get_name(), is_move) << ";" << endl;
+ << maybeMove(
+ tmp_name + "." + (*f_iter)->get_name(),
+ is_move && is_complex_type((*f_iter)->get_type()))
+ << ";" << endl;
}
if (has_nonrequired_fields) {
- indent(out) << "__isset = " << maybeMove(tmp_name + ".__isset", is_move) << ";" << endl;
+ indent(out) << "__isset = " << maybeMove(tmp_name + ".__isset", false) << ";" << endl;
}
indent_down();
@@ -1005,8 +1017,10 @@
} else {
out << "const " << tstruct->get_name() << "& ";
}
- out << tmp_name << ") {" << endl;
-
+ out << tmp_name << ") ";
+ if(is_move || is_struct_storage_not_throwing(tstruct))
+ out << "noexcept ";
+ out << "{" << endl;
indent_up();
const vector<t_field*>& members = tstruct->get_members();
@@ -1021,10 +1035,13 @@
if ((*f_iter)->get_req() != t_field::T_REQUIRED)
has_nonrequired_fields = true;
indent(out) << (*f_iter)->get_name() << " = "
- << maybeMove(tmp_name + "." + (*f_iter)->get_name(), is_move) << ";" << endl;
+ << maybeMove(
+ tmp_name + "." + (*f_iter)->get_name(),
+ is_move && is_complex_type((*f_iter)->get_type()))
+ << ";" << endl;
}
if (has_nonrequired_fields) {
- indent(out) << "__isset = " << maybeMove(tmp_name + ".__isset", is_move) << ";" << endl;
+ indent(out) << "__isset = " << maybeMove(tmp_name + ".__isset", false) << ";" << endl;
}
indent(out) << "return *this;" << endl;
@@ -1111,47 +1128,71 @@
indent_up();
if (!pointers) {
+ bool ok_noexcept = is_struct_storage_not_throwing(tstruct);
// Copy constructor
- indent(out) << tstruct->get_name() << "(const " << tstruct->get_name() << "&);" << endl;
+ indent(out) << tstruct->get_name() << "(const " << tstruct->get_name() << "&)"
+ << (ok_noexcept? " noexcept" : "") << ';' << endl;
// Move constructor
if (gen_moveable_) {
- indent(out) << tstruct->get_name() << "(" << tstruct->get_name() << "&&);" << endl;
+ indent(out) << tstruct->get_name() << "(" << tstruct->get_name() << "&&) noexcept;"
+ << endl;
}
// Assignment Operator
- indent(out) << tstruct->get_name() << "& operator=(const " << tstruct->get_name() << "&);"
- << endl;
+ indent(out) << tstruct->get_name() << "& operator=(const " << tstruct->get_name() << "&)"
+ << (ok_noexcept? " noexcept" : "") << ';' << endl;
// Move assignment operator
if (gen_moveable_) {
- indent(out) << tstruct->get_name() << "& operator=(" << tstruct->get_name() << "&&);" << endl;
+ indent(out) << tstruct->get_name() << "& operator=(" << tstruct->get_name() << "&&) noexcept;"
+ << 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;
+ }
+ }
}
// Default constructor
- indent(out) << tstruct->get_name() << "()";
+ 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;
- if (t->is_enum()) {
- dval += "(" + type_name(t) + ")";
- }
- dval += (t->is_string() || is_reference(*m_iter)) ? "" : "0";
t_const_value* cv = (*m_iter)->get_value();
if (cv != nullptr) {
- dval = render_const_value(out, (*m_iter)->get_name(), t, cv);
+ 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;
- out << " : ";
- out << (*m_iter)->get_name() << "(" << dval << ")";
+ if(has_default_value) {
+ out << " : ";
+ } else {
+ out << '\n' << args_indent << ": ";
+ args_indent.append(" ");
+ }
} else {
- out << ", " << (*m_iter)->get_name() << "(" << dval << ")";
+ out << ",\n" << args_indent;
}
+ out << (*m_iter)->get_name() << "(" << dval << ")";
}
}
out << " {" << endl;
@@ -1248,7 +1289,10 @@
<< "uint32_t read(Protocol_* iprot);" << endl;
} else {
out << indent() << "uint32_t read("
- << "::apache::thrift::protocol::TProtocol* iprot);" << endl;
+ << "::apache::thrift::protocol::TProtocol* iprot)";
+ if(!is_exception && !extends.empty())
+ out << " override";
+ out << ';' << endl;
}
}
if (write) {
@@ -1257,7 +1301,10 @@
<< "uint32_t write(Protocol_* oprot) const;" << endl;
} else {
out << indent() << "uint32_t write("
- << "::apache::thrift::protocol::TProtocol* oprot) const;" << endl;
+ << "::apache::thrift::protocol::TProtocol* oprot) const";
+ if(!is_exception && !extends.empty())
+ out << " override";
+ out << ';' << endl;
}
}
out << endl;
@@ -1697,6 +1744,8 @@
out << tstruct->get_name() << "::";
}
out << "what() const noexcept";
+ if(!external)
+ out << " override";
}
namespace struct_ostream_operator_generator {
@@ -2059,8 +2108,10 @@
f_header_ << indent() << "typedef " << service_if_name << " Handler;" << endl << endl << indent()
<< "virtual ~" << factory_name << "() {}" << endl << endl << indent() << "virtual "
<< service_if_name << "* getHandler("
- << "const ::apache::thrift::TConnectionInfo& connInfo) = 0;" << endl << indent()
- << "virtual void releaseHandler(" << base_if_name << "* /* handler */) = 0;" << endl;
+ << "const ::apache::thrift::TConnectionInfo& connInfo)"
+ << (extends.empty() ? "" : " override") << " = 0;" << endl << indent()
+ << "virtual void releaseHandler(" << base_if_name << "* /* handler */)"
+ << (extends.empty() ? "" : " override") << " = 0;" << endl << indent();
indent_down();
f_header_ << "};" << endl << endl;
@@ -2074,9 +2125,9 @@
<< ">& iface) : iface_(iface) {}" << endl << indent() << "virtual ~"
<< singleton_factory_name << "() {}" << endl << endl << indent() << "virtual "
<< service_if_name << "* getHandler("
- << "const ::apache::thrift::TConnectionInfo&) {" << endl << indent()
+ << "const ::apache::thrift::TConnectionInfo&) override {" << endl << indent()
<< " return iface_.get();" << endl << indent() << "}" << endl << indent()
- << "virtual void releaseHandler(" << base_if_name << "* /* handler */) {}" << endl;
+ << "virtual void releaseHandler(" << base_if_name << "* /* handler */) override {}" << endl;
f_header_ << endl << " protected:" << endl << indent() << "::std::shared_ptr<" << service_if_name
<< "> iface_;" << endl;
@@ -2102,7 +2153,8 @@
vector<t_function*> functions = tservice->get_functions();
vector<t_function*>::iterator f_iter;
for (f_iter = functions.begin(); f_iter != functions.end(); ++f_iter) {
- f_header_ << indent() << function_signature(*f_iter, style, "", false) << " {" << endl;
+ f_header_ << indent() << function_signature(*f_iter, style, "", false)
+ << " override {" << endl;
indent_up();
t_type* returntype = (*f_iter)->get_returntype();
@@ -2340,7 +2392,7 @@
}
call += ")";
- f_header_ << indent() << function_signature(*f_iter, "") << " {" << endl;
+ f_header_ << indent() << function_signature(*f_iter, "") << " override {" << endl;
indent_up();
f_header_ << indent() << "size_t sz = ifaces_.size();" << endl << indent() << "size_t i = 0;"
<< endl << indent() << "for (; i < (sz - 1); ++i) {" << endl;
@@ -2547,7 +2599,8 @@
vector<t_function*>::const_iterator f_iter;
for (f_iter = functions.begin(); f_iter != functions.end(); ++f_iter) {
generate_java_doc(f_header_, *f_iter);
- indent(f_header_) << function_signature(*f_iter, ifstyle) << ";" << endl;
+ indent(f_header_) << function_signature(*f_iter, ifstyle)
+ << " override;" << endl;
// TODO(dreiss): Use private inheritance to avoid generating thise in cob-style.
if (style == "Concurrent" && !(*f_iter)->is_oneway()) {
// concurrent clients need to move the seqid from the send function to the
@@ -3096,7 +3149,8 @@
f_header_ << indent() << "virtual " << ret_type_ << "dispatchCall(" << finish_cob_
<< "::apache::thrift::protocol::TProtocol* iprot, "
<< "::apache::thrift::protocol::TProtocol* oprot, "
- << "const std::string& fname, int32_t seqid" << call_context_ << ");" << endl;
+ << "const std::string& fname, int32_t seqid" << call_context_
+ << ") override;" << endl;
if (generator_->gen_templates_) {
f_header_ << indent() << "virtual " << ret_type_ << "dispatchCallTemplated(" << finish_cob_
<< "Protocol_* iprot, Protocol_* oprot, "
@@ -3309,11 +3363,12 @@
indent_up();
f_header_ << indent() << factory_class_name_ << "(const ::std::shared_ptr< " << if_factory_name
- << " >& handlerFactory) :" << endl << indent()
+ << " >& handlerFactory) noexcept :" << endl << indent()
<< " handlerFactory_(handlerFactory) {}" << endl << endl << indent()
<< "::std::shared_ptr< ::apache::thrift::"
<< (style_ == "Cob" ? "async::TAsyncProcessor" : "TProcessor") << " > "
- << "getProcessor(const ::apache::thrift::TConnectionInfo& connInfo);" << endl;
+ << "getProcessor(const ::apache::thrift::TConnectionInfo& connInfo) override;"
+ << endl;
f_header_ << endl << " protected:" << endl << indent() << "::std::shared_ptr< "
<< if_factory_name << " > handlerFactory_;" << endl;
@@ -3506,9 +3561,9 @@
<< ") {" << endl;
if (!tfunction->is_oneway()) {
indent_up();
- out << indent() << "result." << (*x_iter)->get_name() << " = " << (*x_iter)->get_name()
- << ";" << endl << indent() << "result.__isset." << (*x_iter)->get_name() << " = true;"
- << endl;
+ out << indent() << "result." << (*x_iter)->get_name()
+ << " = std::move(" << (*x_iter)->get_name() << ");" << endl
+ << indent() << "result.__isset." << (*x_iter)->get_name() << " = true;" << endl;
indent_down();
out << indent() << "}";
} else {
@@ -3945,7 +4000,8 @@
} else if (type->is_enum()) {
string t = tmp("ecast");
out << indent() << "int32_t " << t << ";" << endl << indent() << "xfer += iprot->readI32(" << t
- << ");" << endl << indent() << name << " = (" << type_name(type) << ")" << t << ";" << endl;
+ << ");" << endl << indent() << name << " = static_cast<"
+ << type_name(type) << ">(" << t << ");" << endl;
} else {
printf("DO NOT KNOW HOW TO DESERIALIZE FIELD '%s' TYPE '%s'\n",
tfield->get_name().c_str(),
@@ -4150,7 +4206,7 @@
+ name;
}
} else if (type->is_enum()) {
- out << "writeI32((int32_t)" << name << ");";
+ out << "writeI32(static_cast<int32_t>(" << name << "));";
}
out << endl;
} else {
@@ -4485,13 +4541,13 @@
result += " = 0";
break;
case t_base_type::TYPE_DOUBLE:
- result += " = (double)0";
+ result += " = 0.0";
break;
default:
throw "compiler error: no C++ initializer for base type " + t_base_type::t_base_name(tbase);
}
} else if (type->is_enum()) {
- result += " = (" + type_name(type) + ")0";
+ result += " = static_cast<" + type_name(type) + ">(0)";
}
}
if (!reference) {
@@ -4619,6 +4675,44 @@
throw "INVALID TYPE IN type_to_enum: " + type->get_name();
}
+
+bool t_cpp_generator::is_struct_storage_not_throwing(t_struct* tstruct) const {
+ vector<t_field*> members = tstruct->get_members();
+
+ for(size_t i=0; i < members.size(); ++i) {
+ t_type* type = get_true_type(members[i]->get_type());
+
+ if(type->is_enum())
+ continue;
+ if(type->is_xception())
+ return false;
+ if(type->is_base_type()) switch(((t_base_type*)type)->get_base()) {
+ case t_base_type::TYPE_BOOL:
+ case t_base_type::TYPE_I8:
+ case t_base_type::TYPE_I16:
+ case t_base_type::TYPE_I32:
+ case t_base_type::TYPE_I64:
+ case t_base_type::TYPE_DOUBLE:
+ continue;
+ case t_base_type::TYPE_VOID:
+ case t_base_type::TYPE_STRING:
+ default:
+ return false;
+ }
+ if(type->is_struct()) {
+ const vector<t_field*>& more = ((t_struct*)type)->get_members();
+ for(auto it = more.begin(); it < more.end(); ++it) {
+ if(std::find(members.begin(), members.end(), *it) == members.end())
+ members.push_back(*it);
+ }
+ continue;
+ }
+ return false; // rest-as-throwing(require-alloc)
+ }
+ return true;
+}
+
+
string t_cpp_generator::get_include_prefix(const t_program& program) const {
string include_prefix = program.get_include_prefix();
if (!use_include_prefix_ || (include_prefix.size() > 0 && include_prefix[0] == '/')) {