http://git-wip-us.apache.org/repos/asf/incubator-hawq/blob/d709f67d/depends/thirdparty/thrift/compiler/cpp/src/generate/t_cpp_generator.cc ---------------------------------------------------------------------- diff --git a/depends/thirdparty/thrift/compiler/cpp/src/generate/t_cpp_generator.cc b/depends/thirdparty/thrift/compiler/cpp/src/generate/t_cpp_generator.cc new file mode 100644 index 0000000..4e03d94 --- /dev/null +++ b/depends/thirdparty/thrift/compiler/cpp/src/generate/t_cpp_generator.cc @@ -0,0 +1,4367 @@ +/* + * 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. + * + * Contains some contributions under the Thrift Software License. + * Please see doc/old-thrift-license.txt in the Thrift distribution for + * details. + */ + +#include <cassert> + +#include <fstream> +#include <iostream> +#include <sstream> +#include <string> +#include <vector> + +#include <sys/stat.h> + +#include "platform.h" +#include "t_oop_generator.h" + +using std::map; +using std::ofstream; +using std::ostream; +using std::string; +using std::vector; + +static const string endl = "\n"; // avoid ostream << std::endl flushes + +/** + * C++ code generator. This is legitimacy incarnate. + * + */ +class t_cpp_generator : public t_oop_generator { +public: + t_cpp_generator(t_program* program, + const std::map<std::string, std::string>& parsed_options, + const std::string& option_string) + : t_oop_generator(program) { + (void)option_string; + std::map<std::string, std::string>::const_iterator iter; + + iter = parsed_options.find("pure_enums"); + gen_pure_enums_ = (iter != parsed_options.end()); + + iter = parsed_options.find("include_prefix"); + use_include_prefix_ = (iter != parsed_options.end()); + + iter = parsed_options.find("cob_style"); + gen_cob_style_ = (iter != parsed_options.end()); + + iter = parsed_options.find("no_client_completion"); + gen_no_client_completion_ = (iter != parsed_options.end()); + + iter = parsed_options.find("no_default_operators"); + gen_no_default_operators_ = (iter != parsed_options.end()); + + iter = parsed_options.find("templates"); + gen_templates_ = (iter != parsed_options.end()); + + gen_templates_only_ = (iter != parsed_options.end() && iter->second == "only"); + + iter = parsed_options.find("moveable_types"); + gen_moveable_ = (iter != parsed_options.end()); + + out_dir_base_ = "gen-cpp"; + } + + /** + * Init and close methods + */ + + void init_generator(); + void close_generator(); + + void generate_consts(std::vector<t_const*> consts); + + /** + * Program-level generation functions + */ + + void generate_typedef(t_typedef* ttypedef); + void generate_enum(t_enum* tenum); + void generate_forward_declaration(t_struct* tstruct); + void generate_struct(t_struct* tstruct) { generate_cpp_struct(tstruct, false); } + void generate_xception(t_struct* txception) { generate_cpp_struct(txception, true); } + void generate_cpp_struct(t_struct* tstruct, bool is_exception); + + void generate_service(t_service* tservice); + + void print_const_value(std::ofstream& out, std::string name, t_type* type, t_const_value* value); + std::string render_const_value(std::ofstream& out, + std::string name, + t_type* type, + t_const_value* value); + + void generate_struct_declaration(std::ofstream& out, + t_struct* tstruct, + bool is_exception = false, + bool pointers = false, + bool read = true, + bool write = true, + bool swap = false, + bool stream = false); + void generate_struct_definition(std::ofstream& out, + std::ofstream& force_cpp_out, + t_struct* tstruct, + bool setters = true); + void generate_copy_constructor(std::ofstream& out, t_struct* tstruct, bool is_exception); + void generate_move_constructor(std::ofstream& out, t_struct* tstruct, bool is_exception); + void generate_constructor_helper(std::ofstream& out, + t_struct* tstruct, + bool is_excpetion, + bool is_move); + void generate_assignment_operator(std::ofstream& out, t_struct* tstruct); + void generate_move_assignment_operator(std::ofstream& out, t_struct* tstruct); + void generate_assignment_helper(std::ofstream& out, t_struct* tstruct, bool is_move); + void generate_struct_reader(std::ofstream& out, t_struct* tstruct, bool pointers = false); + 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_print_method(std::ofstream& out, t_struct* tstruct); + void generate_exception_what_method(std::ofstream& out, t_struct* tstruct); + + /** + * Service-level generation functions + */ + + void generate_service_interface(t_service* tservice, string style); + void generate_service_interface_factory(t_service* tservice, string style); + void generate_service_null(t_service* tservice, string style); + void generate_service_multiface(t_service* tservice); + void generate_service_helpers(t_service* tservice); + void generate_service_client(t_service* tservice, string style); + void generate_service_processor(t_service* tservice, string style); + void generate_service_skeleton(t_service* tservice); + void generate_process_function(t_service* tservice, + t_function* tfunction, + string style, + bool specialized = false); + void generate_function_helpers(t_service* tservice, t_function* tfunction); + void generate_service_async_skeleton(t_service* tservice); + + /** + * Serialization constructs + */ + + void generate_deserialize_field(std::ofstream& out, + t_field* tfield, + std::string prefix = "", + std::string suffix = ""); + + void generate_deserialize_struct(std::ofstream& out, + t_struct* tstruct, + std::string prefix = "", + bool pointer = false); + + void generate_deserialize_container(std::ofstream& out, t_type* ttype, std::string prefix = ""); + + void generate_deserialize_set_element(std::ofstream& out, t_set* tset, std::string prefix = ""); + + void generate_deserialize_map_element(std::ofstream& out, t_map* tmap, std::string prefix = ""); + + void generate_deserialize_list_element(std::ofstream& out, + t_list* tlist, + std::string prefix, + bool push_back, + std::string index); + + void generate_serialize_field(std::ofstream& out, + t_field* tfield, + std::string prefix = "", + std::string suffix = ""); + + void generate_serialize_struct(std::ofstream& out, + t_struct* tstruct, + std::string prefix = "", + bool pointer = false); + + void generate_serialize_container(std::ofstream& out, t_type* ttype, std::string prefix = ""); + + void generate_serialize_map_element(std::ofstream& out, t_map* tmap, std::string iter); + + void generate_serialize_set_element(std::ofstream& out, t_set* tmap, std::string iter); + + void generate_serialize_list_element(std::ofstream& out, t_list* tlist, std::string iter); + + void generate_function_call(ostream& out, + t_function* tfunction, + string target, + string iface, + string arg_prefix); + /* + * Helper rendering functions + */ + + std::string namespace_prefix(std::string ns); + std::string namespace_open(std::string ns); + std::string namespace_close(std::string ns); + std::string type_name(t_type* ttype, bool in_typedef = false, bool arg = false); + std::string base_type_name(t_base_type::t_base tbase); + std::string declare_field(t_field* tfield, + bool init = false, + bool pointer = false, + bool constant = false, + bool reference = false); + std::string function_signature(t_function* tfunction, + std::string style, + std::string prefix = "", + bool name_params = true); + std::string cob_function_signature(t_function* tfunction, + std::string prefix = "", + bool name_params = true); + std::string argument_list(t_struct* tstruct, bool name_params = true, bool start_comma = false); + std::string type_to_enum(t_type* ttype); + + void generate_enum_constant_list(std::ofstream& f, + const vector<t_enum_value*>& constants, + const char* prefix, + const char* suffix, + bool include_values); + + void generate_struct_ostream_operator(std::ofstream& f, t_struct* tstruct); + void generate_struct_print_method_decl(std::ofstream& f, t_struct* tstruct); + void generate_exception_what_method_decl(std::ofstream& f, + t_struct* tstruct, + bool external = false); + + bool is_reference(t_field* tfield) { return tfield->get_reference(); } + + bool is_complex_type(t_type* ttype) { + ttype = get_true_type(ttype); + + return ttype->is_container() || ttype->is_struct() || ttype->is_xception() + || (ttype->is_base_type() + && (((t_base_type*)ttype)->get_base() == t_base_type::TYPE_STRING)); + } + + void set_use_include_prefix(bool use_include_prefix) { use_include_prefix_ = use_include_prefix; } + +private: + /** + * Returns the include prefix to use for a file generated by program, or the + * empty string if no include prefix should be used. + */ + std::string get_include_prefix(const t_program& program) const; + + /** + * True if we should generate pure enums for Thrift enums, instead of wrapper classes. + */ + bool gen_pure_enums_; + + /** + * True if we should generate templatized reader/writer methods. + */ + bool gen_templates_; + + /** + * True iff we should generate process function pointers for only templatized + * reader/writer methods. + */ + bool gen_templates_only_; + + /** + * True if we should generate move constructors & assignment operators. + */ + bool gen_moveable_; + + /** + * True iff we should use a path prefix in our #include statements for other + * thrift-generated header files. + */ + bool use_include_prefix_; + + /** + * True if we should generate "Continuation OBject"-style classes as well. + */ + bool gen_cob_style_; + + /** + * True if we should omit calls to completion__() in CobClient class. + */ + bool gen_no_client_completion_; + + /** + * True if we should omit generating the default opeartors ==, != and <. + */ + bool gen_no_default_operators_; + + /** + * Strings for namespace, computed once up front then used directly + */ + + std::string ns_open_; + std::string ns_close_; + + /** + * File streams, stored here to avoid passing them as parameters to every + * function. + */ + + std::ofstream f_types_; + std::ofstream f_types_impl_; + std::ofstream f_types_tcc_; + std::ofstream f_header_; + std::ofstream f_service_; + std::ofstream f_service_tcc_; + + // The ProcessorGenerator is used to generate parts of the code, + // so it needs access to many of our protected members and methods. + // + // TODO: The code really should be cleaned up so that helper methods for + // writing to the output files are separate from the generator classes + // themselves. + friend class ProcessorGenerator; +}; + +/** + * Prepares for file generation by opening up the necessary file output + * streams. + */ +void t_cpp_generator::init_generator() { + // Make output directory + MKDIR(get_out_dir().c_str()); + + // Make output file + string f_types_name = get_out_dir() + program_name_ + "_types.h"; + f_types_.open(f_types_name.c_str()); + + 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_) { + // 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"; + f_types_tcc_.open(f_types_tcc_name.c_str()); + } + + // Print header + f_types_ << autogen_comment(); + f_types_impl_ << autogen_comment(); + f_types_tcc_ << autogen_comment(); + + // Start ifndef + f_types_ << "#ifndef " << program_name_ << "_TYPES_H" << endl << "#define " << program_name_ + << "_TYPES_H" << endl << endl; + f_types_tcc_ << "#ifndef " << program_name_ << "_TYPES_TCC" << endl << "#define " << program_name_ + << "_TYPES_TCC" << endl << endl; + + // 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 + << "#include <thrift/transport/TTransport.h>" << endl + << endl; + // Include C++xx compatibility header + f_types_ << "#include <thrift/cxxfunctional.h>" << endl; + + // Include other Thrift includes + const vector<t_program*>& includes = program_->get_includes(); + for (size_t i = 0; i < includes.size(); ++i) { + f_types_ << "#include \"" << get_include_prefix(*(includes[i])) << includes[i]->get_name() + << "_types.h\"" << endl; + + // XXX(simpkins): If gen_templates_ is enabled, we currently assume all + // included files were also generated with templates enabled. + f_types_tcc_ << "#include \"" << get_include_prefix(*(includes[i])) << includes[i]->get_name() + << "_types.tcc\"" << endl; + } + f_types_ << endl; + + // Include custom headers + const vector<string>& cpp_includes = program_->get_cpp_includes(); + for (size_t i = 0; i < cpp_includes.size(); ++i) { + if (cpp_includes[i][0] == '<') { + f_types_ << "#include " << cpp_includes[i] << endl; + } else { + f_types_ << "#include \"" << cpp_includes[i] << "\"" << endl; + } + } + f_types_ << endl; + + // Include the types file + f_types_impl_ << "#include \"" << get_include_prefix(*get_program()) << program_name_ + << "_types.h\"" << endl << endl; + f_types_tcc_ << "#include \"" << get_include_prefix(*get_program()) << program_name_ + << "_types.h\"" << endl << endl; + + // The swap() code needs <algorithm> for std::swap() + 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")); + ns_close_ = namespace_close(program_->get_namespace("cpp")); + + f_types_ << ns_open_ << endl << endl; + + f_types_impl_ << ns_open_ << endl << endl; + + f_types_tcc_ << ns_open_ << endl << endl; +} + +/** + * Closes the output files. + */ +void t_cpp_generator::close_generator() { + // Close namespace + f_types_ << ns_close_ << endl << endl; + f_types_impl_ << ns_close_ << endl; + f_types_tcc_ << ns_close_ << endl << endl; + + // 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_) { + f_types_ << "#include \"" << get_include_prefix(*get_program()) << program_name_ + << "_types.tcc\"" << endl << endl; + } + + // Close ifndef + f_types_ << "#endif" << endl; + f_types_tcc_ << "#endif" << endl; + + // Close output file + f_types_.close(); + f_types_impl_.close(); + f_types_tcc_.close(); +} + +/** + * Generates a typedef. This is just a simple 1-liner in C++ + * + * @param ttypedef The type definition + */ +void t_cpp_generator::generate_typedef(t_typedef* ttypedef) { + f_types_ << indent() << "typedef " << type_name(ttypedef->get_type(), true) << " " + << ttypedef->get_symbolic() << ";" << endl << endl; +} + +void t_cpp_generator::generate_enum_constant_list(std::ofstream& f, + const vector<t_enum_value*>& constants, + const char* prefix, + const char* suffix, + bool include_values) { + f << " {" << endl; + indent_up(); + + vector<t_enum_value*>::const_iterator c_iter; + bool first = true; + for (c_iter = constants.begin(); c_iter != constants.end(); ++c_iter) { + if (first) { + first = false; + } else { + f << "," << endl; + } + indent(f) << prefix << (*c_iter)->get_name() << suffix; + if (include_values) { + f << " = " << (*c_iter)->get_value(); + } + } + + f << endl; + indent_down(); + indent(f) << "};" << endl; +} + +/** + * Generates code for an enumerated type. In C++, this is essentially the same + * as the thrift definition itself, using the enum keyword in C++. + * + * @param tenum The enumeration + */ +void t_cpp_generator::generate_enum(t_enum* tenum) { + vector<t_enum_value*> constants = tenum->get_constants(); + + std::string enum_name = tenum->get_name(); + if (!gen_pure_enums_) { + enum_name = "type"; + f_types_ << indent() << "struct " << tenum->get_name() << " {" << endl; + indent_up(); + } + f_types_ << indent() << "enum " << enum_name; + + generate_enum_constant_list(f_types_, constants, "", "", true); + + if (!gen_pure_enums_) { + indent_down(); + f_types_ << "};" << endl; + } + + f_types_ << endl; + + /** + Generate a character array of enum names for debugging purposes. + */ + std::string prefix = ""; + if (!gen_pure_enums_) { + prefix = tenum->get_name() + "::"; + } + + f_types_impl_ << indent() << "int _k" << tenum->get_name() << "Values[] ="; + generate_enum_constant_list(f_types_impl_, constants, prefix.c_str(), "", false); + + f_types_impl_ << indent() << "const char* _k" << tenum->get_name() << "Names[] ="; + generate_enum_constant_list(f_types_impl_, constants, "\"", "\"", false); + + f_types_ << indent() << "extern const std::map<int, const char*> _" << tenum->get_name() + << "_VALUES_TO_NAMES;" << endl << endl; + + f_types_impl_ << indent() << "const std::map<int, const char*> _" << tenum->get_name() + << "_VALUES_TO_NAMES(::apache::thrift::TEnumIterator(" << constants.size() << ", _k" + << tenum->get_name() << "Values" + << ", _k" << tenum->get_name() << "Names), " + << "::apache::thrift::TEnumIterator(-1, NULL, NULL));" << endl << endl; +} + +/** + * Generates a class that holds all the constants. + */ +void t_cpp_generator::generate_consts(std::vector<t_const*> consts) { + string f_consts_name = get_out_dir() + program_name_ + "_constants.h"; + ofstream f_consts; + f_consts.open(f_consts_name.c_str()); + + string f_consts_impl_name = get_out_dir() + program_name_ + "_constants.cpp"; + ofstream f_consts_impl; + f_consts_impl.open(f_consts_impl_name.c_str()); + + // Print header + f_consts << autogen_comment(); + f_consts_impl << autogen_comment(); + + // Start ifndef + f_consts << "#ifndef " << program_name_ << "_CONSTANTS_H" << endl << "#define " << program_name_ + << "_CONSTANTS_H" << endl << endl << "#include \"" << get_include_prefix(*get_program()) + << program_name_ << "_types.h\"" << endl << endl << ns_open_ << endl << endl; + + f_consts_impl << "#include \"" << get_include_prefix(*get_program()) << program_name_ + << "_constants.h\"" << endl << endl << ns_open_ << endl << endl; + + f_consts << "class " << program_name_ << "Constants {" << endl << " public:" << endl << " " + << program_name_ << "Constants();" << endl << endl; + indent_up(); + vector<t_const*>::iterator c_iter; + for (c_iter = consts.begin(); c_iter != consts.end(); ++c_iter) { + string name = (*c_iter)->get_name(); + t_type* type = (*c_iter)->get_type(); + f_consts << indent() << type_name(type) << " " << name << ";" << endl; + } + indent_down(); + f_consts << "};" << endl; + + f_consts_impl << "const " << program_name_ << "Constants g_" << program_name_ << "_constants;" + << endl << endl << program_name_ << "Constants::" << program_name_ + << "Constants() {" << endl; + indent_up(); + for (c_iter = consts.begin(); c_iter != consts.end(); ++c_iter) { + print_const_value(f_consts_impl, + (*c_iter)->get_name(), + (*c_iter)->get_type(), + (*c_iter)->get_value()); + } + indent_down(); + indent(f_consts_impl) << "}" << endl; + + f_consts << endl << "extern const " << program_name_ << "Constants g_" << program_name_ + << "_constants;" << endl << endl << ns_close_ << endl << endl << "#endif" << endl; + f_consts.close(); + + f_consts_impl << endl << ns_close_ << endl << endl; +} + +/** + * Prints the value of a constant with the given type. Note that type checking + * is NOT performed in this function as it is always run beforehand using the + * validate_types method in main.cc + */ +void t_cpp_generator::print_const_value(ofstream& out, + string name, + t_type* type, + t_const_value* value) { + type = get_true_type(type); + if (type->is_base_type()) { + string v2 = render_const_value(out, name, type, value); + indent(out) << name << " = " << v2 << ";" << endl << endl; + } else if (type->is_enum()) { + indent(out) << name << " = (" << type_name(type) << ")" << value->get_integer() << ";" << endl + << endl; + } else if (type->is_struct() || type->is_xception()) { + const vector<t_field*>& fields = ((t_struct*)type)->get_members(); + vector<t_field*>::const_iterator f_iter; + const map<t_const_value*, t_const_value*>& val = value->get_map(); + map<t_const_value*, t_const_value*>::const_iterator v_iter; + bool is_nonrequired_field = false; + for (v_iter = val.begin(); v_iter != val.end(); ++v_iter) { + t_type* field_type = NULL; + is_nonrequired_field = false; + for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) { + if ((*f_iter)->get_name() == v_iter->first->get_string()) { + field_type = (*f_iter)->get_type(); + is_nonrequired_field = (*f_iter)->get_req() != t_field::T_REQUIRED; + } + } + if (field_type == NULL) { + throw "type error: " + type->get_name() + " has no field " + v_iter->first->get_string(); + } + string val = render_const_value(out, name, field_type, v_iter->second); + indent(out) << name << "." << v_iter->first->get_string() << " = " << val << ";" << endl; + if (is_nonrequired_field) { + indent(out) << name << ".__isset." << v_iter->first->get_string() << " = true;" << endl; + } + } + out << endl; + } else if (type->is_map()) { + t_type* ktype = ((t_map*)type)->get_key_type(); + t_type* vtype = ((t_map*)type)->get_val_type(); + const map<t_const_value*, t_const_value*>& val = value->get_map(); + map<t_const_value*, t_const_value*>::const_iterator v_iter; + for (v_iter = val.begin(); v_iter != val.end(); ++v_iter) { + string key = render_const_value(out, name, ktype, v_iter->first); + string val = render_const_value(out, name, vtype, v_iter->second); + indent(out) << name << ".insert(std::make_pair(" << key << ", " << val << "));" << endl; + } + out << endl; + } else if (type->is_list()) { + t_type* etype = ((t_list*)type)->get_elem_type(); + const vector<t_const_value*>& val = value->get_list(); + vector<t_const_value*>::const_iterator v_iter; + for (v_iter = val.begin(); v_iter != val.end(); ++v_iter) { + string val = render_const_value(out, name, etype, *v_iter); + indent(out) << name << ".push_back(" << val << ");" << endl; + } + out << endl; + } else if (type->is_set()) { + t_type* etype = ((t_set*)type)->get_elem_type(); + const vector<t_const_value*>& val = value->get_list(); + vector<t_const_value*>::const_iterator v_iter; + for (v_iter = val.begin(); v_iter != val.end(); ++v_iter) { + string val = render_const_value(out, name, etype, *v_iter); + indent(out) << name << ".insert(" << val << ");" << endl; + } + out << endl; + } else { + throw "INVALID TYPE IN print_const_value: " + type->get_name(); + } +} + +/** + * + */ +string t_cpp_generator::render_const_value(ofstream& out, + string name, + t_type* type, + t_const_value* value) { + (void)name; + std::ostringstream render; + + if (type->is_base_type()) { + t_base_type::t_base tbase = ((t_base_type*)type)->get_base(); + switch (tbase) { + case t_base_type::TYPE_STRING: + render << '"' << get_escaped_string(value) << '"'; + break; + case t_base_type::TYPE_BOOL: + render << ((value->get_integer() > 0) ? "true" : "false"); + break; + case t_base_type::TYPE_BYTE: + case t_base_type::TYPE_I16: + case t_base_type::TYPE_I32: + render << value->get_integer(); + break; + case t_base_type::TYPE_I64: + render << value->get_integer() << "LL"; + break; + case t_base_type::TYPE_DOUBLE: + if (value->get_type() == t_const_value::CV_INTEGER) { + render << value->get_integer(); + } else { + render << value->get_double(); + } + break; + default: + throw "compiler error: no const of base type " + t_base_type::t_base_name(tbase); + } + } else if (type->is_enum()) { + render << "(" << type_name(type) << ")" << value->get_integer(); + } else { + string t = tmp("tmp"); + indent(out) << type_name(type) << " " << t << ";" << endl; + print_const_value(out, t, type, value); + render << t; + } + + return render.str(); +} + +void t_cpp_generator::generate_forward_declaration(t_struct* tstruct) { + // Forward declare struct def + f_types_ << indent() << "class " << tstruct->get_name() << ";" << endl << endl; +} + +/** + * Generates a struct definition for a thrift data type. This is a class + * with data members and a read/write() function, plus a mirroring isset + * inner class. + * + * @param tstruct The struct definition + */ +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); + + std::ofstream& 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); + generate_copy_constructor(f_types_impl_, tstruct, is_exception); + if (gen_moveable_) { + generate_move_constructor(f_types_impl_, tstruct, is_exception); + } + generate_assignment_operator(f_types_impl_, tstruct); + if (gen_moveable_) { + generate_move_assignment_operator(f_types_impl_, tstruct); + } + generate_struct_print_method(f_types_impl_, tstruct); + if (is_exception) { + generate_exception_what_method(f_types_impl_, tstruct); + } +} + +void t_cpp_generator::generate_copy_constructor(ofstream& out, + t_struct* tstruct, + bool is_exception) { + generate_constructor_helper(out, tstruct, is_exception, /*is_move=*/false); +} + +void t_cpp_generator::generate_move_constructor(ofstream& out, + t_struct* tstruct, + bool is_exception) { + generate_constructor_helper(out, tstruct, is_exception, /*is_move=*/true); +} + +namespace { +// Helper to convert a variable to rvalue, if move is enabled +std::string maybeMove(std::string const& other, bool move) { + if (move) { + return "std::move(" + other + ")"; + } + return other; +} +} + +void t_cpp_generator::generate_constructor_helper(ofstream& out, + t_struct* tstruct, + bool is_exception, + bool is_move) { + + std::string tmp_name = tmp("other"); + + indent(out) << tstruct->get_name() << "::" << tstruct->get_name(); + + if (is_move) { + out << "( " << tstruct->get_name() << "&& "; + } else { + out << "(const " << tstruct->get_name() << "& "; + } + out << tmp_name << ") "; + if (is_exception) + out << ": TException() "; + out << "{" << endl; + indent_up(); + + const vector<t_field*>& members = tstruct->get_members(); + + // eliminate compiler unused warning + if (members.empty()) + indent(out) << "(void) " << tmp_name << ";" << endl; + + vector<t_field*>::const_iterator f_iter; + bool has_nonrequired_fields = false; + for (f_iter = members.begin(); f_iter != members.end(); ++f_iter) { + 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; + } + + if (has_nonrequired_fields) { + indent(out) << "__isset = " << maybeMove(tmp_name + ".__isset", is_move) << ";" << endl; + } + + indent_down(); + indent(out) << "}" << endl; +} + +void t_cpp_generator::generate_assignment_operator(ofstream& out, t_struct* tstruct) { + generate_assignment_helper(out, tstruct, /*is_move=*/false); +} + +void t_cpp_generator::generate_move_assignment_operator(ofstream& out, t_struct* tstruct) { + generate_assignment_helper(out, tstruct, /*is_move=*/true); +} + +void t_cpp_generator::generate_assignment_helper(ofstream& out, t_struct* tstruct, bool is_move) { + std::string tmp_name = tmp("other"); + + indent(out) << tstruct->get_name() << "& " << tstruct->get_name() << "::operator=("; + + if (is_move) { + out << tstruct->get_name() << "&& "; + } else { + out << "const " << tstruct->get_name() << "& "; + } + out << tmp_name << ") {" << endl; + + indent_up(); + + const vector<t_field*>& members = tstruct->get_members(); + + // eliminate compiler unused warning + if (members.empty()) + indent(out) << "(void) " << tmp_name << ";" << endl; + + vector<t_field*>::const_iterator f_iter; + bool has_nonrequired_fields = false; + for (f_iter = members.begin(); f_iter != members.end(); ++f_iter) { + 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; + } + if (has_nonrequired_fields) { + indent(out) << "__isset = " << maybeMove(tmp_name + ".__isset", is_move) << ";" << endl; + } + + indent(out) << "return *this;" << endl; + indent_down(); + indent(out) << "}" << endl; +} + +/** + * Writes the struct declaration into the header file + * + * @param out Output stream + * @param tstruct The struct + */ +void t_cpp_generator::generate_struct_declaration(ofstream& out, + t_struct* tstruct, + bool is_exception, + bool pointers, + bool read, + bool write, + bool swap, + bool stream) { + string extends = ""; + if (is_exception) { + extends = " : public ::apache::thrift::TException"; + } + + // Get members + vector<t_field*>::const_iterator m_iter; + const vector<t_field*>& members = tstruct->get_members(); + + // Write the isset structure declaration outside the class. This makes + // the generated code amenable to processing by SWIG. + // We only declare the struct if it gets used in the class. + + // Isset struct has boolean fields, but only for non-required fields. + bool has_nonrequired_fields = false; + for (m_iter = members.begin(); m_iter != members.end(); ++m_iter) { + if ((*m_iter)->get_req() != t_field::T_REQUIRED) + has_nonrequired_fields = true; + } + + if (has_nonrequired_fields && (!pointers || read)) { + + out << indent() << "typedef struct _" << tstruct->get_name() << "__isset {" << endl; + indent_up(); + + indent(out) << "_" << tstruct->get_name() << "__isset() "; + bool first = true; + for (m_iter = members.begin(); m_iter != members.end(); ++m_iter) { + if ((*m_iter)->get_req() == t_field::T_REQUIRED) { + continue; + } + string isSet = ((*m_iter)->get_value() != NULL) ? "true" : "false"; + if (first) { + first = false; + out << ": " << (*m_iter)->get_name() << "(" << isSet << ")"; + } else { + out << ", " << (*m_iter)->get_name() << "(" << isSet << ")"; + } + } + out << " {}" << endl; + + for (m_iter = members.begin(); m_iter != members.end(); ++m_iter) { + if ((*m_iter)->get_req() != t_field::T_REQUIRED) { + indent(out) << "bool " << (*m_iter)->get_name() << " :1;" << endl; + } + } + + indent_down(); + indent(out) << "} _" << tstruct->get_name() << "__isset;" << endl; + } + + out << endl; + + // Open struct def + out << indent() << "class " << tstruct->get_name() << extends << " {" << endl << indent() + << " public:" << endl << endl; + indent_up(); + + if (!pointers) { + // Copy constructor + indent(out) << tstruct->get_name() << "(const " << tstruct->get_name() << "&);" << endl; + + // Move constructor + if (gen_moveable_) { + indent(out) << tstruct->get_name() << "(" << tstruct->get_name() << "&&);" << endl; + } + + // Assignment Operator + indent(out) << tstruct->get_name() << "& operator=(const " << tstruct->get_name() << "&);" + << endl; + + // Move assignment operator + if (gen_moveable_) { + indent(out) << tstruct->get_name() << "& operator=(" << tstruct->get_name() << "&&);" << endl; + } + + // Default constructor + indent(out) << tstruct->get_name() << "()"; + + bool init_ctor = false; + + 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 != NULL) { + dval = render_const_value(out, (*m_iter)->get_name(), t, cv); + } + if (!init_ctor) { + init_ctor = true; + out << " : "; + out << (*m_iter)->get_name() << "(" << dval << ")"; + } else { + 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_const_value* cv = (*m_iter)->get_value(); + if (cv != NULL) { + print_const_value(out, (*m_iter)->get_name(), t, cv); + } + } + } + scope_down(out); + } + + if (tstruct->annotations_.find("final") == tstruct->annotations_.end()) { + out << endl << indent() << "virtual ~" << tstruct->get_name() << "() throw();" << endl; + } + + // Declare all fields + for (m_iter = members.begin(); m_iter != members.end(); ++m_iter) { + indent(out) << declare_field(*m_iter, + false, + (pointers && !(*m_iter)->get_type()->is_xception()), + !read) << endl; + } + + // Add the __isset data member if we need it, using the definition from above + if (has_nonrequired_fields && (!pointers || read)) { + out << endl << indent() << "_" << tstruct->get_name() << "__isset __isset;" << endl; + } + + // Create a setter function for each field + for (m_iter = members.begin(); m_iter != members.end(); ++m_iter) { + if (pointers) { + continue; + } + if (is_reference((*m_iter))) { + out << endl << indent() << "void __set_" << (*m_iter)->get_name() << "(boost::shared_ptr<" + << type_name((*m_iter)->get_type(), false, false) << ">"; + out << " val);" << endl; + } else { + out << endl << indent() << "void __set_" << (*m_iter)->get_name() << "(" + << type_name((*m_iter)->get_type(), false, true); + out << " val);" << endl; + } + } + out << endl; + + 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. + 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); + out << indent() << "bool operator != (const " << tstruct->get_name() << " &rhs) const {" + << endl << indent() << " return !(*this == rhs);" << endl << indent() << "}" << endl + << endl; + + // Generate the declaration of a less-than operator. This must be + // implemented by the application developer if they wish to use it. (They + // will get a link error if they try to use it without an implementation.) + out << indent() << "bool operator < (const " << tstruct->get_name() << " & ) const;" << endl + << endl; + } + } + + if (read) { + if (gen_templates_) { + out << indent() << "template <class Protocol_>" << endl << indent() + << "uint32_t read(Protocol_* iprot);" << endl; + } else { + out << indent() << "uint32_t read(" + << "::apache::thrift::protocol::TProtocol* iprot);" << endl; + } + } + if (write) { + if (gen_templates_) { + out << indent() << "template <class Protocol_>" << endl << indent() + << "uint32_t write(Protocol_* oprot) const;" << endl; + } else { + out << indent() << "uint32_t write(" + << "::apache::thrift::protocol::TProtocol* oprot) const;" << endl; + } + } + out << endl; + + if (stream) { + out << indent() << "virtual "; + generate_struct_print_method_decl(out, NULL); + out << ";" << endl; + } + + // std::exception::what() + if (is_exception) { + out << indent() << "mutable std::string thriftTExceptionMessageHolder_;" << endl; + out << indent(); + generate_exception_what_method_decl(out, tstruct, false); + out << ";" << endl; + } + + indent_down(); + indent(out) << "};" << endl << endl; + + if (swap) { + // Generate a namespace-scope swap() function + out << indent() << "void swap(" << tstruct->get_name() << " &a, " << tstruct->get_name() + << " &b);" << endl << endl; + } + + if (stream) { + generate_struct_ostream_operator(out, tstruct); + } +} + +void t_cpp_generator::generate_struct_definition(ofstream& out, + ofstream& force_cpp_out, + t_struct* tstruct, + bool setters) { + // Get members + vector<t_field*>::const_iterator m_iter; + const vector<t_field*>& members = tstruct->get_members(); + + // Destructor + if (tstruct->annotations_.find("final") == tstruct->annotations_.end()) { + force_cpp_out << endl << indent() << tstruct->get_name() << "::~" << tstruct->get_name() + << "() throw() {" << endl; + indent_up(); + + indent_down(); + force_cpp_out << indent() << "}" << endl << endl; + } + + // Create a setter function for each field + if (setters) { + for (m_iter = members.begin(); m_iter != members.end(); ++m_iter) { + if (is_reference((*m_iter))) { + std::string type = type_name((*m_iter)->get_type()); + out << endl << indent() << "void " << tstruct->get_name() << "::__set_" + << (*m_iter)->get_name() << "(boost::shared_ptr<" + << type_name((*m_iter)->get_type(), false, false) << ">"; + out << " val) {" << endl; + } else { + out << endl << indent() << "void " << tstruct->get_name() << "::__set_" + << (*m_iter)->get_name() << "(" << type_name((*m_iter)->get_type(), false, true); + out << " val) {" << endl; + } + indent_up(); + out << indent() << "this->" << (*m_iter)->get_name() << " = val;" << endl; + indent_down(); + + // assume all fields are required except optional fields. + // for optional fields change __isset.name to true + bool is_optional = (*m_iter)->get_req() == t_field::T_OPTIONAL; + if (is_optional) { + out << indent() << indent() << "__isset." << (*m_iter)->get_name() << " = true;" << endl; + } + out << indent() << "}" << endl; + } + } + out << endl; +} + +/** + * Makes a helper function to gen a struct reader. + * + * @param out Stream to write to + * @param tstruct The struct + */ +void t_cpp_generator::generate_struct_reader(ofstream& out, t_struct* tstruct, bool pointers) { + if (gen_templates_) { + out << indent() << "template <class Protocol_>" << endl << indent() << "uint32_t " + << tstruct->get_name() << "::read(Protocol_* iprot) {" << endl; + } else { + indent(out) << "uint32_t " << tstruct->get_name() + << "::read(::apache::thrift::protocol::TProtocol* iprot) {" << endl; + } + indent_up(); + + const vector<t_field*>& fields = tstruct->get_members(); + vector<t_field*>::const_iterator f_iter; + + // Declare stack tmp variables + out << endl + << indent() << "apache::thrift::protocol::TInputRecursionTracker tracker(*iprot);" << endl + << indent() << "uint32_t xfer = 0;" << endl + << indent() << "std::string fname;" << endl + << indent() << "::apache::thrift::protocol::TType ftype;" << endl + << indent() << "int16_t fid;" << endl + << endl + << indent() << "xfer += iprot->readStructBegin(fname);" << endl + << endl + << indent() << "using ::apache::thrift::protocol::TProtocolException;" << endl + << endl; + + // Required variables aren't in __isset, so we need tmp vars to check them. + for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) { + if ((*f_iter)->get_req() == t_field::T_REQUIRED) + indent(out) << "bool isset_" << (*f_iter)->get_name() << " = false;" << endl; + } + out << endl; + + // Loop over reading in fields + indent(out) << "while (true)" << endl; + scope_up(out); + + // Read beginning field marker + indent(out) << "xfer += iprot->readFieldBegin(fname, ftype, fid);" << endl; + + // Check for field STOP marker + out << indent() << "if (ftype == ::apache::thrift::protocol::T_STOP) {" << endl << indent() + << " break;" << endl << indent() << "}" << endl; + + if (fields.empty()) { + out << indent() << "xfer += iprot->skip(ftype);" << endl; + } else { + // Switch statement on the field we are reading + indent(out) << "switch (fid)" << endl; + + scope_up(out); + + // Generate deserialization code for known cases + for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) { + indent(out) << "case " << (*f_iter)->get_key() << ":" << endl; + indent_up(); + indent(out) << "if (ftype == " << type_to_enum((*f_iter)->get_type()) << ") {" << endl; + indent_up(); + + const char* isset_prefix = ((*f_iter)->get_req() != t_field::T_REQUIRED) ? "this->__isset." + : "isset_"; + +#if 0 + // This code throws an exception if the same field is encountered twice. + // We've decided to leave it out for performance reasons. + // TODO(dreiss): Generate this code and "if" it out to make it easier + // for people recompiling thrift to include it. + out << + indent() << "if (" << isset_prefix << (*f_iter)->get_name() << ")" << endl << + indent() << " throw TProtocolException(TProtocolException::INVALID_DATA);" << endl; +#endif + + if (pointers && !(*f_iter)->get_type()->is_xception()) { + generate_deserialize_field(out, *f_iter, "(*(this->", "))"); + } else { + generate_deserialize_field(out, *f_iter, "this->"); + } + out << indent() << isset_prefix << (*f_iter)->get_name() << " = true;" << endl; + indent_down(); + out << indent() << "} else {" << endl << indent() << " xfer += iprot->skip(ftype);" << endl + << + // TODO(dreiss): Make this an option when thrift structs + // have a common base class. + // indent() << " throw TProtocolException(TProtocolException::INVALID_DATA);" << endl << + indent() << "}" << endl << indent() << "break;" << endl; + indent_down(); + } + + // In the default case we skip the field + out << indent() << "default:" << endl << indent() << " xfer += iprot->skip(ftype);" << endl + << indent() << " break;" << endl; + + scope_down(out); + } //!fields.empty() + // Read field end marker + indent(out) << "xfer += iprot->readFieldEnd();" << endl; + + scope_down(out); + + out << endl << indent() << "xfer += iprot->readStructEnd();" << endl; + + // Throw if any required fields are missing. + // We do this after reading the struct end so that + // there might possibly be a chance of continuing. + out << endl; + for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) { + if ((*f_iter)->get_req() == t_field::T_REQUIRED) + out << indent() << "if (!isset_" << (*f_iter)->get_name() << ')' << endl << indent() + << " throw TProtocolException(TProtocolException::INVALID_DATA);" << endl; + } + + indent(out) << "return xfer;" << endl; + + indent_down(); + indent(out) << "}" << endl << endl; +} + +/** + * Generates the write function. + * + * @param out Stream to write to + * @param tstruct The struct + */ +void t_cpp_generator::generate_struct_writer(ofstream& out, t_struct* tstruct, bool pointers) { + string name = tstruct->get_name(); + const vector<t_field*>& fields = tstruct->get_sorted_members(); + vector<t_field*>::const_iterator f_iter; + + if (gen_templates_) { + out << indent() << "template <class Protocol_>" << endl << indent() << "uint32_t " + << tstruct->get_name() << "::write(Protocol_* oprot) const {" << endl; + } else { + indent(out) << "uint32_t " << tstruct->get_name() + << "::write(::apache::thrift::protocol::TProtocol* oprot) const {" << endl; + } + indent_up(); + + out << indent() << "uint32_t xfer = 0;" << endl; + + indent(out) << "apache::thrift::protocol::TOutputRecursionTracker tracker(*oprot);" << endl; + indent(out) << "xfer += oprot->writeStructBegin(\"" << name << "\");" << endl; + + for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) { + bool check_if_set = (*f_iter)->get_req() == t_field::T_OPTIONAL + || (*f_iter)->get_type()->is_xception(); + if (check_if_set) { + out << endl << indent() << "if (this->__isset." << (*f_iter)->get_name() << ") {" << endl; + indent_up(); + } else { + out << endl; + } + + // Write field header + out << indent() << "xfer += oprot->writeFieldBegin(" + << "\"" << (*f_iter)->get_name() << "\", " << type_to_enum((*f_iter)->get_type()) << ", " + << (*f_iter)->get_key() << ");" << endl; + // Write field contents + if (pointers && !(*f_iter)->get_type()->is_xception()) { + generate_serialize_field(out, *f_iter, "(*(this->", "))"); + } else { + generate_serialize_field(out, *f_iter, "this->"); + } + // Write field closer + indent(out) << "xfer += oprot->writeFieldEnd();" << endl; + if (check_if_set) { + indent_down(); + indent(out) << '}'; + } + } + + out << endl; + + // Write the struct map + out << indent() << "xfer += oprot->writeFieldStop();" << endl << indent() + << "xfer += oprot->writeStructEnd();" << endl << indent() + << "return xfer;" << endl; + + indent_down(); + indent(out) << "}" << endl << endl; +} + +/** + * Struct writer for result of a function, which can have only one of its + * fields set and does a conditional if else look up into the __isset field + * of the struct. + * + * @param out Output stream + * @param tstruct The result struct + */ +void t_cpp_generator::generate_struct_result_writer(ofstream& out, + t_struct* tstruct, + bool pointers) { + string name = tstruct->get_name(); + const vector<t_field*>& fields = tstruct->get_sorted_members(); + vector<t_field*>::const_iterator f_iter; + + if (gen_templates_) { + out << indent() << "template <class Protocol_>" << endl << indent() << "uint32_t " + << tstruct->get_name() << "::write(Protocol_* oprot) const {" << endl; + } else { + indent(out) << "uint32_t " << tstruct->get_name() + << "::write(::apache::thrift::protocol::TProtocol* oprot) const {" << endl; + } + indent_up(); + + out << endl << indent() << "uint32_t xfer = 0;" << endl << endl; + + indent(out) << "xfer += oprot->writeStructBegin(\"" << name << "\");" << endl; + + bool first = true; + for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) { + if (first) { + first = false; + out << endl << indent() << "if "; + } else { + out << " else if "; + } + + out << "(this->__isset." << (*f_iter)->get_name() << ") {" << endl; + + indent_up(); + + // Write field header + out << indent() << "xfer += oprot->writeFieldBegin(" + << "\"" << (*f_iter)->get_name() << "\", " << type_to_enum((*f_iter)->get_type()) << ", " + << (*f_iter)->get_key() << ");" << endl; + // Write field contents + if (pointers) { + generate_serialize_field(out, *f_iter, "(*(this->", "))"); + } else { + generate_serialize_field(out, *f_iter, "this->"); + } + // Write field closer + indent(out) << "xfer += oprot->writeFieldEnd();" << endl; + + indent_down(); + indent(out) << "}"; + } + + // Write the struct map + out << endl << indent() << "xfer += oprot->writeFieldStop();" << endl << indent() + << "xfer += oprot->writeStructEnd();" << endl << indent() << "return xfer;" << endl; + + indent_down(); + indent(out) << "}" << endl << endl; +} + +/** + * Generates the swap function. + * + * @param out Stream to write to + * @param tstruct The struct + */ +void t_cpp_generator::generate_struct_swap(ofstream& out, t_struct* tstruct) { + out << indent() << "void swap(" << tstruct->get_name() << " &a, " << tstruct->get_name() + << " &b) {" << endl; + indent_up(); + + // Let argument-dependent name lookup find the correct swap() function to + // use based on the argument types. If none is found in the arguments' + // namespaces, fall back to ::std::swap(). + out << indent() << "using ::std::swap;" << endl; + + bool has_nonrequired_fields = false; + const vector<t_field*>& fields = tstruct->get_members(); + for (vector<t_field*>::const_iterator f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) { + t_field* tfield = *f_iter; + + if (tfield->get_req() != t_field::T_REQUIRED) { + has_nonrequired_fields = true; + } + + out << indent() << "swap(a." << tfield->get_name() << ", b." << tfield->get_name() << ");" + << endl; + } + + if (has_nonrequired_fields) { + out << indent() << "swap(a.__isset, b.__isset);" << endl; + } + + // handle empty structs + if (fields.size() == 0) { + out << indent() << "(void) a;" << endl; + out << indent() << "(void) b;" << endl; + } + + scope_down(out); + out << endl; +} + +void t_cpp_generator::generate_struct_ostream_operator(std::ofstream& out, t_struct* tstruct) { + out << "inline std::ostream& operator<<(std::ostream& out, const " + << tstruct->get_name() + << "& obj)" << endl; + scope_up(out); + out << indent() << "obj.printTo(out);" << endl + << indent() << "return out;" << endl; + scope_down(out); + out << endl; +} + +void t_cpp_generator::generate_struct_print_method_decl(std::ofstream& out, t_struct* tstruct) { + out << "void "; + if (tstruct) { + out << tstruct->get_name() << "::"; + } + out << "printTo(std::ostream& out) const"; +} + +void t_cpp_generator::generate_exception_what_method_decl(std::ofstream& out, + t_struct* tstruct, + bool external) { + out << "const char* "; + if (external) { + out << tstruct->get_name() << "::"; + } + out << "what() const throw()"; +} + +namespace struct_ostream_operator_generator { +void generate_required_field_value(std::ofstream& out, const t_field* field) { + out << " << to_string(" << field->get_name() << ")"; +} + +void generate_optional_field_value(std::ofstream& out, const t_field* field) { + out << "; (__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_print_method(std::ofstream& out, t_struct* tstruct) { + out << indent(); + generate_struct_print_method_decl(out, tstruct); + out << " {" << endl; + + indent_up(); + + out << indent() << "using ::apache::thrift::to_string;" << endl; + out << indent() << "out << \"" << tstruct->get_name() << "(\";" << endl; + struct_ostream_operator_generator::generate_fields(out, tstruct->get_members(), indent()); + out << indent() << "out << \")\";" << endl; + + indent_down(); + out << "}" << endl << endl; +} + +/** + * Generates what() method for exceptions + */ +void t_cpp_generator::generate_exception_what_method(std::ofstream& out, t_struct* tstruct) { + out << indent(); + generate_exception_what_method_decl(out, tstruct, true); + out << " {" << endl; + + indent_up(); + out << indent() << "try {" << endl; + + indent_up(); + out << indent() << "std::stringstream ss;" << endl; + out << indent() << "ss << \"TException - service has thrown: \" << *this;" << endl; + out << indent() << "this->thriftTExceptionMessageHolder_ = ss.str();" << endl; + out << indent() << "return this->thriftTExceptionMessageHolder_.c_str();" << endl; + indent_down(); + + out << indent() << "} catch (const std::exception&) {" << endl; + + indent_up(); + out << indent() << "return \"TException - service has thrown: " << tstruct->get_name() << "\";" + << endl; + indent_down(); + + out << indent() << "}" << 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 + * the data types defined in the main header file, and the implementation + * file contains implementations of the basic printer and default interfaces. + * + * @param tservice The service definition + */ +void t_cpp_generator::generate_service(t_service* tservice) { + string svcname = tservice->get_name(); + + // Make output files + string f_header_name = get_out_dir() + svcname + ".h"; + f_header_.open(f_header_name.c_str()); + + // Print header file includes + f_header_ << autogen_comment(); + f_header_ << "#ifndef " << svcname << "_H" << endl << "#define " << svcname << "_H" << endl + << endl; + if (gen_cob_style_) { + f_header_ << "#include <thrift/transport/TBufferTransports.h>" << endl << // TMemoryBuffer + "#include <thrift/cxxfunctional.h>" << endl + << "namespace apache { namespace thrift { namespace async {" << endl + << "class TAsyncChannel;" << endl << "}}}" << endl; + } + f_header_ << "#include <thrift/TDispatchProcessor.h>" << endl; + if (gen_cob_style_) { + f_header_ << "#include <thrift/async/TAsyncDispatchProcessor.h>" << endl; + } + f_header_ << "#include <thrift/async/TConcurrentClientSyncInfo.h>" << endl; + f_header_ << "#include \"" << get_include_prefix(*get_program()) << program_name_ << "_types.h\"" + << endl; + + t_service* extends_service = tservice->get_extends(); + if (extends_service != NULL) { + f_header_ << "#include \"" << get_include_prefix(*(extends_service->get_program())) + << extends_service->get_name() << ".h\"" << endl; + } + + f_header_ << endl << ns_open_ << endl << endl; + + f_header_ << + "#ifdef _WIN32\n" + " #pragma warning( push )\n" + " #pragma warning (disable : 4250 ) //inheriting methods via dominance \n" + "#endif\n\n"; + + // Service implementation file includes + string f_service_name = get_out_dir() + svcname + ".cpp"; + f_service_.open(f_service_name.c_str()); + f_service_ << autogen_comment(); + f_service_ << "#include \"" << get_include_prefix(*get_program()) << svcname << ".h\"" << endl; + if (gen_cob_style_) { + f_service_ << "#include \"thrift/async/TAsyncChannel.h\"" << endl; + } + if (gen_templates_) { + f_service_ << "#include \"" << get_include_prefix(*get_program()) << svcname << ".tcc\"" + << endl; + + string f_service_tcc_name = get_out_dir() + svcname + ".tcc"; + f_service_tcc_.open(f_service_tcc_name.c_str()); + f_service_tcc_ << autogen_comment(); + f_service_tcc_ << "#include \"" << get_include_prefix(*get_program()) << svcname << ".h\"" + << endl; + + f_service_tcc_ << "#ifndef " << svcname << "_TCC" << endl << "#define " << svcname << "_TCC" + << endl << endl; + + if (gen_cob_style_) { + f_service_tcc_ << "#include \"thrift/async/TAsyncChannel.h\"" << endl; + } + } + + f_service_ << endl << ns_open_ << endl << endl; + f_service_tcc_ << endl << ns_open_ << endl << endl; + + // Generate all the components + generate_service_interface(tservice, ""); + generate_service_interface_factory(tservice, ""); + generate_service_null(tservice, ""); + generate_service_helpers(tservice); + generate_service_client(tservice, ""); + generate_service_processor(tservice, ""); + generate_service_multiface(tservice); + generate_service_skeleton(tservice); + generate_service_client(tservice, "Concurrent"); + + // Generate all the cob components + if (gen_cob_style_) { + generate_service_interface(tservice, "CobCl"); + generate_service_interface(tservice, "CobSv"); + generate_service_interface_factory(tservice, "CobSv"); + generate_service_null(tservice, "CobSv"); + generate_service_client(tservice, "Cob"); + generate_service_processor(tservice, "Cob"); + generate_service_async_skeleton(tservice); + } + + f_header_ << + "#ifdef _WIN32\n" + " #pragma warning( pop )\n" + "#endif\n\n"; + + // Close the namespace + f_service_ << ns_close_ << endl << endl; + f_service_tcc_ << ns_close_ << endl << endl; + f_header_ << ns_close_ << endl << endl; + + // TODO(simpkins): Make this a separate option + if (gen_templates_) { + f_header_ << "#include \"" << get_include_prefix(*get_program()) << svcname << ".tcc\"" << endl + << "#include \"" << get_include_prefix(*get_program()) << program_name_ + << "_types.tcc\"" << endl << endl; + } + + f_header_ << "#endif" << endl; + f_service_tcc_ << "#endif" << endl; + + // Close the files + f_service_tcc_.close(); + f_service_.close(); + f_header_.close(); +} + +/** + * Generates helper functions for a service. Basically, this generates types + * for all the arguments and results to functions. + * + * @param tservice The service to generate a header definition for + */ +void t_cpp_generator::generate_service_helpers(t_service* tservice) { + vector<t_function*> functions = tservice->get_functions(); + vector<t_function*>::iterator f_iter; + std::ofstream& out = (gen_templates_ ? f_service_tcc_ : f_service_); + + for (f_iter = functions.begin(); f_iter != functions.end(); ++f_iter) { + t_struct* ts = (*f_iter)->get_arglist(); + string name_orig = ts->get_name(); + + // TODO(dreiss): Why is this stuff not in generate_function_helpers? + ts->set_name(tservice->get_name() + "_" + (*f_iter)->get_name() + "_args"); + generate_struct_declaration(f_header_, ts, false); + 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_writer(out, ts, true); + ts->set_name(name_orig); + + generate_function_helpers(tservice, *f_iter); + } +} + +/** + * Generates a service interface definition. + * + * @param tservice The service to generate a header definition for + */ +void t_cpp_generator::generate_service_interface(t_service* tservice, string style) { + + string service_if_name = service_name_ + style + "If"; + if (style == "CobCl") { + // Forward declare the client. + string client_name = service_name_ + "CobClient"; + if (gen_templates_) { + client_name += "T"; + service_if_name += "T"; + indent(f_header_) << "template <class Protocol_>" << endl; + } + indent(f_header_) << "class " << client_name << ";" << endl << endl; + } + + string extends = ""; + if (tservice->get_extends() != NULL) { + extends = " : virtual public " + type_name(tservice->get_extends()) + style + "If"; + if (style == "CobCl" && gen_templates_) { + // TODO(simpkins): If gen_templates_ is enabled, we currently assume all + // parent services were also generated with templates enabled. + extends += "T<Protocol_>"; + } + } + + if (style == "CobCl" && gen_templates_) { + f_header_ << "template <class Protocol_>" << endl; + } + f_header_ << "class " << service_if_name << extends << " {" << endl << " public:" << endl; + indent_up(); + f_header_ << indent() << "virtual ~" << service_if_name << "() {}" << endl; + + vector<t_function*> functions = tservice->get_functions(); + vector<t_function*>::iterator f_iter; + for (f_iter = functions.begin(); f_iter != functions.end(); ++f_iter) { + if ((*f_iter)->has_doc()) + f_header_ << endl; + generate_java_doc(f_header_, *f_iter); + f_header_ << indent() << "virtual " << function_signature(*f_iter, style) << " = 0;" << endl; + } + indent_down(); + f_header_ << "};" << endl << endl; + + if (style == "CobCl" && gen_templates_) { + // generate a backwards-compatible typedef for clients that do not + // know about the new template-style code + f_header_ << "typedef " << service_if_name << "< ::apache::thrift::protocol::TProtocol> " + << service_name_ << style << "If;" << endl << endl; + } +} + +/** + * Generates a service interface factory. + * + * @param tservice The service to generate an interface factory for. + */ +void t_cpp_generator::generate_service_interface_factory(t_service* tservice, string style) { + string service_if_name = service_name_ + style + "If"; + + // Figure out the name of the upper-most parent class. + // Getting everything to work out properly with inheritance is annoying. + // Here's what we're doing for now: + // + // - All handlers implement getHandler(), but subclasses use covariant return + // types to return their specific service interface class type. We have to + // use raw pointers because of this; shared_ptr<> can't be used for + // covariant return types. + // + // - Since we're not using shared_ptr<>, we also provide a releaseHandler() + // function that must be called to release a pointer to a handler obtained + // via getHandler(). + // + // releaseHandler() always accepts a pointer to the upper-most parent class + // type. This is necessary since the parent versions of releaseHandler() + // may accept any of the parent types, not just the most specific subclass + // type. Implementations can use dynamic_cast to cast the pointer to the + // subclass type if desired. + t_service* base_service = tservice; + while (base_service->get_extends() != NULL) { + base_service = base_service->get_extends(); + } + string base_if_name = type_name(base_service) + style + "If"; + + // Generate the abstract factory class + string factory_name = service_if_name + "Factory"; + string extends; + if (tservice->get_extends() != NULL) { + extends = " : virtual public " + type_name(tservice->get_extends()) + style + "IfFactory"; + } + + f_header_ << "class " << factory_name << extends << " {" << endl << " public:" << endl; + indent_up(); + 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; + + indent_down(); + f_header_ << "};" << endl << endl; + + // Generate the singleton factory class + string singleton_factory_name = service_if_name + "SingletonFactory"; + f_header_ << "class " << singleton_factory_name << " : virtual public " << factory_name << " {" + << endl << " public:" << endl; + indent_up(); + f_header_ << indent() << singleton_factory_name << "(const boost::shared_ptr<" << service_if_name + << ">& iface) : iface_(iface) {}" << endl << indent() << "virtual ~" + << singleton_factory_name << "() {}" << endl << endl << indent() << "virtual " + << service_if_name << "* getHandler(" + << "const ::apache::thrift::TConnectionInfo&) {" << endl << indent() + << " return iface_.get();" << endl << indent() << "}" << endl << indent() + << "virtual void releaseHandler(" << base_if_name << "* /* handler */) {}" << endl; + + f_header_ << endl << " protected:" << endl << indent() << "boost::shared_ptr<" << service_if_name + << "> iface_;" << endl; + + indent_down(); + f_header_ << "};" << endl << endl; +} + +/** + * Generates a null implementation of the service. + * + * @param tservice The service to generate a header definition for + */ +void t_cpp_generator::generate_service_null(t_service* tservice, string style) { + string extends = ""; + if (tservice->get_extends() != NULL) { + extends = " , virtual public " + type_name(tservice->get_extends()) + style + "Null"; + } + f_header_ << "class " << service_name_ << style << "Null : virtual public " << service_name_ + << style << "If" << extends << " {" << endl << " public:" << endl; + indent_up(); + f_header_ << indent() << "virtual ~" << service_name_ << style << "Null() {}" << endl; + 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; + indent_up(); + + t_type* returntype = (*f_iter)->get_returntype(); + t_field returnfield(returntype, "_return"); + + if (style == "") { + if (returntype->is_void() || is_complex_type(returntype)) { + f_header_ << indent() << "return;" << endl; + } else { + f_header_ << indent() << declare_field(&returnfield, true) << endl << indent() + << "return _return;" << endl; + } + } else if (style == "CobSv") { + if (returntype->is_void()) { + f_header_ << indent() << "return cob();" << endl; + } else { + t_field returnfield(returntype, "_return"); + f_header_ << indent() << declare_field(&returnfield, true) << endl << indent() + << "return cob(_return);" << endl; + } + + } else { + throw "UNKNOWN STYLE"; + } + + indent_down(); + f_header_ << indent() << "}" << endl; + } + indent_down(); + f_header_ << "};" << endl << endl; +} + +void t_cpp_generator::generate_function_call(ostream& out, + t_function* tfunction, + string target, + string iface, + string arg_prefix) { + bool first = true; + t_type* ret_type = get_true_type(tfunction->get_returntype()); + out << indent(); + if (!tfunction->is_oneway() && !ret_type->is_void()) { + if (is_complex_type(ret_type)) { + first = false; + out << iface << "->" << tfunction->get_name() << "(" << target; + } else { + out << target << " = " << iface << "->" << tfunction->get_name() << "("; + } + } else { + out << iface << "->" << tfunction->get_name() << "("; + } + const std::vector<t_field*>& fields = tfunction->get_arglist()->get_members(); + vector<t_field*>::const_iterator f_iter; + for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) { + if (first) { + first = false; + } else { + out << ", "; + } + out << arg_prefix << (*f_iter)->get_name(); + } + out << ");" << endl; +} + +void t_cpp_generator::generate_service_async_skeleton(t_service* tservice) { + string svcname = tservice->get_name(); + + // Service implementation file includes + string f_skeleton_name = get_out_dir() + svcname + "_async_server.skeleton.cpp"; + + string ns = namespace_prefix(tservice->get_program()->get_namespace("cpp")); + + ofstream f_skeleton; + f_skeleton.open(f_skeleton_name.c_str()); + f_skeleton << "// This autogenerated skeleton file illustrates one way to adapt a synchronous" + << endl << "// interface into an asynchronous interface. You should copy it to another" + << endl + << "// filename to avoid overwriting it and rewrite as asynchronous any functions" + << endl << "// that would otherwise introduce unwanted latency." << endl << endl + << "#include \"" << get_include_prefix(*get_program()) << svcname << ".h\"" << endl + << "#include <thrift/protocol/TBinaryProtocol.h>" << endl << endl + << "using namespace ::apache::thrift;" << endl + << "using namespace ::apache::thrift::protocol;" << endl + << "using namespace ::apache::thrift::transport;" << endl + << "using namespace ::apache::thrift::async;" << endl << endl + << "using boost::shared_ptr;" << endl << endl; + + // the following code would not compile: + // using namespace ; + // using namespace ::; + if ((!ns.empty()) && (ns.compare(" ::") != 0)) { + f_skeleton << "using namespace " << string(ns, 0, ns.size() - 2) << ";" << endl << endl; + } + + f_skeleton << "class " << svcname << "AsyncHandler : " + << "public " << svcname << "CobSvIf {" << endl << " public:" << endl; + indent_up(); + f_skeleton << indent() << svcname << "AsyncHandler() {" << endl << indent() + << " syncHandler_ = std::auto_ptr<" << svcname << "Handler>(new " << svcname + << "Handler);" << endl << indent() << " // Your initialization goes here" << endl + << indent() << "}" << endl; + f_skeleton << indent() << "virtual ~" << service_name_ << "AsyncHandler();" << endl; + + 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_skeleton << endl << indent() << function_signature(*f_iter, "CobSv", "", true) << " {" + << endl; + indent_up(); + + t_type* returntype = (*f_iter)->get_returntype(); + t_field returnfield(returntype, "_return"); + + string target = returntype->is_void() ? "" : "_return"; + if (!returntype->is_void()) { + f_skeleton << indent() << declare_field(&returnfield, true) << endl; + } + generate_function_call(f_skeleton, *f_iter, target, "syncHandler_", ""); + f_skeleton << indent() << "return cob(" << target << ");" << endl; + + scope_down(f_skeleton); + } + f_skeleton << endl << " protected:" << endl << indent() << "std::auto_ptr<" << svcname + << "Handler> syncHandler_;" << endl; + indent_down(); + f_skeleton << "};" << endl << endl; +} + +/** + * Generates a multiface, which is a single server that just takes a set + * of objects implementing the interface and calls them all, returning the + * value of the last one to be called. + * + * @param tservice The service to generate a multiserver for. + */ +void t_cpp_generator::generate_service_multiface(t_service* tservice) { + // Generate the dispatch methods + vector<t_function*> functions = tservice->get_functions(); + vector<t_function*>::iterator f_iter; + + string extends = ""; + string extends_multiface = ""; + if (tservice->get_extends() != NULL) { + extends = type_name(tservice->get_extends()); + extends_multiface = ", public " + extends + "Multiface"; + } + + string list_type = string("std::vector<boost::shared_ptr<") + service_name_ + "If> >"; + + // Generate the header portion + f_header_ << "class " << service_name_ << "Multiface : " + << "virtual public " << service_name_ << "If" << extends_multiface << " {" << endl + << " public:" << endl; + indent_up(); + f_header_ << indent() << service_name_ << "Multiface(" << list_type + << "& ifaces) : ifaces_(ifaces) {" << endl; + if (!extends.empty()) { + f_header_ << indent() + << " std::vector<boost::shared_ptr<" + service_name_ + "If> >::iterator iter;" + << endl << indent() << " for (iter = ifaces.begin(); iter != ifaces.end(); ++iter) {" + << endl << indent() << " " << extends << "Multiface::add(*iter);" << endl + << indent() << " }" << endl; + } + f_header_ << indent() << "}" << endl << indent() << "virtual ~" << service_name_ + << "Multiface() {}" << endl; + indent_down(); + + // Protected data members + f_header_ << " protected:" << endl; + indent_up(); + f_header_ << indent() << list_type << " ifaces_;" << endl << indent() << service_name_ + << "Multiface() {}" << endl << indent() << "void add(boost::shared_ptr<" + << service_name_ << "If> iface) {" << endl; + if (!extends.empty()) { + f_header_ << indent() << " " << extends << "Multiface::add(iface);" << endl; + } + f_header_ << indent() << " ifaces_.push_back(iface);" << endl << indent() << "}" << endl; + indent_down(); + + f_header_ << indent() << " public:" << endl; + indent_up(); + + for (f_iter = functions.begin(); f_iter != functions.end(); ++f_iter) { + t_struct* arglist = (*f_iter)->get_arglist(); + const vector<t_field*>& args = arglist->get_members(); + vector<t_field*>::const_iterator a_iter; + + string call = string("ifaces_[i]->") + (*f_iter)->get_name() + "("; + bool first = true; + if (is_complex_type((*f_iter)->get_returntype())) { + call += "_return"; + first = false; + } + for (a_iter = args.begin(); a_iter != args.end(); ++a_iter) { + if (first) { + first = false; + } else { + call += ", "; + } + call += (*a_iter)->get_name(); + } + call += ")"; + + f_header_ << indent() << function_signature(*f_iter, "") << " {" << 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; + indent_up(); + f_header_ << indent() << call << ";" << endl; + indent_down(); + f_header_ << indent() << "}" << endl; + + if (!(*f_iter)->get_returntype()->is_void()) { + if (is_complex_type((*f_iter)->get_returntype())) { + f_header_ << indent() << call << ";" << endl << indent() << "return;" << endl; + } else { + f_header_ << indent() << "return " << call << ";" << endl; + } + } else { + f_header_ << indent() << call << ";" << endl; + } + + indent_down(); + f_header_ << indent() << "}" << endl << endl; + } + + indent_down(); + f_header_ << indent() << "};" << endl << endl; +} + +/** + * Generates a service client definition. + * + * @param tservice The service to generate a server for. + */ +void t_cpp_generator::generate_service_client(t_service* tservice, string style) { + string ifstyle; + if (style == "Cob") { + ifstyle = "CobCl"; + } + + std::ofstream& out = (gen_templates_ ? f_service_tcc_ : f_service_); + string template_header, template_suffix, short_suffix, protocol_type, _this; + string const prot_factory_type = "::apache::thrift::protocol::TProtocolFactory"; + if (gen_templates_) { + template_header = "template <class Protocol_>\n"; + short_suffix = "T"; + template_suffix = "T<Protocol_>"; + protocol_type = "Protocol_"; + _this = "this->"; + } else { + protocol_type = "::apache::thrift::protocol::TProtocol"; + } + string prot_ptr = "boost::shared_ptr< " + protocol_type + ">"; + string client_suffix = "Client" + template_suffix; + string if_suffix = "If"; + if (style == "Cob") { + if_suffix += template_suffix; + } + + string extends = ""; + string extends_client = ""; + if (tservice->get_extends() != NULL) { + // TODO(simpkins): If gen_templates_ is enabled, we currently assume all + // parent services were also generated with templates enabled. + extends = type_name(tservice->get_extends()); + extends_client = ", public " + extends + style + client_suffix; + } + + // Generate the header portion + if(style == "Concurrent") + { + f_header_ << + "// The \'concurrent\' client is a thread safe client that correctly handles\n" + "// out of order responses. It is slower than the regular client, so should\n" + "// only be used when you need to share a connection among multiple threads\n"; + } + f_header_ << template_header << "class " << service_name_ << style << "Client" << short_suffix + << " : " + << "virtual public " << service_name_ << ifstyle << if_suffix << extends_client << " {" + << endl << " public:" << endl; + + indent_up(); + if (style != "Cob") { + f_header_ << indent() << service_name_ << style << "Client" << short_suffix << "(" << prot_ptr + << " prot) "; + + if (extends.empty()) { + f_header_ << "{" << endl; + f_header_ << indent() << " setProtocol" << short_suffix << "(prot);" << endl << indent() + << "}" << endl; + } else { + f_header_ << ":" << endl; + f_header_ << indent() << " " << extends << style << client_suffix << "(prot, prot) {}" + << endl; + } + + f_header_ << indent() << service_name_ << style << "Client" << short_suffix << "(" << prot_ptr + << " iprot, " << prot_ptr << " oprot) "; + if (extends.empty()) { + f_header_ << "{" << endl; + f_header_ << indent() << " setProtocol" << short_suffix << "(iprot,oprot);" << endl + << indent() << "}" << endl; + } else { + f_header_ << ":" << indent() << " " << extends << style << client_suffix + << "(iprot, oprot) {}" << endl; + } + + // create the setProtocol methods + if (extends.empty()) { + f_header_ << " private:" << endl; + // 1: one parameter + f_header_ << indent() << "void setProtocol" << short_suffix << "(" << prot_ptr << " prot) {" + << endl; + f_header_ << indent() << "setProtocol" << short_suffix << "(prot,prot);" << endl; + f_header_ << indent() << "}" << endl; + // 2: two parameter + f_header_ << indent() << "void setProtocol" << short_suffix << "(" << prot_ptr << " iprot, " + << prot_ptr << " oprot) {" << endl; + + f_header_ << indent() << " piprot_=iprot;" << endl << indent() << " poprot_=oprot;" << endl + << indent() << " iprot_ = iprot.get();" << endl << indent() + << " oprot_ = oprot.get();" << endl; + + f_header_ << indent() << "}" << endl; + f_header_ << " public:" << endl; + } + + // Generate getters for the protocols. + // Note that these are not currently templated for simplicity. + // TODO(simpkins): should they be templated? + f_header_ << indent() + << "boost::shared_ptr< ::apache::thrift::protocol::TProtocol> getInputProtocol() {" + << endl << indent() << " return " << _this << "piprot_;" << endl << indent() << "}" + << endl; + + f_header_ << indent() + << "boost::shared_ptr< ::apache::thrift::protocol::TProtocol> getOutputProtocol() {" + << endl << indent() << " return " << _this << "poprot_;" << endl << indent() << "}" + << endl; + + } else /* if (style == "Cob") */ { + f_header_ << indent() << service_name_ << style << "Client" << short_suffix << "(" + << "boost::shared_ptr< ::apache::thrift::async::TAsyncChannel> channel, " + << "::apache::thrift::protocol::TProtocolFactory* protocolFactory) :" << endl; + if (extends.empty()) { + f_header_ << indent() << " channel_(channel)," << endl << indent() + << " itrans_(new ::apache::thrift::transport::TMemoryBuffer())," << endl + << indent() << " otrans_(new ::apache::thrift::transport::TMemoryBuffer())," + << endl; + if (gen_templates_) { + // TProtocolFactory classes return generic TProtocol pointers. + // We have to dynamic cast to the Protocol_ type we are expecting. + f_header_ << indent() << " piprot_(boost::dynamic_pointer_cast<Protocol_>(" + << "protocolFactory->getProtocol(itrans_)))," << endl << indent() + << " poprot_(boost::dynamic_pointer_cast<Protocol_>(" + << "protocolFactory->getProtocol(otrans_))) {" << endl; + // Throw a TException if either dynamic cast failed. + f_header_ << indent() << " if (!piprot_ || !poprot_) {" << endl << indent() + << " throw ::apache::thrift::TException(\"" + << "TProtocolFactory returned unexpected protocol type in " << service_name_ + << style << "Client" << short_suffix << " constructor\");" << endl << indent() + << " }" << endl; + } else { + f_header_ << indent() << " piprot_(protocolFactory->getProtocol(itrans_))," << endl + << indent() << " poprot_(protocolFactory->getProtocol(otrans_)) {" << endl; + } + f_header_ << indent() << " iprot_ = piprot_.get();" << endl << indent() + << " oprot_ = poprot_.get();" << endl << indent() << "}" << endl; + } else { + f_header_ << indent() << " " << extends << style << client_suffix + << "(channel, protocolFactory) {}" << endl; + } + } + + if (style == "Cob") { + f_header_ << indent() + << "boost::shared_ptr< ::apache::thrift::async::TAsyncChannel> getChannel() {" << endl + << indent() << " return " << _this << "channel_;" << endl << indent() << "}" << endl; + if (!gen_no_client_completion_) { + f_header_ << indent() << "virtual void completed__(bool /* success */) {}" << endl; + } + } + + vector<t_function*> functions = tservice->get_functions(); + vector<t_function*>::const_iterator f_iter; + for (f_iter = functions.begin(); f_iter != functions.end(); ++f_iter) { + indent(f_header_) << function_signature(*f_iter, ifstyle) << ";" << 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 + // recv function. Oneway methods don't have a recv function, so we don't need to + // move the seqid for them. Attempting to do so would result in a seqid leak. + t_function send_function(g_type_i32, /*returning seqid*/ + string("send_") + (*f_iter)->get_name(), + (*f_iter)->get_arglist()); + indent(f_header_) << function_signature(&send_function, "") << ";" << endl; + } + else { + t_function send_function(g_type_void, + string("send_") + (*f_iter)->get_name(), + (*f_iter)->get_arglist()); + indent(f_header_) << function_signature(&send_functi
<TRUNCATED>
