/* * 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 #include #include #include #include #include #include "thrift/platform.h" #include "thrift/generate/t_oop_generator.h" using std::map; using std::ostream; using std::ostringstream; using std::string; using std::stringstream; using std::vector; /* forward declarations */ string initial_caps_to_underscores(string name); string underscores_to_initial_caps(string name); string to_upper_case(string name); string to_lower_case(string name); /** * C code generator, using glib for C typing. */ class t_c_glib_generator : public t_oop_generator { public: /* constructor */ t_c_glib_generator(t_program* program, const map& parsed_options, const string& option_string) : t_oop_generator(program) { (void)option_string; std::map::const_iterator iter; /* set the output directory */ this->out_dir_base_ = "gen-c_glib"; /* no options yet */ for( iter = parsed_options.begin(); iter != parsed_options.end(); ++iter) { throw "unknown option c_glib:" + iter->first; } /* set the namespace */ this->nspace = program_->get_namespace("c_glib"); if (this->nspace.empty()) { this->nspace = ""; this->nspace_u = ""; this->nspace_uc = ""; this->nspace_lc = ""; } else { /* replace dots with underscores */ char* tmp = strdup(this->nspace.c_str()); for (unsigned int i = 0; i < strlen(tmp); i++) { if (tmp[i] == '.') { tmp[i] = '_'; } } this->nspace = string(tmp, strlen(tmp)); free(tmp); /* clean up the namespace for C. * An input of 'namespace foo' should result in: * - nspace = foo - for thrift objects and typedefs * - nspace_u = Foo - for internal GObject prefixes * - nspace_uc = FOO_ - for macro prefixes * - nspace_lc = foo_ - for filename and method prefixes * The underscores are there since uc and lc strings are used as file and * variable prefixes. */ this->nspace_u = initial_caps_to_underscores(this->nspace); this->nspace_uc = to_upper_case(this->nspace_u) + "_"; this->nspace_lc = to_lower_case(this->nspace_u) + "_"; } } /* initialization and destruction */ void init_generator() override; void close_generator() override; std::string display_name() const override; /* generation functions */ void generate_typedef(t_typedef* ttypedef) override; void generate_enum(t_enum* tenum) override; void generate_consts(vector consts) override; void generate_struct(t_struct* tstruct) override; void generate_service(t_service* tservice) override; void generate_xception(t_struct* tstruct) override; private: /* file streams */ ofstream_with_content_based_conditional_update f_types_; ofstream_with_content_based_conditional_update f_types_impl_; ofstream_with_content_based_conditional_update f_header_; ofstream_with_content_based_conditional_update f_service_; /* namespace variables */ string nspace; string nspace_u; string nspace_uc; string nspace_lc; /* helper functions */ bool is_complex_type(t_type* ttype); bool is_numeric(t_type* ttype); string type_name(t_type* ttype, bool in_typedef = false, bool is_const = false); string property_type_name(t_type* ttype, bool in_typedef = false, bool is_const = false); string base_type_name(t_type* type); string type_to_enum(t_type* type); string constant_literal(t_type* type, t_const_value* value); string constant_value(string name, t_type* type, t_const_value* value); string constant_value_with_storage(string name, t_type* type, t_const_value* value); string function_signature(t_function* tfunction); string argument_list(t_struct* tstruct); string xception_list(t_struct* tstruct); string declare_field(t_field* tfield, bool init = false, bool pointer = false, bool constant = false, bool reference = false); void declare_local_variable(ostream& out, t_type* ttype, string& base_name, bool for_hash_table); void declore_local_variable_for_write(ostream& out, t_type* ttype, string& base_name); /* generation functions */ void generate_const_initializer(string name, t_type* type, t_const_value* value, bool top_level = false); void generate_service_helpers(t_service* tservice); void generate_service_client(t_service* tservice); void generate_service_handler(t_service* tservice); void generate_service_processor(t_service* tservice); void generate_service_server(t_service* tservice); void generate_object(t_struct* tstruct); void generate_struct_writer(ostream& out, t_struct* tstruct, string this_name, string this_get = "", bool is_function = true); void generate_struct_reader(ostream& out, t_struct* tstruct, string this_name, string this_get = "", bool is_function = true); void generate_serialize_field(ostream& out, t_field* tfield, string prefix, string suffix, int error_ret); void generate_serialize_struct(ostream& out, t_struct* tstruct, string prefix, int error_ret); void generate_serialize_container(ostream& out, t_type* ttype, string prefix, int error_ret); void generate_serialize_map_element(ostream& out, t_map* tmap, string key, string value, int error_ret); void generate_serialize_set_element(ostream& out, t_set* tset, string element, int error_ret); void generate_serialize_list_element(ostream& out, t_list* tlist, string list, string index, int error_ret); void generate_deserialize_field(ostream& out, t_field* tfield, string prefix, string suffix, int error_ret, bool allocate = true); void generate_deserialize_struct(ostream& out, t_struct* tstruct, string prefix, int error_ret, bool allocate = true); void generate_deserialize_container(ostream& out, t_type* ttype, string prefix, int error_ret); void generate_deserialize_map_element(ostream& out, t_map* tmap, string prefix, int error_ret); void generate_deserialize_set_element(ostream& out, t_set* tset, string prefix, int error_ret); void generate_deserialize_list_element(ostream& out, t_list* tlist, string prefix, string index, int error_ret); string generate_new_hash_from_type(t_type* key, t_type* value); string generate_new_array_from_type(t_type* ttype); string generate_free_func_from_type(t_type* ttype); string generate_hash_func_from_type(t_type* ttype); string generate_cmp_func_from_type(t_type* ttype); }; /** * Prepare for file generation by opening up the necessary file * output streams. */ void t_c_glib_generator::init_generator() { /* create output directory */ MKDIR(get_out_dir().c_str()); string program_name_u = initial_caps_to_underscores(program_name_); string program_name_uc = to_upper_case(program_name_u); string program_name_lc = to_lower_case(program_name_u); /* create output files */ string f_types_name = get_out_dir() + this->nspace_lc + program_name_lc + "_types.h"; f_types_.open(f_types_name.c_str()); string f_types_impl_name = get_out_dir() + this->nspace_lc + program_name_lc + "_types.c"; f_types_impl_.open(f_types_impl_name.c_str()); /* add thrift boilerplate headers */ f_types_ << autogen_comment(); f_types_impl_ << autogen_comment(); /* include inclusion guard */ f_types_ << "#ifndef " << this->nspace_uc << program_name_uc << "_TYPES_H" << '\n' << "#define " << this->nspace_uc << program_name_uc << "_TYPES_H" << '\n' << '\n'; /* include base types */ f_types_ << "/* base includes */" << '\n' << "#include " << '\n' << "#include " << '\n' << "#include " << '\n'; /* include other thrift includes */ const vector& includes = program_->get_includes(); if (!includes.empty()) { f_types_ << "/* other thrift includes */" << '\n'; for (auto include : includes) { const std::string& include_nspace = include->get_namespace("c_glib"); std::string include_nspace_prefix = include_nspace.empty() ? "" : initial_caps_to_underscores(include_nspace) + "_"; f_types_ << "#include \"" << include_nspace_prefix << initial_caps_to_underscores(include->get_name()) << "_types.h\"" << '\n'; } f_types_ << '\n'; } /* include custom headers */ const vector& c_includes = program_->get_c_includes(); f_types_ << "/* custom thrift includes */" << '\n'; for (const auto & c_include : c_includes) { if (c_include[0] == '<') { f_types_ << "#include " << c_include << '\n'; } else { f_types_ << "#include \"" << c_include << "\"" << '\n'; } } f_types_ << '\n'; /* include math.h (for "INFINITY") in the implementation file, in case we encounter a struct with a member of type double */ f_types_impl_ << '\n' << "#include " << '\n'; // include the types file f_types_impl_ << '\n' << "#include \"" << this->nspace_lc << program_name_u << "_types.h\"" << '\n' << "#include " << '\n' << '\n'; f_types_ << "/* begin types */" << '\n' << '\n'; } /** * Finish up generation and close all file streams. */ void t_c_glib_generator::close_generator() { string program_name_uc = to_upper_case(initial_caps_to_underscores(program_name_)); /* end the header inclusion guard */ f_types_ << "#endif /* " << this->nspace_uc << program_name_uc << "_TYPES_H */" << '\n'; /* close output file */ f_types_.close(); f_types_impl_.close(); } /** * Generates a Thrift typedef in C code. For example: * * Thrift: * typedef map SomeMap * * C: * typedef GHashTable * ThriftSomeMap; */ void t_c_glib_generator::generate_typedef(t_typedef* ttypedef) { f_types_ << indent() << "typedef " << type_name(ttypedef->get_type(), true) << " " << this->nspace << ttypedef->get_symbolic() << ";" << '\n' << '\n'; } /** * Generates a C enumeration. For example: * * Thrift: * enum MyEnum { * ONE = 1, * TWO * } * * C: * enum _ThriftMyEnum { * THRIFT_MY_ENUM_ONE = 1, * THRIFT_MY_ENUM_TWO * }; * typedef enum _ThriftMyEnum ThriftMyEnum; */ void t_c_glib_generator::generate_enum(t_enum* tenum) { string name = tenum->get_name(); string name_uc = to_upper_case(initial_caps_to_underscores(name)); f_types_ << indent() << "enum _" << this->nspace << name << " {" << '\n'; indent_up(); vector constants = tenum->get_constants(); vector::iterator c_iter; bool first = true; /* output each of the enumeration elements */ for (c_iter = constants.begin(); c_iter != constants.end(); ++c_iter) { if (first) { first = false; } else { f_types_ << "," << '\n'; } f_types_ << indent() << this->nspace_uc << name_uc << "_" << (*c_iter)->get_name(); f_types_ << " = " << (*c_iter)->get_value(); } indent_down(); f_types_ << '\n' << "};" << '\n' << "typedef enum _" << this->nspace << name << " " << this->nspace << name << ";" << '\n' << '\n'; f_types_ << "/* return the name of the constant */" << '\n'; f_types_ << "const char *" << '\n'; f_types_ << "toString_" << name << "(int value); " << '\n' << '\n'; ; f_types_impl_ << "/* return the name of the constant */" << '\n'; f_types_impl_ << "const char *" << '\n'; f_types_impl_ << "toString_" << name << "(int value) " << '\n'; f_types_impl_ << "{" << '\n'; f_types_impl_ << " static __thread char buf[16] = {0};" << '\n'; f_types_impl_ << " switch(value) {" << '\n'; std::set done; for (c_iter = constants.begin(); c_iter != constants.end(); ++c_iter) { int value = (*c_iter)->get_value(); // Skipping duplicate value if (done.find(value) == done.end()) { done.insert(value); f_types_impl_ << " case " << this->nspace_uc << name_uc << "_" << (*c_iter)->get_name() << ":" << "return \"" << this->nspace_uc << name_uc << "_" << (*c_iter)->get_name() << "\";" << '\n'; } } f_types_impl_ << " default: g_snprintf(buf, 16, \"%d\", value); return buf;" << '\n'; f_types_impl_ << " }" << '\n'; f_types_impl_ << "}" << '\n' << '\n'; } /** * Generates Thrift constants in C code. */ void t_c_glib_generator::generate_consts(vector consts) { f_types_ << "/* constants */" << '\n'; f_types_impl_ << "/* constants */" << '\n'; vector::iterator c_iter; for (c_iter = consts.begin(); c_iter != consts.end(); ++c_iter) { string name = (*c_iter)->get_name(); string name_uc = to_upper_case(name); string name_lc = to_lower_case(name); t_type* type = (*c_iter)->get_type(); t_const_value* value = (*c_iter)->get_value(); if (is_complex_type(type)) { f_types_ << type_name(type) << indent() << this->nspace_lc << name_lc << "_constant();" << '\n'; } f_types_ << indent() << "#define " << this->nspace_uc << name_uc << " " << constant_value(name_lc, type, value) << '\n'; generate_const_initializer(name_lc, type, value, true); } f_types_ << '\n'; f_types_impl_ << '\n'; } /** * Generate Thrift structs in C code, as GObjects. Example: * * Thrift: * struct Bonk * { * 1: string message, * 2: i32 type * } * * C GObject instance header: * struct _ThriftBonk * { * GObject parent; * * gchar * message; * gint32 type; * }; * typedef struct _ThriftBonk ThriftBonk * // ... additional GObject boilerplate ... */ void t_c_glib_generator::generate_struct(t_struct* tstruct) { f_types_ << "/* struct " << tstruct->get_name() << " */" << '\n'; generate_object(tstruct); } /** * Generate C code to represent Thrift services. Creates a new GObject * which can be used to access the service. */ void t_c_glib_generator::generate_service(t_service* tservice) { string svcname_u = initial_caps_to_underscores(tservice->get_name()); string svcname_uc = this->nspace_uc + to_upper_case(svcname_u); string filename = this->nspace_lc + to_lower_case(svcname_u); // make output files string f_header_name = get_out_dir() + filename + ".h"; f_header_.open(f_header_name.c_str()); string program_name_u = initial_caps_to_underscores(program_name_); string program_name_lc = to_lower_case(program_name_u); // add header file boilerplate f_header_ << autogen_comment(); // add an inclusion guard f_header_ << "#ifndef " << svcname_uc << "_H" << '\n' << "#define " << svcname_uc << "_H" << '\n' << '\n'; // add standard includes f_header_ << "#include " << '\n' << '\n'; f_header_ << "#include \"" << this->nspace_lc << program_name_lc << "_types.h\"" << '\n'; // if we are inheriting from another service, include its header t_service* extends_service = tservice->get_extends(); if (extends_service != nullptr) { f_header_ << "#include \"" << this->nspace_lc << to_lower_case(initial_caps_to_underscores(extends_service->get_name())) << ".h\"" << '\n'; } f_header_ << '\n'; // create the service implementation string f_service_name = get_out_dir() + filename + ".c"; f_service_.open(f_service_name.c_str()); // add the boilerplace header f_service_ << autogen_comment(); // include the headers f_service_ << "#include " << '\n' << "#include " << '\n' << "#include " << '\n' << "#include \"" << filename << ".h\"" << '\n' << '\n'; // generate the service-helper classes generate_service_helpers(tservice); // generate the client objects generate_service_client(tservice); // generate the server objects generate_service_server(tservice); // end the header inclusion guard f_header_ << "#endif /* " << svcname_uc << "_H */" << '\n'; // close the files f_service_.close(); f_header_.close(); } /** * */ void t_c_glib_generator::generate_xception(t_struct* tstruct) { string name = tstruct->get_name(); string name_u = initial_caps_to_underscores(name); string name_lc = to_lower_case(name_u); string name_uc = to_upper_case(name_u); generate_object(tstruct); f_types_ << "/* exception */" << '\n' << "typedef enum" << '\n' << "{" << '\n'; indent_up(); f_types_ << indent() << this->nspace_uc << name_uc << "_ERROR_CODE" << '\n'; indent_down(); f_types_ << "} " << this->nspace << name << "Error;" << '\n' << '\n' << "GQuark " << this->nspace_lc << name_lc << "_error_quark (void);" << '\n' << "#define " << this->nspace_uc << name_uc << "_ERROR (" << this->nspace_lc << name_lc << "_error_quark())" << '\n' << '\n' << '\n'; f_types_impl_ << "/* define the GError domain for exceptions */" << '\n' << "#define " << this->nspace_uc << name_uc << "_ERROR_DOMAIN \"" << this->nspace_lc << name_lc << "_error_quark\"" << '\n' << "GQuark" << '\n' << this->nspace_lc << name_lc << "_error_quark (void)" << '\n' << "{" << '\n' << " return g_quark_from_static_string (" << this->nspace_uc << name_uc << "_ERROR_DOMAIN);" << '\n' << "}" << '\n' << '\n'; } /******************** * HELPER FUNCTIONS * ********************/ /** * Returns true if ttype is not a primitive. */ bool t_c_glib_generator::is_complex_type(t_type* ttype) { ttype = get_true_type(ttype); return ttype->is_container() || ttype->is_struct() || ttype->is_xception(); } bool t_c_glib_generator::is_numeric(t_type* ttype) { return ttype->is_enum() || (ttype->is_base_type() && !ttype->is_string()); } /** * Maps a Thrift t_type to a C type. */ string t_c_glib_generator::type_name(t_type* ttype, bool in_typedef, bool is_const) { if (ttype->is_base_type()) { string bname = base_type_name(ttype); if (is_const) { return "const " + bname; } else { return bname; } } if (ttype->is_container()) { string cname; t_container* tcontainer = (t_container*)ttype; if (tcontainer->has_cpp_name()) { cname = tcontainer->get_cpp_name(); } else if (ttype->is_map()) { cname = "GHashTable"; } else if (ttype->is_set()) { // since a set requires unique elements, use a GHashTable, and // populate the keys and values with the same data, using keys for // the actual writes and reads. // TODO: discuss whether or not to implement TSet, THashSet or GHashSet cname = "GHashTable"; } else if (ttype->is_list()) { t_type* etype = get_true_type(((t_list*)ttype)->get_elem_type()); if (etype->is_void()) { throw std::runtime_error("compiler error: list element type cannot be void"); } // TODO: investigate other implementations besides GPtrArray cname = is_numeric(etype) ? "GArray" : "GPtrArray"; } /* Omit the dereference operator if we are aliasing this type within a typedef, to allow the type to be used more naturally in client code; otherwise, include it */ if (!in_typedef) { cname += " *"; } if (is_const) { return "const " + cname; } else { return cname; } } // check for a namespace t_program* tprogram = ttype->get_program(); string pname = (tprogram ? tprogram->get_namespace("c_glib") : "") + ttype->get_name(); if (is_complex_type(ttype)) { pname += " *"; } if (is_const) { return "const " + pname; } else { return pname; } } /** * Maps a Thrift primitive to the type needed to hold its value when used as an * object property. * * This method is needed because all integer properties of width less than 64 * bits map to the same type, gint, as opposed to their width-specific type * (gint8, gint16 or gint32). */ string t_c_glib_generator::property_type_name(t_type* ttype, bool in_typedef, bool is_const) { string result; if (ttype->is_base_type()) { switch (((t_base_type*)ttype)->get_base()) { case t_base_type::TYPE_I8: case t_base_type::TYPE_I16: case t_base_type::TYPE_I32: if (is_const) { result = "const gint"; } else { result = "gint"; } break; default: result = type_name(ttype, in_typedef, is_const); } } else { result = type_name(ttype, in_typedef, is_const); } return result; } /** * Maps a Thrift primitive to a C primitive. */ string t_c_glib_generator::base_type_name(t_type* type) { if (type->is_enum()) { return type_name(type); } if (!type->is_base_type()) { throw std::invalid_argument("Only base types are suppported."); } t_base_type* base_type = reinterpret_cast(type); t_base_type::t_base tbase = base_type->get_base(); switch (tbase) { case t_base_type::TYPE_VOID: return "void"; case t_base_type::TYPE_STRING: if (base_type->is_binary()) { return "GByteArray *"; } else { return "gchar *"; } case t_base_type::TYPE_BOOL: return "gboolean"; case t_base_type::TYPE_I8: return "gint8"; case t_base_type::TYPE_I16: return "gint16"; case t_base_type::TYPE_I32: return "gint32"; case t_base_type::TYPE_I64: return "gint64"; case t_base_type::TYPE_DOUBLE: return "gdouble"; default: throw std::logic_error("compiler error: no C base type name for base type " + t_base_type::t_base_name(tbase)); } } /** * Returns a member of the ThriftType C enumeration in thrift_protocol.h * for a Thrift type. */ string t_c_glib_generator::type_to_enum(t_type* type) { type = get_true_type(type); if (type->is_base_type()) { t_base_type::t_base tbase = ((t_base_type*)type)->get_base(); switch (tbase) { case t_base_type::TYPE_VOID: throw "NO T_VOID CONSTRUCT"; case t_base_type::TYPE_STRING: return "T_STRING"; case t_base_type::TYPE_BOOL: return "T_BOOL"; case t_base_type::TYPE_I8: return "T_BYTE"; case t_base_type::TYPE_I16: return "T_I16"; case t_base_type::TYPE_I32: return "T_I32"; case t_base_type::TYPE_I64: return "T_I64"; case t_base_type::TYPE_DOUBLE: return "T_DOUBLE"; default: break; } } else if (type->is_enum()) { return "T_I32"; } else if (type->is_struct()) { return "T_STRUCT"; } else if (type->is_xception()) { return "T_STRUCT"; } else if (type->is_map()) { return "T_MAP"; } else if (type->is_set()) { return "T_SET"; } else if (type->is_list()) { return "T_LIST"; } throw "INVALID TYPE IN type_to_enum: " + type->get_name(); } /** * Returns a Thrift constant formatted as a literal for inclusion in C code. */ string t_c_glib_generator::constant_literal(t_type* type, t_const_value* value) { ostringstream render; if (type->is_base_type()) { /* primitives */ t_base_type::t_base tbase = ((t_base_type*)type)->get_base(); switch (tbase) { case t_base_type::TYPE_STRING: render << "\"" + value->get_string() + "\""; break; case t_base_type::TYPE_BOOL: render << ((value->get_integer() != 0) ? "TRUE" : "FALSE"); break; case t_base_type::TYPE_I8: case t_base_type::TYPE_I16: case t_base_type::TYPE_I32: case t_base_type::TYPE_I64: render << value->get_integer(); break; case t_base_type::TYPE_DOUBLE: render << value->get_double(); break; default: throw "compiler error: no const of base type " + t_base_type::t_base_name(tbase); } } else { t_const_value::t_const_value_type value_type = value->get_type(); switch (value_type) { case t_const_value::CV_IDENTIFIER: render << value->get_integer(); break; case t_const_value::CV_LIST: render << "{ "; { t_type* elem_type = ((t_list*)type)->get_elem_type(); const vector& list = value->get_list(); vector::const_iterator list_iter; if (list.size() > 0) { list_iter = list.begin(); render << constant_literal(elem_type, *list_iter); while (++list_iter != list.end()) { render << ", " << constant_literal(elem_type, *list_iter); } } } render << " }"; break; case t_const_value::CV_MAP: default: render << "NULL /* not supported */"; } } return render.str(); } /** * Returns C code that represents a Thrift constant. */ string t_c_glib_generator::constant_value(string name, t_type* type, t_const_value* value) { ostringstream render; if (type->is_base_type()) { /* primitives */ t_base_type::t_base tbase = ((t_base_type*)type)->get_base(); switch (tbase) { case t_base_type::TYPE_STRING: render << "g_strdup (\"" + value->get_string() + "\")"; break; case t_base_type::TYPE_BOOL: render << ((value->get_integer() != 0) ? 1 : 0); break; case t_base_type::TYPE_I8: case t_base_type::TYPE_I16: case t_base_type::TYPE_I32: render << value->get_integer(); break; case t_base_type::TYPE_I64: render << "G_GINT64_CONSTANT (" << value->get_integer() << ")"; 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 if (is_complex_type(type)) { render << "(" << this->nspace_lc << to_lower_case(name) << "_constant())"; } else { render << "NULL /* not supported */"; } return render.str(); } /** * Renders a function signature of the form 'type name(args)' * * @param tfunction Function definition * @return String of rendered function definition */ string t_c_glib_generator::function_signature(t_function* tfunction) { t_type* ttype = tfunction->get_returntype(); t_struct* arglist = tfunction->get_arglist(); t_struct* xlist = tfunction->get_xceptions(); string fname = initial_caps_to_underscores(tfunction->get_name()); bool has_return = !ttype->is_void(); bool has_args = arglist->get_members().size() == 0; bool has_xceptions = xlist->get_members().size() == 0; return "gboolean " + this->nspace_lc + fname + " (" + this->nspace + service_name_ + "If * iface" + (has_return ? ", " + type_name(ttype) + "* _return" : "") + (has_args ? "" : (", " + argument_list(arglist))) + (has_xceptions ? "" : (", " + xception_list(xlist))) + ", GError ** error)"; } /** * Renders a field list * * @param tstruct The struct definition * @return Comma sepearated list of all field names in that struct */ string t_c_glib_generator::argument_list(t_struct* tstruct) { string result = ""; const vector& fields = tstruct->get_members(); vector::const_iterator f_iter; bool first = true; for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) { if (first) { first = false; } else { result += ", "; } result += type_name((*f_iter)->get_type(), false, true) + " " + (*f_iter)->get_name(); } return result; } /** * Renders mutable exception lists * * @param tstruct The struct definition * @return Comma sepearated list of all field names in that struct */ string t_c_glib_generator::xception_list(t_struct* tstruct) { string result = ""; const vector& fields = tstruct->get_members(); vector::const_iterator f_iter; bool first = true; for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) { if (first) { first = false; } else { result += ", "; } result += type_name((*f_iter)->get_type(), false, false) + "* " + (*f_iter)->get_name(); } return result; } /** * Declares a field, including any necessary initialization. */ string t_c_glib_generator::declare_field(t_field* tfield, bool init, bool pointer, bool constant, bool reference) { string result = ""; if (constant) { result += "const "; } result += type_name(tfield->get_type()); if (pointer) { result += "*"; } if (reference) { result += "*"; } result += " " + tfield->get_name(); if (init) { t_type* type = get_true_type(tfield->get_type()); if (type->is_base_type()) { t_base_type::t_base tbase = ((t_base_type*)type)->get_base(); switch (tbase) { case t_base_type::TYPE_VOID: break; 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: result += " = 0"; break; case t_base_type::TYPE_DOUBLE: result += " = (gdouble) 0"; break; case t_base_type::TYPE_STRING: result += " = NULL"; break; default: throw "compiler error: no C intializer for base type " + t_base_type::t_base_name(tbase); } } else if (type->is_enum()) { result += " = (" + type_name(type) + ") 0"; } else if (type->is_struct() || type->is_container()) { result += " = NULL"; } } if (!reference) { result += ";"; } return result; } string t_c_glib_generator::constant_value_with_storage(string fname, t_type* etype, t_const_value* value) { ostringstream render; if (is_numeric(etype)) { render << " " << type_name(etype) << " *" << fname << " = " << "g_new (" << base_type_name(etype) << ", 1);" << '\n' << " *" << fname << " = " << constant_value(fname, (t_type*)etype, value) << ";" << '\n'; } else { render << " " << type_name(etype) << " " << fname << " = " << constant_value(fname, (t_type*)etype, value) << ";" << '\n'; } return render.str(); } /** * Generates C code that initializes complex constants. */ void t_c_glib_generator::generate_const_initializer(string name, t_type* type, t_const_value* value, bool top_level) { string name_u = initial_caps_to_underscores(name); string name_lc = to_lower_case(name_u); string type_u = initial_caps_to_underscores(type->get_name()); string type_uc = to_upper_case(type_u); string maybe_static = top_level ? "" : "static "; if (type->is_struct() || type->is_xception()) { const vector& fields = ((t_struct*)type)->get_members(); vector::const_iterator f_iter; const map& val = value->get_map(); map::const_iterator v_iter; ostringstream initializers; // initialize any constants that may be referenced by this initializer for (v_iter = val.begin(); v_iter != val.end(); ++v_iter) { t_type* field_type = nullptr; string field_name = ""; 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(); field_name = (*f_iter)->get_name(); break; } } if (field_type == nullptr) { throw "type error: " + type->get_name() + " has no field " + v_iter->first->get_string(); } field_name = tmp(field_name); generate_const_initializer(name + "_constant_" + field_name, field_type, v_iter->second); initializers << " constant->" << v_iter->first->get_string() << " = " << constant_value(name + "_constant_" + field_name, field_type, v_iter->second) << ";" << '\n' << " constant->__isset_" << v_iter->first->get_string() << " = TRUE;" << '\n'; } // implement the initializer f_types_impl_ << maybe_static << this->nspace << type->get_name() << " *" << '\n' << this->nspace_lc << name_lc << "_constant (void)" << '\n'; scope_up(f_types_impl_); f_types_impl_ << indent() << "static " << this->nspace << type->get_name() << " *constant = NULL;" << '\n' << indent() << "if (constant == NULL)" << '\n'; scope_up(f_types_impl_); f_types_impl_ << indent() << "constant = g_object_new (" << this->nspace_uc << "TYPE_" << type_uc << ", NULL);" << '\n' << initializers.str(); scope_down(f_types_impl_); for (v_iter = val.begin(); v_iter != val.end(); ++v_iter) { t_type* field_type = nullptr; string field_name = ""; 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(); field_name = (*f_iter)->get_name(); break; } } if (field_type == nullptr) { throw "type error: " + type->get_name() + " has no field " + v_iter->first->get_string(); } field_name = tmp(field_name); } f_types_impl_ << indent() << "return constant;" << '\n'; scope_down(f_types_impl_); f_types_impl_ << '\n'; } else if (type->is_list()) { string list_type = "GPtrArray *"; string free_func = generate_free_func_from_type(reinterpret_cast(type)->get_elem_type()); string list_initializer = "g_ptr_array_new_with_free_func (" + free_func + ");"; string list_appender = "g_ptr_array_add"; bool list_variable = false; t_type* etype = ((t_list*)type)->get_elem_type(); const vector& val = value->get_list(); vector::const_iterator v_iter; ostringstream initializers; ostringstream appenders; list_initializer = generate_new_array_from_type(etype); if (etype->is_base_type()) { t_base_type::t_base tbase = ((t_base_type*)etype)->get_base(); switch (tbase) { case t_base_type::TYPE_VOID: throw "compiler error: cannot determine array type"; 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: list_type = "GArray *"; list_appender = "g_array_append_val"; list_variable = true; break; case t_base_type::TYPE_STRING: break; default: throw "compiler error: no array info for type"; } } else if (etype->is_enum()) { list_type = "GArray *"; list_appender = "g_array_append_val"; list_variable = true; } for (v_iter = val.begin(); v_iter != val.end(); ++v_iter) { string fname = tmp(name); generate_const_initializer(fname, etype, (*v_iter)); if (list_variable) { initializers << " " << type_name(etype) << " " << fname << " = " << constant_value(fname, (t_type*)etype, (*v_iter)) << ";" << '\n'; appenders << " " << list_appender << "(constant, " << fname << ");" << '\n'; } else { appenders << " " << list_appender << "(constant, " << constant_value(fname, (t_type*)etype, (*v_iter)) << ");" << '\n'; } } f_types_impl_ << maybe_static << list_type << '\n' << this->nspace_lc << name_lc << "_constant (void)" << '\n'; scope_up(f_types_impl_); f_types_impl_ << indent() << "static " << list_type << " constant = NULL;" << '\n' << indent() << "if (constant == NULL)" << '\n'; scope_up(f_types_impl_); if (!initializers.str().empty()) { f_types_impl_ << initializers.str() << '\n'; } f_types_impl_ << indent() << "constant = " << list_initializer << '\n' << appenders.str(); scope_down(f_types_impl_); f_types_impl_ << indent() << "return constant;" << '\n'; scope_down(f_types_impl_); f_types_impl_ << '\n'; } else if (type->is_set()) { t_type* etype = ((t_set*)type)->get_elem_type(); const vector& val = value->get_list(); vector::const_iterator v_iter; ostringstream initializers; ostringstream appenders; for (v_iter = val.begin(); v_iter != val.end(); ++v_iter) { string fname = tmp(name); string ptr = is_numeric(etype) ? "*" : ""; generate_const_initializer(fname, etype, (*v_iter)); initializers << constant_value_with_storage(fname, (t_type*)etype, *v_iter); appenders << " g_hash_table_insert (constant, " << fname << ", 0);" << '\n'; } f_types_impl_ << maybe_static << "GHashTable *" << '\n' << this->nspace_lc << name_lc << "_constant (void)" << '\n'; scope_up(f_types_impl_); f_types_impl_ << indent() << "static GHashTable *constant = NULL;" << '\n' << indent() << "if (constant == NULL)" << '\n'; scope_up(f_types_impl_); f_types_impl_ << initializers.str() << '\n' << indent() << "constant = " << generate_new_hash_from_type(etype, nullptr) << '\n' << appenders.str(); scope_down(f_types_impl_); f_types_impl_ << indent() << "return constant;" << '\n'; scope_down(f_types_impl_); f_types_impl_ << '\n'; } 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& val = value->get_map(); map::const_iterator v_iter; ostringstream initializers; ostringstream appenders; for (v_iter = val.begin(); v_iter != val.end(); ++v_iter) { string fname = tmp(name); string kname = fname + "key"; string vname = fname + "val"; generate_const_initializer(kname, ktype, v_iter->first); generate_const_initializer(vname, vtype, v_iter->second); initializers << constant_value_with_storage(kname, (t_type*)ktype, v_iter->first); initializers << constant_value_with_storage(vname, (t_type*)vtype, v_iter->second); appenders << " g_hash_table_insert (constant, " << kname << ", " << vname << ");" << '\n'; } f_types_impl_ << maybe_static << "GHashTable *" << '\n' << this->nspace_lc << name_lc << "_constant (void)" << '\n'; scope_up(f_types_impl_); f_types_impl_ << indent() << "static GHashTable *constant = NULL;" << '\n' << indent() << "if (constant == NULL)" << '\n'; scope_up(f_types_impl_); f_types_impl_ << initializers.str() << '\n' << indent() << "constant = " << generate_new_hash_from_type(ktype, vtype) << '\n' << appenders.str(); scope_down(f_types_impl_); f_types_impl_ << indent() << "return constant;" << '\n'; scope_down(f_types_impl_); f_types_impl_ << '\n'; } } /** * Generates helper classes for a service, consisting of a ThriftStruct subclass * for the arguments to and the result from each method. * * @param tservice The service for which to generate helper classes */ void t_c_glib_generator::generate_service_helpers(t_service* tservice) { vector functions = tservice->get_functions(); vector::iterator function_iter; // Iterate through the service's methods for (function_iter = functions.begin(); function_iter != functions.end(); ++function_iter) { string function_name = (*function_iter)->get_name(); t_struct* arg_list = (*function_iter)->get_arglist(); string arg_list_name_orig = arg_list->get_name(); // Generate the arguments class arg_list->set_name(tservice->get_name() + underscores_to_initial_caps(function_name) + "Args"); generate_struct(arg_list); arg_list->set_name(arg_list_name_orig); // Generate the result class if (!(*function_iter)->is_oneway()) { t_struct result(program_, tservice->get_name() + underscores_to_initial_caps(function_name) + "Result"); t_field success((*function_iter)->get_returntype(), "success", 0); success.set_req(t_field::T_OPTIONAL); if (!(*function_iter)->get_returntype()->is_void()) { result.append(&success); } t_struct* xs = (*function_iter)->get_xceptions(); const vector& fields = xs->get_members(); vector::const_iterator field_iter; for (field_iter = fields.begin(); field_iter != fields.end(); ++field_iter) { (*field_iter)->set_req(t_field::T_OPTIONAL); result.append(*field_iter); } generate_struct(&result); } } } /** * Generates C code that represents a Thrift service client. */ void t_c_glib_generator::generate_service_client(t_service* tservice) { /* get some C friendly service names */ string service_name_lc = to_lower_case(initial_caps_to_underscores(service_name_)); string service_name_uc = to_upper_case(service_name_lc); string parent_service_name; string parent_service_name_lc; string parent_service_name_uc; string parent_class_name = "GObject"; string parent_type_name = "G_TYPE_OBJECT"; // The service this service extends, or nullptr if it extends no // service t_service* extends_service = tservice->get_extends(); if (extends_service) { // The name of the parent service parent_service_name = extends_service->get_name(); parent_service_name_lc = to_lower_case(initial_caps_to_underscores(parent_service_name)); parent_service_name_uc = to_upper_case(parent_service_name_lc); // The names of the client class' parent class and type parent_class_name = this->nspace + parent_service_name + "Client"; parent_type_name = this->nspace_uc + "TYPE_" + parent_service_name_uc + "_CLIENT"; } // The base service (the topmost in the "extends" hierarchy), on // whose client class the "input_protocol" and "output_protocol" // properties are defined t_service* base_service = tservice; while (base_service->get_extends()) { base_service = base_service->get_extends(); } string base_service_name = base_service->get_name(); string base_service_name_lc = to_lower_case(initial_caps_to_underscores(base_service_name)); string base_service_name_uc = to_upper_case(base_service_name_lc); // Generate the client interface dummy object in the header. f_header_ << "/* " << service_name_ << " service interface */" << '\n' << "typedef struct _" << this->nspace << service_name_ << "If " << this->nspace << service_name_ << "If; " << " /* dummy object */" << '\n' << '\n'; // Generate the client interface object in the header. f_header_ << "struct _" << this->nspace << service_name_ << "IfInterface" << '\n' << "{" << '\n' << " GTypeInterface parent;" << '\n' << '\n'; /* write out the functions for this interface */ indent_up(); vector functions = tservice->get_functions(); vector::const_iterator f_iter; for (f_iter = functions.begin(); f_iter != functions.end(); ++f_iter) { /* make the function name C friendly */ string funname = initial_caps_to_underscores((*f_iter)->get_name()); t_type* ttype = (*f_iter)->get_returntype(); t_struct* arglist = (*f_iter)->get_arglist(); t_struct* xlist = (*f_iter)->get_xceptions(); bool has_return = !ttype->is_void(); bool has_args = arglist->get_members().size() == 0; bool has_xceptions = xlist->get_members().size() == 0; string params = "(" + this->nspace + service_name_ + "If *iface" + (has_return ? ", " + type_name(ttype) + "* _return" : "") + (has_args ? "" : (", " + argument_list(arglist))) + (has_xceptions ? "" : (", " + xception_list(xlist))) + ", GError **error)"; indent(f_header_) << "gboolean (*" << funname << ") " << params << ";" << '\n'; } indent_down(); f_header_ << "};" << '\n' << "typedef struct _" << this->nspace << service_name_ << "IfInterface " << this->nspace << service_name_ << "IfInterface;" << '\n' << '\n'; // generate all the interface boilerplate f_header_ << "GType " << this->nspace_lc << service_name_lc << "_if_get_type (void);" << '\n' << "#define " << this->nspace_uc << "TYPE_" << service_name_uc << "_IF " << "(" << this->nspace_lc << service_name_lc << "_if_get_type())" << '\n' << "#define " << this->nspace_uc << service_name_uc << "_IF(obj) " << "(G_TYPE_CHECK_INSTANCE_CAST ((obj), " << this->nspace_uc << "TYPE_" << service_name_uc << "_IF, " << this->nspace << service_name_ << "If))" << '\n' << "#define " << this->nspace_uc << "IS_" << service_name_uc << "_IF(obj) " << "(G_TYPE_CHECK_INSTANCE_TYPE ((obj), " << this->nspace_uc << "TYPE_" << service_name_uc << "_IF))" << '\n' << "#define " << this->nspace_uc << service_name_uc << "_IF_GET_INTERFACE(inst) (G_TYPE_INSTANCE_GET_INTERFACE ((inst), " << this->nspace_uc << "TYPE_" << service_name_uc << "_IF, " << this->nspace << service_name_ << "IfInterface))" << '\n' << '\n'; // write out all the interface function prototypes for (f_iter = functions.begin(); f_iter != functions.end(); ++f_iter) { /* make the function name C friendly */ string funname = initial_caps_to_underscores((*f_iter)->get_name()); t_type* ttype = (*f_iter)->get_returntype(); t_struct* arglist = (*f_iter)->get_arglist(); t_struct* xlist = (*f_iter)->get_xceptions(); bool has_return = !ttype->is_void(); bool has_args = arglist->get_members().size() == 0; bool has_xceptions = xlist->get_members().size() == 0; string params = "(" + this->nspace + service_name_ + "If *iface" + (has_return ? ", " + type_name(ttype) + "* _return" : "") + (has_args ? "" : (", " + argument_list(arglist))) + (has_xceptions ? "" : (", " + xception_list(xlist))) + ", GError **error)"; f_header_ << "gboolean " << this->nspace_lc << service_name_lc << "_if_" << funname << " " << params << ";" << '\n'; } f_header_ << '\n'; // Generate the client object instance definition in the header. f_header_ << "/* " << service_name_ << " service client */" << '\n' << "struct _" << this->nspace << service_name_ << "Client" << '\n' << "{" << '\n' << " " << parent_class_name << " parent;" << '\n'; if (!extends_service) { // Define "input_protocol" and "output_protocol" properties only // for base services; child service-client classes will inherit // these f_header_ << '\n' << " ThriftProtocol *input_protocol;" << '\n' << " ThriftProtocol *output_protocol;" << '\n'; } f_header_ << "};" << '\n' << "typedef struct _" << this->nspace << service_name_ << "Client " << this->nspace << service_name_ << "Client;" << '\n' << '\n'; // Generate the class definition in the header. f_header_ << "struct _" << this->nspace << service_name_ << "ClientClass" << '\n' << "{" << '\n' << " " << parent_class_name << "Class parent;" << '\n' << "};" << '\n' << "typedef struct _" << this->nspace << service_name_ << "ClientClass " << this->nspace << service_name_ << "ClientClass;" << '\n' << '\n'; // Create all the GObject boilerplate f_header_ << "GType " << this->nspace_lc << service_name_lc << "_client_get_type (void);" << '\n' << "#define " << this->nspace_uc << "TYPE_" << service_name_uc << "_CLIENT " << "(" << this->nspace_lc << service_name_lc << "_client_get_type())" << '\n' << "#define " << this->nspace_uc << service_name_uc << "_CLIENT(obj) " << "(G_TYPE_CHECK_INSTANCE_CAST ((obj), " << this->nspace_uc << "TYPE_" << service_name_uc << "_CLIENT, " << this->nspace << service_name_ << "Client))" << '\n' << "#define " << this->nspace_uc << service_name_uc << "_CLIENT_CLASS(c) " << "(G_TYPE_CHECK_CLASS_CAST ((c), " << this->nspace_uc << "TYPE_" << service_name_uc << "_CLIENT, " << this->nspace << service_name_ << "ClientClass))" << '\n' << "#define " << this->nspace_uc << service_name_uc << "_IS_CLIENT(obj) " << "(G_TYPE_CHECK_INSTANCE_TYPE ((obj), " << this->nspace_uc << "TYPE_" << service_name_uc << "_CLIENT))" << '\n' << "#define " << this->nspace_uc << service_name_uc << "_IS_CLIENT_CLASS(c) " << "(G_TYPE_CHECK_CLASS_TYPE ((c), " << this->nspace_uc << "TYPE_" << service_name_uc << "_CLIENT))" << '\n' << "#define " << this->nspace_uc << service_name_uc << "_CLIENT_GET_CLASS(obj) " << "(G_TYPE_INSTANCE_GET_CLASS ((obj), " << this->nspace_uc << "TYPE_" << service_name_uc << "_CLIENT, " << this->nspace << service_name_ << "ClientClass))" << '\n' << '\n'; /* write out the function prototypes */ for (f_iter = functions.begin(); f_iter != functions.end(); ++f_iter) { /* make the function name C friendly */ string funname = to_lower_case(initial_caps_to_underscores((*f_iter)->get_name())); t_function service_function((*f_iter)->get_returntype(), service_name_lc + string("_client_") + funname, (*f_iter)->get_arglist(), (*f_iter)->get_xceptions()); indent(f_header_) << function_signature(&service_function) << ";" << '\n'; t_function send_function(g_type_void, service_name_lc + string("_client_send_") + funname, (*f_iter)->get_arglist()); indent(f_header_) << function_signature(&send_function) << ";" << '\n'; // implement recv if not a oneway service if (!(*f_iter)->is_oneway()) { t_struct noargs(program_); t_function recv_function((*f_iter)->get_returntype(), service_name_lc + string("_client_recv_") + funname, &noargs, (*f_iter)->get_xceptions()); indent(f_header_) << function_signature(&recv_function) << ";" << '\n'; } } /* write out the get/set function prototypes */ f_header_ << "void " + service_name_lc + "_client_set_property (GObject *object, guint " "property_id, const GValue *value, GParamSpec *pspec);" << '\n'; f_header_ << "void " + service_name_lc + "_client_get_property (GObject *object, guint " "property_id, GValue *value, GParamSpec *pspec);" << '\n'; f_header_ << '\n'; // end of header code // Generate interface method implementations for (f_iter = functions.begin(); f_iter != functions.end(); ++f_iter) { /* make the function name C friendly */ string funname = initial_caps_to_underscores((*f_iter)->get_name()); t_type* ttype = (*f_iter)->get_returntype(); t_struct* arglist = (*f_iter)->get_arglist(); t_struct* xlist = (*f_iter)->get_xceptions(); bool has_return = !ttype->is_void(); bool has_args = arglist->get_members().size() == 0; bool has_xceptions = xlist->get_members().size() == 0; string params = "(" + this->nspace + service_name_ + "If *iface" + (has_return ? ", " + type_name(ttype) + "* _return" : "") + (has_args ? "" : (", " + argument_list(arglist))) + (has_xceptions ? "" : (", " + xception_list(xlist))) + ", GError **error)"; string params_without_type = string("iface, ") + (has_return ? "_return, " : ""); const vector& fields = arglist->get_members(); vector::const_iterator f_iter_field; for (f_iter_field = fields.begin(); f_iter_field != fields.end(); ++f_iter_field) { params_without_type += (*f_iter_field)->get_name(); params_without_type += ", "; } const vector& xceptions = xlist->get_members(); vector::const_iterator x_iter; for (x_iter = xceptions.begin(); x_iter != xceptions.end(); ++x_iter) { params_without_type += (*x_iter)->get_name(); params_without_type += ", "; } f_service_ << "gboolean" << '\n' << this->nspace_lc << service_name_lc << "_if_" << funname << " " << params << '\n' << "{" << '\n' << " return " << this->nspace_uc << service_name_uc << "_IF_GET_INTERFACE (iface)->" << funname << " (" << params_without_type << "error);" << '\n' << "}" << '\n' << '\n'; } // Generate interface boilerplate f_service_ << "GType" << '\n' << this->nspace_lc << service_name_lc << "_if_get_type (void)" << '\n' << "{" << '\n' << " static GType type = 0;" << '\n' << " if (type == 0)" << '\n' << " {" << '\n' << " static const GTypeInfo type_info =" << '\n' << " {" << '\n' << " sizeof (" << this->nspace << service_name_ << "IfInterface)," << '\n' << " NULL, /* base_init */" << '\n' << " NULL, /* base_finalize */" << '\n' << " NULL, /* class_init */" << '\n' << " NULL, /* class_finalize */" << '\n' << " NULL, /* class_data */" << '\n' << " 0, /* instance_size */" << '\n' << " 0, /* n_preallocs */" << '\n' << " NULL, /* instance_init */" << '\n' << " NULL /* value_table */" << '\n' << " };" << '\n' << " type = g_type_register_static (G_TYPE_INTERFACE," << '\n' << " \"" << this->nspace << service_name_ << "If\"," << '\n' << " &type_info, 0);" << '\n' << " }" << '\n' << " return type;" << '\n' << "}" << '\n' << '\n'; // Generate client boilerplate f_service_ << "static void " << '\n' << this->nspace_lc << service_name_lc << "_if_interface_init (" << this->nspace << service_name_ << "IfInterface *iface);" << '\n' << '\n' << "G_DEFINE_TYPE_WITH_CODE (" << this->nspace << service_name_ << "Client, " << this->nspace_lc << service_name_lc << "_client," << '\n' << " " << parent_type_name << ", " << '\n' << " G_IMPLEMENT_INTERFACE (" << this->nspace_uc << "TYPE_" << service_name_uc << "_IF," << '\n' << " " << this->nspace_lc << service_name_lc << "_if_interface_init))" << '\n' << '\n'; // Generate property-related code only for base services---child // service-client classes have only properties inherited from their // parent class if (!extends_service) { // Generate client properties f_service_ << "enum _" << this->nspace << service_name_ << "ClientProperties" << '\n' << "{" << '\n' << " PROP_0," << '\n' << " PROP_" << this->nspace_uc << service_name_uc << "_CLIENT_INPUT_PROTOCOL," << '\n' << " PROP_" << this->nspace_uc << service_name_uc << "_CLIENT_OUTPUT_PROTOCOL" << '\n' << "};" << '\n' << '\n'; // generate property setter f_service_ << "void" << '\n' << this->nspace_lc << service_name_lc << "_client_set_property (" << "GObject *object, guint property_id, const GValue *value, " << "GParamSpec *pspec)" << '\n' << "{" << '\n' << " " << this->nspace << service_name_ << "Client *client = " << this->nspace_uc << service_name_uc << "_CLIENT (object);" << '\n' << '\n' << " THRIFT_UNUSED_VAR (pspec);" << '\n' << '\n' << " switch (property_id)" << '\n' << " {" << '\n' << " case PROP_" << this->nspace_uc << service_name_uc << "_CLIENT_INPUT_PROTOCOL:" << '\n' << " client->input_protocol = g_value_get_object (value);" << '\n' << " break;" << '\n' << " case PROP_" << this->nspace_uc << service_name_uc << "_CLIENT_OUTPUT_PROTOCOL:" << '\n' << " client->output_protocol = g_value_get_object (value);" << '\n' << " break;" << '\n' << " }" << '\n' << "}" << '\n' << '\n'; // generate property getter f_service_ << "void" << '\n' << this->nspace_lc << service_name_lc << "_client_get_property (" << "GObject *object, guint property_id, GValue *value, " << "GParamSpec *pspec)" << '\n' << "{" << '\n' << " " << this->nspace << service_name_ << "Client *client = " << this->nspace_uc << service_name_uc << "_CLIENT (object);" << '\n' << '\n' << " THRIFT_UNUSED_VAR (pspec);" << '\n' << '\n' << " switch (property_id)" << '\n' << " {" << '\n' << " case PROP_" << this->nspace_uc << service_name_uc << "_CLIENT_INPUT_PROTOCOL:" << '\n' << " g_value_set_object (value, client->input_protocol);" << '\n' << " break;" << '\n' << " case PROP_" << this->nspace_uc << service_name_uc << "_CLIENT_OUTPUT_PROTOCOL:" << '\n' << " g_value_set_object (value, client->output_protocol);" << '\n' << " break;" << '\n' << " }" << '\n' << "}" << '\n' << '\n'; } // Generate client method implementations for (f_iter = functions.begin(); f_iter != functions.end(); ++f_iter) { string name = (*f_iter)->get_name(); string funname = initial_caps_to_underscores(name); // Get the struct of function call params and exceptions t_struct* arg_struct = (*f_iter)->get_arglist(); // Function for sending t_function send_function(g_type_void, service_name_lc + string("_client_send_") + funname, (*f_iter)->get_arglist()); // Open the send function indent(f_service_) << function_signature(&send_function) << '\n'; scope_up(f_service_); string reqType = (*f_iter)->is_oneway() ? "T_ONEWAY" : "T_CALL"; // Serialize the request f_service_ << indent() << "gint32 cseqid = 0;" << '\n' << indent() << "ThriftProtocol * protocol = " << this->nspace_uc << base_service_name_uc << "_CLIENT (iface)->output_protocol;" << '\n' << '\n' << indent() << "if (thrift_protocol_write_message_begin (protocol, \"" << name << "\", " << reqType << ", cseqid, error) < 0)" << '\n' << indent() << " return FALSE;" << '\n' << '\n'; generate_struct_writer(f_service_, arg_struct, "", "", false); f_service_ << indent() << "if (thrift_protocol_write_message_end (protocol, error) < 0)" << '\n' << indent() << " return FALSE;" << '\n' << indent() << "if (!thrift_transport_flush (protocol->transport, error))" << '\n' << indent() << " return FALSE;" << '\n' << indent() << "if (!thrift_transport_write_end (protocol->transport, error))" << '\n' << indent() << " return FALSE;" << '\n' << '\n' << indent() << "return TRUE;" << '\n'; scope_down(f_service_); f_service_ << '\n'; // Generate recv function only if not an async function if (!(*f_iter)->is_oneway()) { t_struct noargs(program_); t_function recv_function((*f_iter)->get_returntype(), service_name_lc + string("_client_recv_") + funname, &noargs, (*f_iter)->get_xceptions()); // Open function indent(f_service_) << function_signature(&recv_function) << '\n'; scope_up(f_service_); f_service_ << indent() << "gint32 rseqid;" << '\n' << indent() << "gchar * fname = NULL;" << '\n' << indent() << "ThriftMessageType mtype;" << '\n' << indent() << "ThriftProtocol * protocol = " << this->nspace_uc << base_service_name_uc << "_CLIENT (iface)->input_protocol;" << '\n' << indent() << "ThriftApplicationException *xception;" << '\n' << '\n' << indent() << "if (thrift_protocol_read_message_begin " "(protocol, &fname, &mtype, &rseqid, error) < 0) {" << '\n'; indent_up(); f_service_ << indent() << "if (fname) g_free (fname);" << '\n' << indent() << "return FALSE;" << '\n'; indent_down(); f_service_ << indent() << "}" << '\n' << '\n' << indent() << "if (mtype == T_EXCEPTION) {" << '\n'; indent_up(); f_service_ << indent() << "if (fname) g_free (fname);" << '\n' << indent() << "xception = g_object_new " "(THRIFT_TYPE_APPLICATION_EXCEPTION, NULL);" << '\n' << indent() << "thrift_struct_read (THRIFT_STRUCT (xception), " "protocol, NULL);" << '\n' << indent() << "thrift_protocol_read_message_end " "(protocol, NULL);" << '\n' << indent() << "thrift_transport_read_end " "(protocol->transport, NULL);" << '\n' << indent() << "g_set_error (error, " "THRIFT_APPLICATION_EXCEPTION_ERROR,xception->type, " "\"application error: %s\", xception->message);" << '\n' << indent() << "g_object_unref (xception);" << '\n' << indent() << "return FALSE;" << '\n'; indent_down(); f_service_ << indent() << "} else if (mtype != T_REPLY) {" << '\n'; indent_up(); f_service_ << indent() << "if (fname) g_free (fname);" << '\n' << indent() << "thrift_protocol_skip (protocol, T_STRUCT, " "NULL);" << '\n' << indent() << "thrift_protocol_read_message_end (protocol, " "NULL);" << '\n' << indent() << "thrift_transport_read_end (" "protocol->transport, NULL);" << '\n' << indent() << "g_set_error (error, " "THRIFT_APPLICATION_EXCEPTION_ERROR, " "THRIFT_APPLICATION_EXCEPTION_ERROR_INVALID_MESSAGE_TYPE, " "\"invalid message type %d, expected T_REPLY\", mtype);" << '\n' << indent() << "return FALSE;" << '\n'; indent_down(); f_service_ << indent() << "} else if (strncmp (fname, \"" << name << "\", " << name.length() << ") != 0) {" << '\n'; indent_up(); f_service_ << indent() << "thrift_protocol_skip (protocol, T_STRUCT, " "NULL);" << '\n' << indent() << "thrift_protocol_read_message_end (protocol," "error);" << '\n' << indent() << "thrift_transport_read_end (" "protocol->transport, error);" << '\n' << indent() << "g_set_error (error, " "THRIFT_APPLICATION_EXCEPTION_ERROR, " "THRIFT_APPLICATION_EXCEPTION_ERROR_WRONG_METHOD_NAME, " "\"wrong method name %s, expected " << name << "\", fname);" << '\n' << indent() << "if (fname) g_free (fname);" << '\n' << indent() << "return FALSE;" << '\n'; indent_down(); f_service_ << indent() << "}" << '\n' << indent() << "if (fname) g_free (fname);" << '\n' << '\n'; t_struct* xs = (*f_iter)->get_xceptions(); const std::vector& xceptions = xs->get_members(); vector::const_iterator x_iter; { t_struct result(program_, tservice->get_name() + "_" + (*f_iter)->get_name() + "_result"); t_field success((*f_iter)->get_returntype(), "*_return", 0); if (!(*f_iter)->get_returntype()->is_void()) { result.append(&success); } // add readers for exceptions, dereferencing the pointer. for (x_iter = xceptions.begin(); x_iter != xceptions.end(); x_iter++) { t_field* xception = new t_field((*x_iter)->get_type(), "*" + (*x_iter)->get_name(), (*x_iter)->get_key()); result.append(xception); } generate_struct_reader(f_service_, &result, "", "", false); } f_service_ << indent() << "if (thrift_protocol_read_message_end (protocol, error) < 0)" << '\n' << indent() << " return FALSE;" << '\n' << '\n' << indent() << "if (!thrift_transport_read_end (protocol->transport, error))" << '\n' << indent() << " return FALSE;" << '\n' << '\n'; // copy over any throw exceptions and return failure for (x_iter = xceptions.begin(); x_iter != xceptions.end(); x_iter++) { f_service_ << indent() << "if (*" << (*x_iter)->get_name() << " != NULL)" << '\n' << indent() << "{" << '\n' << indent() << " g_set_error (error, " << this->nspace_uc << to_upper_case(initial_caps_to_underscores((*x_iter)->get_type()->get_name())) << "_ERROR, " << this->nspace_uc << to_upper_case(initial_caps_to_underscores((*x_iter)->get_type()->get_name())) << "_ERROR_CODE, \"" << (*x_iter)->get_type()->get_name() << "\");" << '\n' << indent() << " return FALSE;" << '\n' << indent() << "}" << '\n'; } // Close function indent(f_service_) << "return TRUE;" << '\n'; scope_down(f_service_); f_service_ << '\n'; } // Open function t_function service_function((*f_iter)->get_returntype(), service_name_lc + string("_client_") + funname, (*f_iter)->get_arglist(), (*f_iter)->get_xceptions()); indent(f_service_) << function_signature(&service_function) << '\n'; scope_up(f_service_); // wrap each function f_service_ << indent() << "if (!" << this->nspace_lc << service_name_lc << "_client_send_" << funname << " (iface"; // Declare the function arguments const vector& fields = arg_struct->get_members(); vector::const_iterator fld_iter; for (fld_iter = fields.begin(); fld_iter != fields.end(); ++fld_iter) { f_service_ << ", " << (*fld_iter)->get_name(); } f_service_ << ", error))" << '\n' << indent() << " return FALSE;" << '\n'; // if not oneway, implement recv if (!(*f_iter)->is_oneway()) { string ret = (*f_iter)->get_returntype()->is_void() ? "" : "_return, "; const vector& xceptions = (*f_iter)->get_xceptions()->get_members(); vector::const_iterator x_iter; for (x_iter = xceptions.begin(); x_iter != xceptions.end(); ++x_iter) { ret += (*x_iter)->get_name(); ret += ", "; } f_service_ << indent() << "if (!" << this->nspace_lc << service_name_lc << "_client_recv_" << funname << " (iface, " << ret << "error))" << '\n' << indent() << " return FALSE;" << '\n'; } // return TRUE which means all functions were called OK indent(f_service_) << "return TRUE;" << '\n'; scope_down(f_service_); f_service_ << '\n'; } // create the interface initializer f_service_ << "static void" << '\n' << this->nspace_lc << service_name_lc << "_if_interface_init (" << this->nspace << service_name_ << "IfInterface *iface)" << '\n'; scope_up(f_service_); if (functions.size() > 0) { for (f_iter = functions.begin(); f_iter != functions.end(); ++f_iter) { /* make the function name C friendly */ string funname = initial_caps_to_underscores((*f_iter)->get_name()); f_service_ << indent() << "iface->" << funname << " = " << this->nspace_lc << service_name_lc << "_client_" << funname << ";" << '\n'; } } else { f_service_ << indent() << "THRIFT_UNUSED_VAR (iface);" << '\n'; } scope_down(f_service_); f_service_ << '\n'; // create the client instance initializer f_service_ << "static void" << '\n' << this->nspace_lc << service_name_lc << "_client_init (" << this->nspace << service_name_ << "Client *client)" << '\n'; scope_up(f_service_); if (!extends_service) { f_service_ << indent() << "client->input_protocol = NULL;" << '\n' << indent() << "client->output_protocol = NULL;" << '\n'; } else { f_service_ << indent() << "THRIFT_UNUSED_VAR (client);" << '\n'; } scope_down(f_service_); f_service_ << '\n'; // create the client class initializer f_service_ << "static void" << '\n' << this->nspace_lc << service_name_lc << "_client_class_init (" << this->nspace << service_name_ << "ClientClass *cls)" << '\n' << "{" << '\n'; if (!extends_service) { f_service_ << " GObjectClass *gobject_class = G_OBJECT_CLASS (cls);" << '\n' << " GParamSpec *param_spec;" << '\n' << '\n' << " gobject_class->set_property = " << this->nspace_lc << service_name_lc << "_client_set_property;" << '\n' << " gobject_class->get_property = " << this->nspace_lc << service_name_lc << "_client_get_property;" << '\n' << '\n' << " param_spec = g_param_spec_object (\"input_protocol\"," << '\n' << " \"input protocol (construct)\"," << '\n' << " \"Set the client input protocol\"," << '\n' << " THRIFT_TYPE_PROTOCOL," << '\n' << " G_PARAM_READWRITE);" << '\n' << " g_object_class_install_property (gobject_class," << '\n' << " PROP_" << this->nspace_uc << service_name_uc << "_CLIENT_INPUT_PROTOCOL, param_spec);" << '\n' << '\n' << " param_spec = g_param_spec_object (\"output_protocol\"," << '\n' << " \"output protocol (construct)\"," << '\n' << " \"Set the client output protocol\"," << '\n' << " THRIFT_TYPE_PROTOCOL," << '\n' << " G_PARAM_READWRITE);" << '\n' << " g_object_class_install_property (gobject_class," << '\n' << " PROP_" << this->nspace_uc << service_name_uc << "_CLIENT_OUTPUT_PROTOCOL, param_spec);" << '\n'; } else { f_service_ << " THRIFT_UNUSED_VAR (cls);" << '\n'; } f_service_ << "}" << '\n' << '\n'; } /** * Generates C code that represents a Thrift service handler. * * @param tservice The service for which to generate a handler. */ void t_c_glib_generator::generate_service_handler(t_service* tservice) { vector functions = tservice->get_functions(); vector::const_iterator function_iter; string service_name_lc = to_lower_case(initial_caps_to_underscores(service_name_)); string service_name_uc = to_upper_case(service_name_lc); string service_handler_name = service_name_ + "Handler"; string class_name = this->nspace + service_handler_name; string class_name_lc = this->nspace_lc + initial_caps_to_underscores(service_handler_name); string class_name_uc = to_upper_case(class_name_lc); string parent_class_name; string parent_type_name; string args_indent; // The service this service extends, or nullptr if it extends no service t_service* extends_service = tservice->get_extends(); // Determine the name of our parent service (if any) and the handler class' // parent class name and type if (extends_service) { string parent_service_name = extends_service->get_name(); string parent_service_name_lc = to_lower_case(initial_caps_to_underscores(parent_service_name)); string parent_service_name_uc = to_upper_case(parent_service_name_lc); parent_class_name = this->nspace + parent_service_name + "Handler"; parent_type_name = this->nspace_uc + "TYPE_" + parent_service_name_uc + "_HANDLER"; } else { parent_class_name = "GObject"; parent_type_name = "G_TYPE_OBJECT"; } // Generate the handler class' definition in the header file // Generate the handler instance definition f_header_ << "/* " << service_name_ << " handler (abstract base class) */" << '\n' << "struct _" << class_name << '\n' << "{" << '\n'; indent_up(); f_header_ << indent() << parent_class_name << " parent;" << '\n'; indent_down(); f_header_ << "};" << '\n' << "typedef struct _" << class_name << " " << class_name << ";" << '\n' << '\n'; // Generate the handler class definition, including its class members // (methods) f_header_ << "struct _" << class_name << "Class" << '\n' << "{" << '\n'; indent_up(); f_header_ << indent() << parent_class_name << "Class parent;" << '\n' << '\n'; for (function_iter = functions.begin(); function_iter != functions.end(); ++function_iter) { string method_name = initial_caps_to_underscores((*function_iter)->get_name()); t_type* return_type = (*function_iter)->get_returntype(); t_struct* arg_list = (*function_iter)->get_arglist(); t_struct* x_list = (*function_iter)->get_xceptions(); bool has_return = !return_type->is_void(); bool has_args = arg_list->get_members().size() == 0; bool has_xceptions = x_list->get_members().size() == 0; string params = "(" + this->nspace + service_name_ + "If *iface" + (has_return ? ", " + type_name(return_type) + "* _return" : "") + (has_args ? "" : (", " + argument_list(arg_list))) + (has_xceptions ? "" : (", " + xception_list(x_list))) + ", GError **error)"; indent(f_header_) << "gboolean (*" << method_name << ") " << params << ";" << '\n'; } indent_down(); f_header_ << "};" << '\n' << "typedef struct _" << class_name << "Class " << class_name << "Class;" << '\n' << '\n'; // Generate the remaining header boilerplate f_header_ << "GType " << class_name_lc << "_get_type (void);" << '\n' << "#define " << this->nspace_uc << "TYPE_" << service_name_uc << "_HANDLER " << "(" << class_name_lc << "_get_type())" << '\n' << "#define " << class_name_uc << "(obj) " << "(G_TYPE_CHECK_INSTANCE_CAST ((obj), " << this->nspace_uc << "TYPE_" << service_name_uc << "_HANDLER, " << class_name << "))" << '\n' << "#define " << this->nspace_uc << "IS_" << service_name_uc << "_HANDLER(obj) " << "(G_TYPE_CHECK_INSTANCE_TYPE ((obj), " << this->nspace_uc << "TYPE_" << service_name_uc << "_HANDLER))" << '\n' << "#define " << class_name_uc << "_CLASS(c) (G_TYPE_CHECK_CLASS_CAST ((c), " << this->nspace_uc << "TYPE_" << service_name_uc << "_HANDLER, " << class_name << "Class))" << '\n' << "#define " << this->nspace_uc << "IS_" << service_name_uc << "_HANDLER_CLASS(c) " << "(G_TYPE_CHECK_CLASS_TYPE ((c), " << this->nspace_uc << "TYPE_" << service_name_uc << "_HANDLER))" << '\n' << "#define " << this->nspace_uc << service_name_uc << "_HANDLER_GET_CLASS(obj) " << "(G_TYPE_INSTANCE_GET_CLASS ((obj), " << this->nspace_uc << "TYPE_" << service_name_uc << "_HANDLER, " << class_name << "Class))" << '\n' << '\n'; // Generate the handler class' method definitions for (function_iter = functions.begin(); function_iter != functions.end(); ++function_iter) { string method_name = initial_caps_to_underscores((*function_iter)->get_name()); t_type* return_type = (*function_iter)->get_returntype(); t_struct* arg_list = (*function_iter)->get_arglist(); t_struct* x_list = (*function_iter)->get_xceptions(); bool has_return = !return_type->is_void(); bool has_args = arg_list->get_members().size() == 0; bool has_xceptions = x_list->get_members().size() == 0; string params = "(" + this->nspace + service_name_ + "If *iface" + (has_return ? ", " + type_name(return_type) + "* _return" : "") + (has_args ? "" : (", " + argument_list(arg_list))) + (has_xceptions ? "" : (", " + xception_list(x_list))) + ", GError **error)"; f_header_ << "gboolean " << class_name_lc << "_" << method_name << " " << params << ";" << '\n'; } f_header_ << '\n'; // Generate the handler's implementation in the implementation file // Generate the implementation boilerplate f_service_ << "static void" << '\n' << class_name_lc << "_" << service_name_lc << "_if_interface_init (" << this->nspace << service_name_ << "IfInterface *iface);" << '\n' << '\n'; args_indent = string(25, ' '); f_service_ << "G_DEFINE_TYPE_WITH_CODE (" << class_name << ", " << '\n' << args_indent << class_name_lc << "," << '\n' << args_indent << parent_type_name << "," << '\n' << args_indent << "G_IMPLEMENT_INTERFACE (" << this->nspace_uc << "TYPE_" << service_name_uc << "_IF," << '\n'; args_indent += string(23, ' '); f_service_ << args_indent << class_name_lc << "_" << service_name_lc << "_if_interface_init))" << '\n' << '\n'; // Generate the handler method implementations for (function_iter = functions.begin(); function_iter != functions.end(); ++function_iter) { string function_name = (*function_iter)->get_name(); string method_name = initial_caps_to_underscores(function_name); t_type* return_type = (*function_iter)->get_returntype(); t_struct* arg_list = (*function_iter)->get_arglist(); t_struct* x_list = (*function_iter)->get_xceptions(); const vector& args = arg_list->get_members(); const vector& xceptions = x_list->get_members(); vector::const_iterator field_iter; t_function implementing_function(return_type, service_name_lc + "_handler_" + method_name, arg_list, x_list, (*function_iter)->is_oneway()); indent(f_service_) << function_signature(&implementing_function) << '\n'; scope_up(f_service_); f_service_ << indent() << "g_return_val_if_fail (" << this->nspace_uc << "IS_" << service_name_uc << "_HANDLER (iface), FALSE);" << '\n' << '\n' << indent() << "return " << class_name_uc << "_GET_CLASS (iface)" << "->" << method_name << " (iface, "; if (!return_type->is_void()) { f_service_ << "_return, "; } for (field_iter = args.begin(); field_iter != args.end(); ++field_iter) { f_service_ << (*field_iter)->get_name() << ", "; } for (field_iter = xceptions.begin(); field_iter != xceptions.end(); ++field_iter) { f_service_ << (*field_iter)->get_name() << ", "; } f_service_ << "error);" << '\n'; scope_down(f_service_); f_service_ << '\n'; } // Generate the handler interface initializer f_service_ << "static void" << '\n' << class_name_lc << "_" << service_name_lc << "_if_interface_init (" << this->nspace << service_name_ << "IfInterface *iface)" << '\n'; scope_up(f_service_); if (functions.size() > 0) { for (function_iter = functions.begin(); function_iter != functions.end(); ++function_iter) { string method_name = initial_caps_to_underscores((*function_iter)->get_name()); f_service_ << indent() << "iface->" << method_name << " = " << class_name_lc << "_" << method_name << ";" << '\n'; } } else { f_service_ << "THRIFT_UNUSED_VAR (iface);" << '\n'; } scope_down(f_service_); f_service_ << '\n'; // Generate the handler instance initializer f_service_ << "static void" << '\n' << class_name_lc << "_init (" << class_name << " *self)" << '\n'; scope_up(f_service_); f_service_ << indent() << "THRIFT_UNUSED_VAR (self);" << '\n'; scope_down(f_service_); f_service_ << '\n'; // Generate the handler class initializer f_service_ << "static void" << '\n' << class_name_lc << "_class_init (" << class_name << "Class *cls)" << '\n'; scope_up(f_service_); if (functions.size() > 0) { for (function_iter = functions.begin(); function_iter != functions.end(); ++function_iter) { string function_name = (*function_iter)->get_name(); string method_name = initial_caps_to_underscores(function_name); // All methods are pure virtual and must be implemented by subclasses f_service_ << indent() << "cls->" << method_name << " = NULL;" << '\n'; } } else { f_service_ << indent() << "THRIFT_UNUSED_VAR (cls);" << '\n'; } scope_down(f_service_); f_service_ << '\n'; } /** * Generates C code that represents a Thrift service processor. * * @param tservice The service for which to generate a processor */ void t_c_glib_generator::generate_service_processor(t_service* tservice) { vector functions = tservice->get_functions(); vector::const_iterator function_iter; string service_name_lc = to_lower_case(initial_caps_to_underscores(service_name_)); string service_name_uc = to_upper_case(service_name_lc); string service_processor_name = service_name_ + "Processor"; string class_name = this->nspace + service_processor_name; string class_name_lc = this->nspace_lc + initial_caps_to_underscores(service_processor_name); string class_name_uc = to_upper_case(class_name_lc); string parent_class_name; string parent_type_name; string handler_class_name = this->nspace + service_name_ + "Handler"; string handler_class_name_lc = initial_caps_to_underscores(handler_class_name); string process_function_type_name = class_name + "ProcessFunction"; string process_function_def_type_name = class_name_lc + "_process_function_def"; string function_name; string args_indent; // The service this service extends, or nullptr if it extends no service t_service* extends_service = tservice->get_extends(); // Determine the name of our parent service (if any) and the // processor class' parent class name and type if (extends_service) { string parent_service_name = extends_service->get_name(); string parent_service_name_lc = to_lower_case(initial_caps_to_underscores(parent_service_name)); string parent_service_name_uc = to_upper_case(parent_service_name_lc); parent_class_name = this->nspace + parent_service_name + "Processor"; parent_type_name = this->nspace_uc + "TYPE_" + parent_service_name_uc + "_PROCESSOR"; } else { parent_class_name = "ThriftDispatchProcessor"; parent_type_name = "THRIFT_TYPE_DISPATCH_PROCESSOR"; } // Generate the processor class' definition in the header file // Generate the processor instance definition f_header_ << "/* " << service_name_ << " processor */" << '\n' << "struct _" << class_name << '\n' << "{" << '\n'; indent_up(); f_header_ << indent() << parent_class_name << " parent;" << '\n' << '\n' << indent() << "/* protected */" << '\n' << indent() << this->nspace + service_name_ + "Handler *handler;" << '\n' << indent() << "GHashTable *process_map;" << '\n'; indent_down(); f_header_ << "};" << '\n' << "typedef struct _" << class_name << " " << class_name << ";" << '\n' << '\n'; // Generate the processor class definition f_header_ << "struct _" << class_name << "Class" << '\n' << "{" << '\n'; indent_up(); f_header_ << indent() << parent_class_name << "Class parent;" << '\n' << '\n' << indent() << "/* protected */" << '\n' << indent() << "gboolean (*dispatch_call) (ThriftDispatchProcessor *processor," << '\n'; args_indent = indent() + string(27, ' '); f_header_ << args_indent << "ThriftProtocol *in," << '\n' << args_indent << "ThriftProtocol *out," << '\n' << args_indent << "gchar *fname," << '\n' << args_indent << "gint32 seqid," << '\n' << args_indent << "GError **error);" << '\n'; indent_down(); f_header_ << "};" << '\n' << "typedef struct _" << class_name << "Class " << class_name << "Class;" << '\n' << '\n'; // Generate the remaining header boilerplate f_header_ << "GType " << class_name_lc << "_get_type (void);" << '\n' << "#define " << this->nspace_uc << "TYPE_" << service_name_uc << "_PROCESSOR " << "(" << class_name_lc << "_get_type())" << '\n' << "#define " << class_name_uc << "(obj) " << "(G_TYPE_CHECK_INSTANCE_CAST ((obj), " << this->nspace_uc << "TYPE_" << service_name_uc << "_PROCESSOR, " << class_name << "))" << '\n' << "#define " << this->nspace_uc << "IS_" << service_name_uc << "_PROCESSOR(obj) " << "(G_TYPE_CHECK_INSTANCE_TYPE ((obj), " << this->nspace_uc << "TYPE_" << service_name_uc << "_PROCESSOR))" << '\n' << "#define " << class_name_uc << "_CLASS(c) (G_TYPE_CHECK_CLASS_CAST ((c), " << this->nspace_uc << "TYPE_" << service_name_uc << "_PROCESSOR, " << class_name << "Class))" << '\n' << "#define " << this->nspace_uc << "IS_" << service_name_uc << "_PROCESSOR_CLASS(c) " << "(G_TYPE_CHECK_CLASS_TYPE ((c), " << this->nspace_uc << "TYPE_" << service_name_uc << "_PROCESSOR))" << '\n' << "#define " << this->nspace_uc << service_name_uc << "_PROCESSOR_GET_CLASS(obj) " << "(G_TYPE_INSTANCE_GET_CLASS ((obj), " << this->nspace_uc << "TYPE_" << service_name_uc << "_PROCESSOR, " << class_name << "Class))" << '\n' << '\n'; // Generate the processor's implementation in the implementation file // Generate the processor's properties enum f_service_ << "enum _" << class_name << "Properties" << '\n' << "{" << '\n'; indent_up(); f_service_ << indent() << "PROP_" << class_name_uc << "_0," << '\n' << indent() << "PROP_" << class_name_uc << "_HANDLER" << '\n'; indent_down(); f_service_ << "};" << '\n' << '\n'; // Generate the implementation boilerplate args_indent = string(15, ' '); f_service_ << "G_DEFINE_TYPE (" << class_name << "," << '\n' << args_indent << class_name_lc << "," << '\n' << args_indent << parent_type_name << ")" << '\n' << '\n'; // Generate the processor's processing-function type args_indent = string(process_function_type_name.length() + 23, ' '); f_service_ << "typedef gboolean (* " << process_function_type_name << ") (" << class_name << " *, " << '\n' << args_indent << "gint32," << '\n' << args_indent << "ThriftProtocol *," << '\n' << args_indent << "ThriftProtocol *," << '\n' << args_indent << "GError **);" << '\n' << '\n'; // Generate the processor's processing-function-definition type f_service_ << "typedef struct" << '\n' << "{" << '\n'; indent_up(); f_service_ << indent() << "gchar *name;" << '\n' << indent() << process_function_type_name << " function;" << '\n'; indent_down(); f_service_ << "} " << process_function_def_type_name << ";" << '\n' << '\n'; // Generate forward declarations of the processor's processing functions so we // can refer to them in the processing-function-definition struct below and // keep all of the processor's declarations in one place for (function_iter = functions.begin(); function_iter != functions.end(); ++function_iter) { function_name = class_name_lc + "_process_" + initial_caps_to_underscores((*function_iter)->get_name()); args_indent = string(function_name.length() + 2, ' '); f_service_ << "static gboolean" << '\n' << function_name << " (" << class_name << " *," << '\n' << args_indent << "gint32," << '\n' << args_indent << "ThriftProtocol *," << '\n' << args_indent << "ThriftProtocol *," << '\n' << args_indent << "GError **);" << '\n'; } f_service_ << '\n'; // Generate the processor's processing-function definitions, if the service // defines any methods if (functions.size() > 0) { f_service_ << indent() << "static " << process_function_def_type_name << '\n' << indent() << class_name_lc << "_process_function_defs[" << functions.size() << "] = {" << '\n'; indent_up(); for (function_iter = functions.begin(); function_iter != functions.end(); ++function_iter) { string service_function_name = (*function_iter)->get_name(); string process_function_name = class_name_lc + "_process_" + initial_caps_to_underscores(service_function_name); f_service_ << indent() << "{" << '\n'; indent_up(); f_service_ << indent() << "\"" << service_function_name << "\"," << '\n' << indent() << process_function_name << '\n'; indent_down(); f_service_ << indent() << "}" << (function_iter == --functions.end() ? "" : ",") << '\n'; } indent_down(); f_service_ << indent() << "};" << '\n' << '\n'; } // Generate the processor's processing functions for (function_iter = functions.begin(); function_iter != functions.end(); ++function_iter) { string service_function_name = (*function_iter)->get_name(); string service_function_name_ic = underscores_to_initial_caps(service_function_name); string service_function_name_lc = initial_caps_to_underscores(service_function_name); string service_function_name_uc = to_upper_case(service_function_name_lc); t_type* return_type = (*function_iter)->get_returntype(); bool has_return_value = !return_type->is_void(); t_struct* arg_list = (*function_iter)->get_arglist(); const vector& args = arg_list->get_members(); vector::const_iterator arg_iter; const vector& xceptions = (*function_iter)->get_xceptions()->get_members(); vector::const_iterator xception_iter; string args_class_name = this->nspace + service_name_ + service_function_name_ic + "Args"; string args_class_type = this->nspace_uc + "TYPE_" + service_name_uc + "_" + service_function_name_uc + "_ARGS"; string result_class_name = this->nspace + service_name_ + service_function_name_ic + "Result"; string result_class_type = this->nspace_uc + "TYPE_" + service_name_uc + "_" + service_function_name_uc + "_RESULT"; string handler_function_name = handler_class_name_lc + "_" + service_function_name_lc; function_name = class_name_lc + "_process_" + initial_caps_to_underscores(service_function_name); args_indent = string(function_name.length() + 2, ' '); f_service_ << "static gboolean" << '\n' << function_name << " (" << class_name << " *self," << '\n' << args_indent << "gint32 sequence_id," << '\n' << args_indent << "ThriftProtocol *input_protocol," << '\n' << args_indent << "ThriftProtocol *output_protocol," << '\n' << args_indent << "GError **error)" << '\n'; scope_up(f_service_); f_service_ << indent() << "gboolean result = TRUE;" << '\n' << indent() << "ThriftTransport * transport;" << '\n' << indent() << "ThriftApplicationException *xception;" << '\n' << indent() << args_class_name + " * args =" << '\n'; indent_up(); f_service_ << indent() << "g_object_new (" << args_class_type << ", NULL);" << '\n' << '\n'; indent_down(); if ((*function_iter)->is_oneway()) { f_service_ << indent() << "THRIFT_UNUSED_VAR (sequence_id);" << '\n' << indent() << "THRIFT_UNUSED_VAR (output_protocol);" << '\n' << '\n'; } f_service_ << indent() << "g_object_get (input_protocol, \"transport\", " << "&transport, NULL);" << '\n' << '\n'; // Read the method's arguments from the caller f_service_ << indent() << "if ((thrift_struct_read (THRIFT_STRUCT (args), " << "input_protocol, error) != -1) &&" << '\n' << indent() << " (thrift_protocol_read_message_end (input_protocol, " << "error) != -1) &&" << '\n' << indent() << " (thrift_transport_read_end (transport, error) != FALSE))" << '\n'; scope_up(f_service_); for (arg_iter = args.begin(); arg_iter != args.end(); ++arg_iter) { f_service_ << indent() << property_type_name((*arg_iter)->get_type()) << " " << (*arg_iter)->get_name() << ";" << '\n'; } for (xception_iter = xceptions.begin(); xception_iter != xceptions.end(); ++xception_iter) { f_service_ << indent() << type_name((*xception_iter)->get_type()) << " " << initial_caps_to_underscores((*xception_iter)->get_name()) << " = NULL;" << '\n'; } if (has_return_value) { f_service_ << indent() << property_type_name(return_type) << " return_value;" << '\n'; } if (!(*function_iter)->is_oneway()) { f_service_ << indent() << result_class_name << " * result_struct;" << '\n'; } f_service_ << '\n'; if (args.size() > 0) { f_service_ << indent() << "g_object_get (args," << '\n'; args_indent = indent() + string(14, ' '); for (arg_iter = args.begin(); arg_iter != args.end(); ++arg_iter) { string arg_name = (*arg_iter)->get_name(); f_service_ << args_indent << "\"" << arg_name << "\", &" << arg_name << "," << '\n'; } f_service_ << args_indent << "NULL);" << '\n' << '\n'; } if (!(*function_iter)->is_oneway()) { f_service_ << indent() << "g_object_unref (transport);" << '\n' << indent() << "g_object_get (output_protocol, \"transport\", " << "&transport, NULL);" << '\n' << '\n' << indent() << "result_struct = g_object_new (" << result_class_type << ", NULL);" << '\n'; if (has_return_value) { f_service_ << indent() << "g_object_get (result_struct, " "\"success\", &return_value, NULL);" << '\n'; } f_service_ << '\n'; } // Pass the arguments to the corresponding method in the handler f_service_ << indent() << "if (" << handler_function_name << " (" << this->nspace_uc << service_name_uc << "_IF (self->handler)," << '\n'; args_indent = indent() + string(handler_function_name.length() + 6, ' '); if (has_return_value) { string return_type_name = type_name(return_type); f_service_ << args_indent; // Cast return_value if it was declared as a type other than the return // value's actual type---this is true for integer values 32 bits or fewer // in width, for which GLib requires a plain gint type be used when // storing or retrieving as an object property if (return_type_name != property_type_name(return_type)) { if (return_type_name[return_type_name.length() - 1] != '*') { return_type_name += ' '; } return_type_name += '*'; f_service_ << "(" << return_type_name << ")"; } f_service_ << "&return_value," << '\n'; } for (arg_iter = args.begin(); arg_iter != args.end(); ++arg_iter) { f_service_ << args_indent << (*arg_iter)->get_name() << "," << '\n'; } for (xception_iter = xceptions.begin(); xception_iter != xceptions.end(); ++xception_iter) { f_service_ << args_indent << "&" << initial_caps_to_underscores((*xception_iter)->get_name()) << "," << '\n'; } f_service_ << args_indent << "error) == TRUE)" << '\n'; scope_up(f_service_); // The handler reported success; return the result, if any, to the caller if (!(*function_iter)->is_oneway()) { if (has_return_value) { f_service_ << indent() << "g_object_set (result_struct, \"success\", "; if (type_name(return_type) != property_type_name(return_type)) { // Roundtrip cast to fix the position of sign bit. f_service_ << "(" << property_type_name(return_type) << ")" << "(" << type_name(return_type) << ")"; } f_service_ << "return_value, " << "NULL);" << '\n'; f_service_ << '\n'; } f_service_ << indent() << "result =" << '\n'; indent_up(); f_service_ << indent() << "((thrift_protocol_write_message_begin (output_protocol," << '\n'; args_indent = indent() + string(39, ' '); f_service_ << args_indent << "\"" << service_function_name << "\"," << '\n' << args_indent << "T_REPLY," << '\n' << args_indent << "sequence_id," << '\n' << args_indent << "error) != -1) &&" << '\n' << indent() << " (thrift_struct_write (THRIFT_STRUCT (result_struct)," << '\n'; args_indent = indent() + string(23, ' '); f_service_ << args_indent << "output_protocol," << '\n' << args_indent << "error) != -1));" << '\n'; indent_down(); } scope_down(f_service_); f_service_ << indent() << "else" << '\n'; scope_up(f_service_); // The handler reported failure; check to see if an application-defined // exception was raised and if so, return it to the caller f_service_ << indent(); if (xceptions.size() > 0) { for (xception_iter = xceptions.begin(); xception_iter != xceptions.end(); ++xception_iter) { f_service_ << "if (" << initial_caps_to_underscores((*xception_iter)->get_name()) << " != NULL)" << '\n'; scope_up(f_service_); f_service_ << indent() << "g_object_set (result_struct," << '\n'; args_indent = indent() + string(14, ' '); f_service_ << args_indent << "\"" << (*xception_iter)->get_name() << "\", " << (*xception_iter)->get_name() << "," << '\n' << args_indent << "NULL);" << '\n' << '\n'; f_service_ << indent() << "g_object_unref ("<< (*xception_iter)->get_name() <<");"<< '\n'; f_service_ << indent() << "result =" << '\n'; indent_up(); f_service_ << indent() << "((thrift_protocol_write_message_begin (output_protocol," << '\n'; args_indent = indent() + string(39, ' '); f_service_ << args_indent << "\"" << service_function_name << "\"," << '\n' << args_indent << "T_REPLY," << '\n' << args_indent << "sequence_id," << '\n' << args_indent << "error) != -1) &&" << '\n' << indent() << " (thrift_struct_write (THRIFT_STRUCT (result_struct)," << '\n'; args_indent = indent() + string(23, ' '); f_service_ << args_indent << "output_protocol," << '\n' << args_indent << "error) != -1));" << '\n'; indent_down(); scope_down(f_service_); f_service_ << indent() << "else" << '\n'; } scope_up(f_service_); f_service_ << indent(); } // If the handler reported failure but raised no application-defined // exception, return a Thrift application exception with the information // returned via GLib's own error-reporting mechanism f_service_ << "if (*error == NULL)" << '\n'; indent_up(); f_service_ << indent() << "g_warning (\"" << service_name_ << "." << (*function_iter)->get_name() << " implementation returned FALSE \"" << '\n' << indent() << string(11, ' ') << "\"but did not set an error\");" << '\n' << '\n'; indent_down(); f_service_ << indent() << "xception =" << '\n'; indent_up(); f_service_ << indent() << "g_object_new (THRIFT_TYPE_APPLICATION_EXCEPTION," << '\n'; args_indent = indent() + string(14, ' '); f_service_ << args_indent << "\"type\", *error != NULL ? (*error)->code :" << '\n' << args_indent << string(11, ' ') << "THRIFT_APPLICATION_EXCEPTION_ERROR_UNKNOWN," << '\n' << args_indent << "\"message\", *error != NULL ? (*error)->message : NULL," << '\n' << args_indent << "NULL);" << '\n'; indent_down(); f_service_ << indent() << "g_clear_error (error);" << '\n' << '\n' << indent() << "result =" << '\n'; indent_up(); f_service_ << indent() << "((thrift_protocol_write_message_begin (output_protocol," << '\n'; args_indent = indent() + string(39, ' '); f_service_ << args_indent << "\"" << service_function_name << "\"," << '\n' << args_indent << "T_EXCEPTION," << '\n' << args_indent << "sequence_id," << '\n' << args_indent << "error) != -1) &&" << '\n' << indent() << " (thrift_struct_write (THRIFT_STRUCT (xception)," << '\n'; args_indent = indent() + string(23, ' '); f_service_ << args_indent << "output_protocol," << '\n' << args_indent << "error) != -1));" << '\n'; indent_down(); f_service_ << '\n' << indent() << "g_object_unref (xception);" << '\n'; if (xceptions.size() > 0) { scope_down(f_service_); } scope_down(f_service_); f_service_ << '\n'; // Dellocate or unref retrieved argument values as necessary for (arg_iter = args.begin(); arg_iter != args.end(); ++arg_iter) { string arg_name = (*arg_iter)->get_name(); t_type* arg_type = get_true_type((*arg_iter)->get_type()); if (arg_type->is_base_type()) { t_base_type* base_type = ((t_base_type*)arg_type); if (base_type->get_base() == t_base_type::TYPE_STRING) { f_service_ << indent() << "if (" << arg_name << " != NULL)" << '\n'; indent_up(); if (base_type->is_binary()) { f_service_ << indent() << "g_byte_array_unref (" << arg_name << ");" << '\n'; } else { f_service_ << indent() << "g_free (" << arg_name << ");" << '\n'; } indent_down(); } } else if (arg_type->is_container()) { f_service_ << indent() << "if (" << arg_name << " != NULL)" << '\n'; indent_up(); if (arg_type->is_list()) { t_type* elem_type = ((t_list*)arg_type)->get_elem_type(); f_service_ << indent(); if (is_numeric(elem_type)) { f_service_ << "g_array_unref"; } else { f_service_ << "g_ptr_array_unref"; } f_service_ << " (" << arg_name << ");" << '\n'; } else if (arg_type->is_map() || arg_type->is_set()) { f_service_ << indent() << "g_hash_table_unref (" << arg_name << ");" << '\n'; } indent_down(); } else if (arg_type->is_struct()) { f_service_ << indent() << "if (" << arg_name << " != NULL)" << '\n'; indent_up(); f_service_ << indent() << "g_object_unref (" << arg_name << ");" << '\n'; indent_down(); } } if (!(*function_iter)->is_oneway()) { if (has_return_value) { // Deallocate (or unref) return_value return_type = get_true_type(return_type); if (return_type->is_base_type()) { t_base_type* base_type = ((t_base_type*)return_type); if (base_type->get_base() == t_base_type::TYPE_STRING) { f_service_ << indent() << "if (return_value != NULL)" << '\n'; indent_up(); if (base_type->is_binary()) { f_service_ << indent() << "g_byte_array_unref (return_value);" << '\n'; } else { f_service_ << indent() << "g_free (return_value);" << '\n'; } indent_down(); } } else if (return_type->is_container()) { f_service_ << indent() << "if (return_value != NULL)" << '\n'; indent_up(); if (return_type->is_list()) { t_type* elem_type = ((t_list*)return_type)->get_elem_type(); f_service_ << indent(); if (is_numeric(elem_type)) { f_service_ << "g_array_unref"; } else { f_service_ << "g_ptr_array_unref"; } f_service_ << " (return_value);" << '\n'; } else if (return_type->is_map() || return_type->is_set()) { f_service_ << indent() << "g_hash_table_unref (return_value);" << '\n'; } indent_down(); } else if (return_type->is_struct()) { f_service_ << indent() << "if (return_value != NULL)" << '\n'; indent_up(); f_service_ << indent() << "g_object_unref (return_value);" << '\n'; indent_down(); } } f_service_ << indent() << "g_object_unref (result_struct);" << '\n' << '\n' << indent() << "if (result == TRUE)" << '\n'; indent_up(); f_service_ << indent() << "result =" << '\n'; indent_up(); f_service_ << indent() << "((thrift_protocol_write_message_end " << "(output_protocol, error) != -1) &&" << '\n' << indent() << " (thrift_transport_write_end (transport, error) " << "!= FALSE) &&" << '\n' << indent() << " (thrift_transport_flush (transport, error) " << "!= FALSE));" << '\n'; indent_down(); indent_down(); } scope_down(f_service_); f_service_ << indent() << "else" << '\n'; indent_up(); f_service_ << indent() << "result = FALSE;" << '\n'; indent_down(); f_service_ << '\n' << indent() << "g_object_unref (transport);" << '\n' << indent() << "g_object_unref (args);" << '\n' << '\n' << indent() << "return result;" << '\n'; scope_down(f_service_); f_service_ << '\n'; } // Generate the processor's dispatch_call implementation function_name = class_name_lc + "_dispatch_call"; args_indent = indent() + string(function_name.length() + 2, ' '); f_service_ << "static gboolean" << '\n' << function_name << " (ThriftDispatchProcessor *dispatch_processor," << '\n' << args_indent << "ThriftProtocol *input_protocol," << '\n' << args_indent << "ThriftProtocol *output_protocol," << '\n' << args_indent << "gchar *method_name," << '\n' << args_indent << "gint32 sequence_id," << '\n' << args_indent << "GError **error)" << '\n'; scope_up(f_service_); f_service_ << indent() << class_name_lc << "_process_function_def *" << "process_function_def;" << '\n'; f_service_ << indent() << "gboolean dispatch_result = FALSE;" << '\n' << '\n' << indent() << class_name << " *self = " << class_name_uc << " (dispatch_processor);" << '\n'; f_service_ << indent() << parent_class_name << "Class " "*parent_class =" << '\n'; indent_up(); f_service_ << indent() << "g_type_class_peek_parent (" << class_name_uc << "_GET_CLASS (self));" << '\n'; indent_down(); f_service_ << '\n' << indent() << "process_function_def = " << "g_hash_table_lookup (self->process_map, method_name);" << '\n' << indent() << "if (process_function_def != NULL)" << '\n'; scope_up(f_service_); args_indent = indent() + string(53, ' '); f_service_ << indent() << "g_free (method_name);" << '\n' << indent() << "dispatch_result = " << "(*process_function_def->function) (self," << '\n' << args_indent << "sequence_id," << '\n' << args_indent << "input_protocol," << '\n' << args_indent << "output_protocol," << '\n' << args_indent << "error);" << '\n'; scope_down(f_service_); f_service_ << indent() << "else" << '\n'; scope_up(f_service_); // Method name not recognized; chain up to our parent processor---note the // top-most implementation of this method, in ThriftDispatchProcessor itself, // will return an application exception to the caller if no class in the // hierarchy recognizes the method name f_service_ << indent() << "dispatch_result = parent_class->dispatch_call " "(dispatch_processor," << '\n'; args_indent = indent() + string(47, ' '); f_service_ << args_indent << "input_protocol," << '\n' << args_indent << "output_protocol," << '\n' << args_indent << "method_name," << '\n' << args_indent << "sequence_id," << '\n' << args_indent << "error);" << '\n'; scope_down(f_service_); f_service_ << '\n' << indent() << "return dispatch_result;" << '\n'; scope_down(f_service_); f_service_ << '\n'; // Generate the processor's property setter function_name = class_name_lc + "_set_property"; args_indent = string(function_name.length() + 2, ' '); f_service_ << "static void" << '\n' << function_name << " (GObject *object," << '\n' << args_indent << "guint property_id," << '\n' << args_indent << "const GValue *value," << '\n' << args_indent << "GParamSpec *pspec)" << '\n'; scope_up(f_service_); f_service_ << indent() << class_name << " *self = " << class_name_uc << " (object);" << '\n' << '\n' << indent() << "switch (property_id)" << '\n'; scope_up(f_service_); f_service_ << indent() << "case PROP_" << class_name_uc << "_HANDLER:" << '\n'; indent_up(); f_service_ << indent() << "if (self->handler != NULL)" << '\n'; indent_up(); f_service_ << indent() << "g_object_unref (self->handler);" << '\n'; indent_down(); f_service_ << indent() << "self->handler = g_value_get_object (value);" << '\n' << indent() << "g_object_ref (self->handler);" << '\n'; if (extends_service) { // Chain up to set the handler in every superclass as well f_service_ << '\n' << indent() << "G_OBJECT_CLASS (" << class_name_lc << "_parent_class)->" << '\n'; indent_up(); f_service_ << indent() << "set_property (object, property_id, value, pspec);" << '\n'; indent_down(); } f_service_ << indent() << "break;" << '\n'; indent_down(); f_service_ << indent() << "default:" << '\n'; indent_up(); f_service_ << indent() << "G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);" << '\n' << indent() << "break;" << '\n'; indent_down(); scope_down(f_service_); scope_down(f_service_); f_service_ << '\n'; // Generate processor's property getter function_name = class_name_lc + "_get_property"; args_indent = string(function_name.length() + 2, ' '); f_service_ << "static void" << '\n' << function_name << " (GObject *object," << '\n' << args_indent << "guint property_id," << '\n' << args_indent << "GValue *value," << '\n' << args_indent << "GParamSpec *pspec)" << '\n'; scope_up(f_service_); f_service_ << indent() << class_name << " *self = " << class_name_uc << " (object);" << '\n' << '\n' << indent() << "switch (property_id)" << '\n'; scope_up(f_service_); f_service_ << indent() << "case PROP_" << class_name_uc << "_HANDLER:" << '\n'; indent_up(); f_service_ << indent() << "g_value_set_object (value, self->handler);" << '\n' << indent() << "break;" << '\n'; indent_down(); f_service_ << indent() << "default:" << '\n'; indent_up(); f_service_ << indent() << "G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);" << '\n' << indent() << "break;" << '\n'; indent_down(); scope_down(f_service_); scope_down(f_service_); f_service_ << '\n'; // Generator the processor's dispose function f_service_ << "static void" << '\n' << class_name_lc << "_dispose (GObject *gobject)" << '\n'; scope_up(f_service_); f_service_ << indent() << class_name << " *self = " << class_name_uc << " (gobject);" << '\n' << '\n' << indent() << "if (self->handler != NULL)" << '\n'; scope_up(f_service_); f_service_ << indent() << "g_object_unref (self->handler);" << '\n' << indent() << "self->handler = NULL;" << '\n'; scope_down(f_service_); f_service_ << '\n' << indent() << "G_OBJECT_CLASS (" << class_name_lc << "_parent_class)" "->dispose (gobject);" << '\n'; scope_down(f_service_); f_service_ << '\n'; // Generate processor finalize function f_service_ << "static void" << '\n' << class_name_lc << "_finalize (GObject *gobject)" << '\n'; scope_up(f_service_); f_service_ << indent() << this->nspace << service_name_ << "Processor *self = " << this->nspace_uc << service_name_uc << "_PROCESSOR (gobject);" << '\n' << '\n' << indent() << "thrift_safe_hash_table_destroy (self->process_map);" << '\n' << '\n' << indent() << "G_OBJECT_CLASS (" << class_name_lc << "_parent_class)" "->finalize (gobject);" << '\n'; scope_down(f_service_); f_service_ << '\n'; // Generate processor instance initializer f_service_ << "static void" << '\n' << class_name_lc << "_init (" << class_name << " *self)" << '\n'; scope_up(f_service_); if (functions.size() > 0) { f_service_ << indent() << "guint index;" << '\n' << '\n'; } f_service_ << indent() << "self->handler = NULL;" << '\n' << indent() << "self->process_map = " "g_hash_table_new (g_str_hash, g_str_equal);" << '\n'; if (functions.size() > 0) { args_indent = string(21, ' '); f_service_ << '\n' << indent() << "for (index = 0; index < " << functions.size() << "; index += 1)" << '\n'; indent_up(); f_service_ << indent() << "g_hash_table_insert (self->process_map," << '\n' << indent() << args_indent << class_name_lc << "_process_function_defs[index].name," << '\n' << indent() << args_indent << "&" << class_name_lc << "_process_function_defs[index]" << ");" << '\n'; indent_down(); } scope_down(f_service_); f_service_ << '\n'; // Generate processor class initializer f_service_ << "static void" << '\n' << class_name_lc << "_class_init (" << class_name << "Class *cls)" << '\n'; scope_up(f_service_); f_service_ << indent() << "GObjectClass *gobject_class = G_OBJECT_CLASS (cls);" << '\n' << indent() << "ThriftDispatchProcessorClass *dispatch_processor_class =" << '\n'; indent_up(); f_service_ << indent() << "THRIFT_DISPATCH_PROCESSOR_CLASS (cls);" << '\n'; indent_down(); f_service_ << indent() << "GParamSpec *param_spec;" << '\n' << '\n' << indent() << "gobject_class->dispose = " << class_name_lc << "_dispose;" << '\n' << indent() << "gobject_class->finalize = " << class_name_lc << "_finalize;" << '\n' << indent() << "gobject_class->set_property = " << class_name_lc << "_set_property;" << '\n' << indent() << "gobject_class->get_property = " << class_name_lc << "_get_property;" << '\n' << '\n' << indent() << "dispatch_processor_class->dispatch_call = " << class_name_lc << "_dispatch_call;" << '\n' << indent() << "cls->dispatch_call = " << class_name_lc << "_dispatch_call;" << '\n' << '\n' << indent() << "param_spec = g_param_spec_object (\"handler\"," << '\n'; args_indent = indent() + string(34, ' '); f_service_ << args_indent << "\"Service handler implementation\"," << '\n' << args_indent << "\"The service handler implementation \"" << '\n' << args_indent << "\"to which method calls are dispatched.\"," << '\n' << args_indent << this->nspace_uc + "TYPE_" + service_name_uc + "_HANDLER," << '\n' << args_indent << "G_PARAM_READWRITE);" << '\n'; f_service_ << indent() << "g_object_class_install_property (gobject_class," << '\n'; args_indent = indent() + string(33, ' '); f_service_ << args_indent << "PROP_" << class_name_uc << "_HANDLER," << '\n' << args_indent << "param_spec);" << '\n'; scope_down(f_service_); } /** * Generates C code that represents a Thrift service server. */ void t_c_glib_generator::generate_service_server(t_service* tservice) { (void)tservice; // Generate the service's handler class generate_service_handler(tservice); // Generate the service's processor class generate_service_processor(tservice); } /** * Generates C code to represent a THrift structure as a GObject. */ void t_c_glib_generator::generate_object(t_struct* tstruct) { string name = tstruct->get_name(); string name_u = initial_caps_to_underscores(name); string name_uc = to_upper_case(name_u); string class_name = this->nspace + name; string class_name_lc = this->nspace_lc + initial_caps_to_underscores(name); string class_name_uc = to_upper_case(class_name_lc); string function_name; string args_indent; // write the instance definition f_types_ << "struct _" << this->nspace << name << '\n' << "{ " << '\n' << " ThriftStruct parent; " << '\n' << '\n' << " /* public */" << '\n'; // for each field, add a member variable vector::const_iterator m_iter; const vector& members = tstruct->get_members(); for (m_iter = members.begin(); m_iter != members.end(); ++m_iter) { t_type* t = get_true_type((*m_iter)->get_type()); f_types_ << " " << type_name(t) << " " << (*m_iter)->get_name() << ";" << '\n'; if ((*m_iter)->get_req() != t_field::T_REQUIRED) { f_types_ << " gboolean __isset_" << (*m_iter)->get_name() << ";" << '\n'; } } // close the structure definition and create a typedef f_types_ << "};" << '\n' << "typedef struct _" << this->nspace << name << " " << this->nspace << name << ";" << '\n' << '\n'; // write the class definition f_types_ << "struct _" << this->nspace << name << "Class" << '\n' << "{" << '\n' << " ThriftStructClass parent;" << '\n' << "};" << '\n' << "typedef struct _" << this->nspace << name << "Class " << this->nspace << name << "Class;" << '\n' << '\n'; // write the standard GObject boilerplate f_types_ << "GType " << this->nspace_lc << name_u << "_get_type (void);" << '\n' << "#define " << this->nspace_uc << "TYPE_" << name_uc << " (" << this->nspace_lc << name_u << "_get_type())" << '\n' << "#define " << this->nspace_uc << name_uc << "(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), " << this->nspace_uc << "TYPE_" << name_uc << ", " << this->nspace << name << "))" << '\n' << "#define " << this->nspace_uc << name_uc << "_CLASS(c) (G_TYPE_CHECK_CLASS_CAST ((c), " << this->nspace_uc << "_TYPE_" << name_uc << ", " << this->nspace << name << "Class))" << '\n' << "#define " << this->nspace_uc << "IS_" << name_uc << "(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), " << this->nspace_uc << "TYPE_" << name_uc << "))" << '\n' << "#define " << this->nspace_uc << "IS_" << name_uc << "_CLASS(c) (G_TYPE_CHECK_CLASS_TYPE ((c), " << this->nspace_uc << "TYPE_" << name_uc << "))" << '\n' << "#define " << this->nspace_uc << name_uc << "_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), " << this->nspace_uc << "TYPE_" << name_uc << ", " << this->nspace << name << "Class))" << '\n' << '\n'; // start writing the object implementation .c file // generate properties enum if (members.size() > 0) { f_types_impl_ << "enum _" << class_name << "Properties" << '\n' << "{" << '\n'; indent_up(); f_types_impl_ << indent() << "PROP_" << class_name_uc << "_0"; for (m_iter = members.begin(); m_iter != members.end(); ++m_iter) { string member_name_uc = to_upper_case(to_lower_case(initial_caps_to_underscores((*m_iter)->get_name()))); f_types_impl_ << "," << '\n' << indent() << "PROP_" << class_name_uc << "_" << member_name_uc; } f_types_impl_ << '\n'; indent_down(); f_types_impl_ << "};" << '\n' << '\n'; } // generate struct I/O methods string this_get = this->nspace + name + " * this_object = " + this->nspace_uc + name_uc + "(object);"; generate_struct_reader(f_types_impl_, tstruct, "this_object->", this_get); generate_struct_writer(f_types_impl_, tstruct, "this_object->", this_get); // generate property setter and getter if (members.size() > 0) { // generate property setter function_name = class_name_lc + "_set_property"; args_indent = string(function_name.length() + 2, ' '); f_types_impl_ << "static void" << '\n' << function_name << " (GObject *object," << '\n' << args_indent << "guint property_id," << '\n' << args_indent << "const GValue *value," << '\n' << args_indent << "GParamSpec *pspec)" << '\n'; scope_up(f_types_impl_); f_types_impl_ << indent() << class_name << " *self = " << class_name_uc << " (object);" << '\n' << '\n' << indent() << "switch (property_id)" << '\n'; scope_up(f_types_impl_); for (m_iter = members.begin(); m_iter != members.end(); ++m_iter) { t_field* member = (*m_iter); string member_name = member->get_name(); string member_name_uc = to_upper_case(to_lower_case(initial_caps_to_underscores(member_name))); t_type* member_type = get_true_type(member->get_type()); string property_identifier = "PROP_" + class_name_uc + "_" + member_name_uc; f_types_impl_ << indent() << "case " << property_identifier + ":" << '\n'; indent_up(); if (member_type->is_base_type()) { t_base_type* base_type = ((t_base_type*)member_type); string assign_function_name; if (base_type->get_base() == t_base_type::TYPE_STRING) { string release_function_name; f_types_impl_ << indent() << "if (self->" << member_name << " != NULL)" << '\n'; indent_up(); if (base_type->is_binary()) { release_function_name = "g_byte_array_unref"; assign_function_name = "g_value_dup_boxed"; } else { release_function_name = "g_free"; assign_function_name = "g_value_dup_string"; } f_types_impl_ << indent() << release_function_name << " (self->" << member_name << ");" << '\n'; indent_down(); } else { switch (base_type->get_base()) { case t_base_type::TYPE_BOOL: assign_function_name = "g_value_get_boolean"; break; case t_base_type::TYPE_I8: case t_base_type::TYPE_I16: case t_base_type::TYPE_I32: assign_function_name = "g_value_get_int"; break; case t_base_type::TYPE_I64: assign_function_name = "g_value_get_int64"; break; case t_base_type::TYPE_DOUBLE: assign_function_name = "g_value_get_double"; break; default: throw "compiler error: " "unrecognized base type \"" + base_type->get_name() + "\" " "for struct member \"" + member_name + "\""; break; } } f_types_impl_ << indent() << "self->" << member_name << " = " << assign_function_name << " (value);" << '\n'; } else if (member_type->is_enum()) { f_types_impl_ << indent() << "self->" << member_name << " = g_value_get_int (value);" << '\n'; } else if (member_type->is_container()) { string release_function_name; string assign_function_name; if (member_type->is_list()) { t_type* elem_type = ((t_list*)member_type)->get_elem_type(); // Lists of base types other than strings are represented as GArrays; // all others as GPtrArrays if (is_numeric(elem_type)) { release_function_name = "g_array_unref"; } else { release_function_name = "g_ptr_array_unref"; } assign_function_name = "g_value_dup_boxed"; } else if (member_type->is_set() || member_type->is_map()) { release_function_name = "g_hash_table_unref"; assign_function_name = "g_value_dup_boxed"; } f_types_impl_ << indent() << "if (self->" << member_name << " != NULL)" << '\n'; indent_up(); f_types_impl_ << indent() << release_function_name << " (self->" << member_name << ");" << '\n'; indent_down(); f_types_impl_ << indent() << "self->" << member_name << " = " << assign_function_name << " (value);" << '\n'; } else if (member_type->is_struct() || member_type->is_xception()) { f_types_impl_ << indent() << "if (self->" << member_name << " != NULL)" << '\n'; indent_up(); f_types_impl_ << indent() << "g_object_unref (self->" << member_name << ");" << '\n'; indent_down(); f_types_impl_ << indent() << "self->" << member_name << " = g_value_dup_object (value);" << '\n'; } if (member->get_req() != t_field::T_REQUIRED) { f_types_impl_ << indent() << "self->__isset_" << member_name << " = TRUE;" << '\n'; } f_types_impl_ << indent() << "break;" << '\n' << '\n'; indent_down(); } f_types_impl_ << indent() << "default:" << '\n'; indent_up(); f_types_impl_ << indent() << "G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);" << '\n' << indent() << "break;" << '\n'; indent_down(); scope_down(f_types_impl_); scope_down(f_types_impl_); f_types_impl_ << '\n'; // generate property getter function_name = class_name_lc + "_get_property"; args_indent = string(function_name.length() + 2, ' '); f_types_impl_ << "static void" << '\n' << function_name << " (GObject *object," << '\n' << args_indent << "guint property_id," << '\n' << args_indent << "GValue *value," << '\n' << args_indent << "GParamSpec *pspec)" << '\n'; scope_up(f_types_impl_); f_types_impl_ << indent() << class_name << " *self = " << class_name_uc << " (object);" << '\n' << '\n' << indent() << "switch (property_id)" << '\n'; scope_up(f_types_impl_); for (m_iter = members.begin(); m_iter != members.end(); ++m_iter) { t_field* member = (*m_iter); string member_name = (*m_iter)->get_name(); string member_name_uc = to_upper_case(to_lower_case(initial_caps_to_underscores(member_name))); t_type* member_type = get_true_type(member->get_type()); string property_identifier = "PROP_" + class_name_uc + "_" + member_name_uc; string setter_function_name; if (member_type->is_base_type()) { t_base_type* base_type = ((t_base_type*)member_type); switch (base_type->get_base()) { case t_base_type::TYPE_BOOL: setter_function_name = "g_value_set_boolean"; break; case t_base_type::TYPE_I8: case t_base_type::TYPE_I16: case t_base_type::TYPE_I32: setter_function_name = "g_value_set_int"; break; case t_base_type::TYPE_I64: setter_function_name = "g_value_set_int64"; break; case t_base_type::TYPE_DOUBLE: setter_function_name = "g_value_set_double"; break; case t_base_type::TYPE_STRING: if (base_type->is_binary()) { setter_function_name = "g_value_set_boxed"; } else { setter_function_name = "g_value_set_string"; } break; default: throw "compiler error: " "unrecognized base type \"" + base_type->get_name() + "\" " "for struct member \"" + member_name + "\""; break; } } else if (member_type->is_enum()) { setter_function_name = "g_value_set_int"; } else if (member_type->is_struct() || member_type->is_xception()) { setter_function_name = "g_value_set_object"; } else if (member_type->is_container()) { setter_function_name = "g_value_set_boxed"; } else { throw "compiler error: " "unrecognized type for struct member \"" + member_name + "\""; } f_types_impl_ << indent() << "case " << property_identifier + ":" << '\n'; indent_up(); f_types_impl_ << indent() << setter_function_name << " (value, self->" << member_name << ");" << '\n' << indent() << "break;" << '\n' << '\n'; indent_down(); } f_types_impl_ << indent() << "default:" << '\n'; indent_up(); f_types_impl_ << indent() << "G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);" << '\n' << indent() << "break;" << '\n'; indent_down(); scope_down(f_types_impl_); scope_down(f_types_impl_); f_types_impl_ << '\n'; } // generate the instance init function f_types_impl_ << "static void " << '\n' << this->nspace_lc << name_u << "_instance_init (" << this->nspace << name << " * object)" << '\n' << "{" << '\n'; indent_up(); // generate default-value structures for container-type members bool constant_declaration_output = false; bool string_list_constant_output = false; for (m_iter = members.begin(); m_iter != members.end(); ++m_iter) { t_field* member = *m_iter; t_const_value* member_value = member->get_value(); if (member_value != nullptr) { string member_name = member->get_name(); t_type* member_type = get_true_type(member->get_type()); if (member_type->is_list()) { const vector& list = member_value->get_list(); t_type* elem_type = ((t_list*)member_type)->get_elem_type(); // Generate an array with the list literal indent(f_types_impl_) << "static " << type_name(elem_type, false, true) << " __default_" << member_name << "[" << list.size() << "] = " << '\n'; indent_up(); f_types_impl_ << indent() << constant_literal(member_type, member_value) << ";" << '\n'; indent_down(); constant_declaration_output = true; // If we are generating values for a pointer array (i.e. a list of // strings), set a flag so we know to also declare an index variable to // use in pre-populating the array if (elem_type->is_string()) { string_list_constant_output = true; } } // TODO: Handle container types other than list } } if (constant_declaration_output) { if (string_list_constant_output) { indent(f_types_impl_) << "unsigned int list_index;" << '\n'; } f_types_impl_ << '\n'; } // satisfy compilers with -Wall turned on indent(f_types_impl_) << "/* satisfy -Wall */" << '\n' << indent() << "THRIFT_UNUSED_VAR (object);" << '\n'; for (m_iter = members.begin(); m_iter != members.end(); ++m_iter) { t_type* member_type = (*m_iter)->get_type(); t_type* t = get_true_type(member_type); if (t->is_base_type()) { string dval = " = "; if (t->is_enum()) { dval += "(" + type_name(t) + ")"; } t_const_value* cv = (*m_iter)->get_value(); if (cv != nullptr) { dval += constant_value("", t, cv); } else { dval += t->is_string() ? "NULL" : "0"; } indent(f_types_impl_) << "object->" << (*m_iter)->get_name() << dval << ";" << '\n'; } else if (t->is_struct()) { string name = (*m_iter)->get_name(); t_program* type_program = member_type->get_program(); string type_nspace = type_program ? type_program->get_namespace("c_glib") : ""; string type_nspace_prefix = type_nspace.empty() ? "" : initial_caps_to_underscores(type_nspace) + "_"; string type_name_uc = to_upper_case(initial_caps_to_underscores(member_type->get_name())); indent(f_types_impl_) << "object->" << name << " = g_object_new (" << to_upper_case(type_nspace_prefix) << "TYPE_" << type_name_uc << ", NULL);" << '\n'; } else if (t->is_xception()) { string name = (*m_iter)->get_name(); indent(f_types_impl_) << "object->" << name << " = NULL;" << '\n'; } else if (t->is_container()) { string name = (*m_iter)->get_name(); string init_function; t_type* etype = nullptr; if (t->is_map()) { t_type* key = ((t_map*)t)->get_key_type(); t_type* value = ((t_map*)t)->get_val_type(); init_function = generate_new_hash_from_type(key, value); } else if (t->is_set()) { etype = ((t_set*)t)->get_elem_type(); init_function = generate_new_hash_from_type(etype, nullptr); } else if (t->is_list()) { etype = ((t_list*)t)->get_elem_type(); init_function = generate_new_array_from_type(etype); } indent(f_types_impl_) << "object->" << name << " = " << init_function << '\n'; // Pre-populate the container with the specified default values, if any if ((*m_iter)->get_value()) { t_const_value* member_value = (*m_iter)->get_value(); if (t->is_list()) { const vector& list = member_value->get_list(); if (is_numeric(etype)) { indent(f_types_impl_) << "g_array_append_vals (object->" << name << ", &__default_" << name << ", " << list.size() << ");" << '\n'; } else { indent(f_types_impl_) << "for (list_index = 0; list_index < " << list.size() << "; " << "list_index += 1)" << '\n'; indent_up(); indent(f_types_impl_) << "g_ptr_array_add (object->" << name << "," << '\n' << indent() << string(17, ' ') << "g_strdup (__default_" << name << "[list_index]));" << '\n'; indent_down(); } } // TODO: Handle container types other than list } } /* if not required, initialize the __isset variable */ if ((*m_iter)->get_req() != t_field::T_REQUIRED) { indent(f_types_impl_) << "object->__isset_" << (*m_iter)->get_name() << " = FALSE;" << '\n'; } } indent_down(); f_types_impl_ << "}" << '\n' << '\n'; /* create the destructor */ f_types_impl_ << "static void " << '\n' << this->nspace_lc << name_u << "_finalize (GObject *object)" << '\n' << "{" << '\n'; indent_up(); f_types_impl_ << indent() << this->nspace << name << " *tobject = " << this->nspace_uc << name_uc << " (object);" << '\n' << '\n'; f_types_impl_ << indent() << "/* satisfy -Wall in case we don't use tobject */" << '\n' << indent() << "THRIFT_UNUSED_VAR (tobject);" << '\n'; for (m_iter = members.begin(); m_iter != members.end(); ++m_iter) { t_type* t = get_true_type((*m_iter)->get_type()); if (t->is_container()) { string name = (*m_iter)->get_name(); if (t->is_map() || t->is_set()) { f_types_impl_ << indent() << "if (tobject->" << name << " != NULL)" << '\n'; f_types_impl_ << indent() << "{" << '\n'; indent_up(); f_types_impl_ << indent() << "g_hash_table_destroy (tobject->" << name << ");" << '\n'; f_types_impl_ << indent() << "tobject->" << name << " = NULL;" << '\n'; indent_down(); f_types_impl_ << indent() << "}" << '\n'; } else if (t->is_list()) { t_type* etype = ((t_list*)t)->get_elem_type(); string destructor_function = "g_ptr_array_unref"; if (etype->is_base_type()) { t_base_type::t_base tbase = ((t_base_type*)etype)->get_base(); switch (tbase) { case t_base_type::TYPE_VOID: throw "compiler error: cannot determine array type"; 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: destructor_function = "g_array_unref"; break; case t_base_type::TYPE_STRING: break; default: throw "compiler error: no array info for type"; } } else if (etype->is_enum()) { destructor_function = "g_array_unref"; } f_types_impl_ << indent() << "if (tobject->" << name << " != NULL)" << '\n'; f_types_impl_ << indent() << "{" << '\n'; indent_up(); f_types_impl_ << indent() << destructor_function << " (tobject->" << name << ");" << '\n'; f_types_impl_ << indent() << "tobject->" << name << " = NULL;" << '\n'; indent_down(); f_types_impl_ << indent() << "}" << '\n'; } } else if (t->is_struct() || t->is_xception()) { string name = (*m_iter)->get_name(); // TODO: g_clear_object needs glib >= 2.28 // f_types_impl_ << indent() << "g_clear_object (&(tobject->" << name << "));" << '\n'; // does g_object_unref the trick? f_types_impl_ << indent() << "if (tobject->" << name << " != NULL)" << '\n'; f_types_impl_ << indent() << "{" << '\n'; indent_up(); f_types_impl_ << indent() << "g_object_unref(tobject->" << name << ");" << '\n'; f_types_impl_ << indent() << "tobject->" << name << " = NULL;" << '\n'; indent_down(); f_types_impl_ << indent() << "}" << '\n'; } else if (t->is_string()) { string name = (*m_iter)->get_name(); f_types_impl_ << indent() << "if (tobject->" << name << " != NULL)" << '\n'; f_types_impl_ << indent() << "{" << '\n'; indent_up(); f_types_impl_ << indent() << generate_free_func_from_type(t) << "(tobject->" << name << ");" << '\n'; f_types_impl_ << indent() << "tobject->" << name << " = NULL;" << '\n'; indent_down(); f_types_impl_ << indent() << "}" << '\n'; } } indent_down(); f_types_impl_ << "}" << '\n' << '\n'; // generate the class init function f_types_impl_ << "static void" << '\n' << class_name_lc << "_class_init (" << class_name << "Class * cls)" << '\n'; scope_up(f_types_impl_); f_types_impl_ << indent() << "GObjectClass *gobject_class = G_OBJECT_CLASS (cls);" << '\n' << indent() << "ThriftStructClass *struct_class = " << "THRIFT_STRUCT_CLASS (cls);" << '\n' << '\n' << indent() << "struct_class->read = " << class_name_lc << "_read;" << '\n' << indent() << "struct_class->write = " << class_name_lc << "_write;" << '\n' << '\n' << indent() << "gobject_class->finalize = " << class_name_lc << "_finalize;" << '\n'; if (members.size() > 0) { f_types_impl_ << indent() << "gobject_class->get_property = " << class_name_lc << "_get_property;" << '\n' << indent() << "gobject_class->set_property = " << class_name_lc << "_set_property;" << '\n'; // install a property for each member for (m_iter = members.begin(); m_iter != members.end(); ++m_iter) { t_field* member = (*m_iter); string member_name = member->get_name(); string member_name_uc = to_upper_case(to_lower_case(initial_caps_to_underscores(member_name))); t_type* member_type = get_true_type(member->get_type()); t_const_value* member_value = member->get_value(); string property_identifier = "PROP_" + class_name_uc + "_" + member_name_uc; f_types_impl_ << '\n' << indent() << "g_object_class_install_property" << '\n'; indent_up(); args_indent = indent() + ' '; f_types_impl_ << indent() << "(gobject_class," << '\n' << args_indent << property_identifier << "," << '\n' << args_indent; if (member_type->is_base_type()) { t_base_type::t_base base_type = ((t_base_type*)member_type)->get_base(); if (base_type == t_base_type::TYPE_STRING) { if (((t_base_type*)member_type)->is_binary()) { args_indent += string(20, ' '); f_types_impl_ << "g_param_spec_boxed (\"" << member_name << "\"," << '\n' << args_indent << "NULL," << '\n' << args_indent << "NULL," << '\n' << args_indent << "G_TYPE_BYTE_ARRAY," << '\n' << args_indent << "G_PARAM_READWRITE));" << '\n'; } else { args_indent += string(21, ' '); f_types_impl_ << "g_param_spec_string (\"" << member_name << "\"," << '\n' << args_indent << "NULL," << '\n' << args_indent << "NULL," << '\n' << args_indent << ((member_value != NULL) ? "\"" + member_value->get_string() + "\"" : "NULL") << "," << '\n' << args_indent << "G_PARAM_READWRITE));" << '\n'; } } else if (base_type == t_base_type::TYPE_BOOL) { args_indent += string(22, ' '); f_types_impl_ << "g_param_spec_boolean (\"" << member_name << "\"," << '\n' << args_indent << "NULL," << '\n' << args_indent << "NULL," << '\n' << args_indent << (((member_value != NULL) && (member_value->get_integer() != 0)) ? "TRUE" : "FALSE") << "," << '\n' << args_indent << "G_PARAM_READWRITE));" << '\n'; } else if ((base_type == t_base_type::TYPE_I8) || (base_type == t_base_type::TYPE_I16) || (base_type == t_base_type::TYPE_I32) || (base_type == t_base_type::TYPE_I64) || (base_type == t_base_type::TYPE_DOUBLE)) { string param_spec_function_name = "g_param_spec_int"; string min_value; string max_value; ostringstream default_value; switch (base_type) { case t_base_type::TYPE_I8: min_value = "G_MININT8"; max_value = "G_MAXINT8"; break; case t_base_type::TYPE_I16: min_value = "G_MININT16"; max_value = "G_MAXINT16"; break; case t_base_type::TYPE_I32: min_value = "G_MININT32"; max_value = "G_MAXINT32"; break; case t_base_type::TYPE_I64: param_spec_function_name = "g_param_spec_int64"; min_value = "G_MININT64"; max_value = "G_MAXINT64"; break; case t_base_type::TYPE_DOUBLE: param_spec_function_name = "g_param_spec_double"; min_value = "-INFINITY"; max_value = "INFINITY"; break; default: throw "compiler error: " "unrecognized base type \"" + member_type->get_name() + "\" " "for struct member \"" + member_name + "\""; break; } if (member_value != nullptr) { default_value << (base_type == t_base_type::TYPE_DOUBLE ? member_value->get_double() : member_value->get_integer()); } else { default_value << "0"; } args_indent += string(param_spec_function_name.length() + 2, ' '); f_types_impl_ << param_spec_function_name << " (\"" << member_name << "\"," << '\n' << args_indent << "NULL," << '\n' << args_indent << "NULL," << '\n' << args_indent << min_value << "," << '\n' << args_indent << max_value << "," << '\n' << args_indent << default_value.str() << "," << '\n' << args_indent << "G_PARAM_READWRITE));" << '\n'; } indent_down(); } else if (member_type->is_enum()) { t_enum_value* enum_min_value = ((t_enum*)member_type)->get_min_value(); t_enum_value* enum_max_value = ((t_enum*)member_type)->get_max_value(); int min_value = (enum_min_value != nullptr) ? enum_min_value->get_value() : 0; int max_value = (enum_max_value != nullptr) ? enum_max_value->get_value() : 0; args_indent += string(18, ' '); f_types_impl_ << "g_param_spec_int (\"" << member_name << "\"," << '\n' << args_indent << "NULL," << '\n' << args_indent << "NULL," << '\n' << args_indent << min_value << "," << '\n' << args_indent << max_value << "," << '\n' << args_indent << min_value << "," << '\n' << args_indent << "G_PARAM_READWRITE));" << '\n'; indent_down(); } else if (member_type->is_struct() || member_type->is_xception()) { t_program* type_program = member_type->get_program(); string type_nspace = type_program ? type_program->get_namespace("c_glib") : ""; string type_nspace_prefix = type_nspace.empty() ? "" : initial_caps_to_underscores(type_nspace) + "_"; string param_type = to_upper_case(type_nspace_prefix) + "TYPE_" + to_upper_case(initial_caps_to_underscores(member_type->get_name())); args_indent += string(20, ' '); f_types_impl_ << "g_param_spec_object (\"" << member_name << "\"," << '\n' << args_indent << "NULL," << '\n' << args_indent << "NULL," << '\n' << args_indent << param_type << "," << '\n' << args_indent << "G_PARAM_READWRITE));" << '\n'; indent_down(); } else if (member_type->is_list()) { t_type* elem_type = ((t_list*)member_type)->get_elem_type(); string param_type; if (elem_type->is_base_type() && !elem_type->is_string()) { param_type = "G_TYPE_ARRAY"; } else { param_type = "G_TYPE_PTR_ARRAY"; } args_indent += string(20, ' '); f_types_impl_ << "g_param_spec_boxed (\"" << member_name << "\"," << '\n' << args_indent << "NULL," << '\n' << args_indent << "NULL," << '\n' << args_indent << param_type << "," << '\n' << args_indent << "G_PARAM_READWRITE));" << '\n'; indent_down(); } else if (member_type->is_set() || member_type->is_map()) { args_indent += string(20, ' '); f_types_impl_ << "g_param_spec_boxed (\"" << member_name << "\"," << '\n' << args_indent << "NULL," << '\n' << args_indent << "NULL," << '\n' << args_indent << "G_TYPE_HASH_TABLE," << '\n' << args_indent << "G_PARAM_READWRITE));" << '\n'; indent_down(); } } } scope_down(f_types_impl_); f_types_impl_ << '\n'; f_types_impl_ << "GType" << '\n' << this->nspace_lc << name_u << "_get_type (void)" << '\n' << "{" << '\n' << " static GType type = 0;" << '\n' << '\n' << " if (type == 0) " << '\n' << " {" << '\n' << " static const GTypeInfo type_info = " << '\n' << " {" << '\n' << " sizeof (" << this->nspace << name << "Class)," << '\n' << " NULL, /* base_init */" << '\n' << " NULL, /* base_finalize */" << '\n' << " (GClassInitFunc) " << this->nspace_lc << name_u << "_class_init," << '\n' << " NULL, /* class_finalize */" << '\n' << " NULL, /* class_data */" << '\n' << " sizeof (" << this->nspace << name << ")," << '\n' << " 0, /* n_preallocs */" << '\n' << " (GInstanceInitFunc) " << this->nspace_lc << name_u << "_instance_init," << '\n' << " NULL, /* value_table */" << '\n' << " };" << '\n' << '\n' << " type = g_type_register_static (THRIFT_TYPE_STRUCT, " << '\n' << " \"" << this->nspace << name << "Type\"," << '\n' << " &type_info, 0);" << '\n' << " }" << '\n' << '\n' << " return type;" << '\n' << "}" << '\n' << '\n'; } /** * Generates functions to write Thrift structures to a stream. */ void t_c_glib_generator::generate_struct_writer(ostream& out, t_struct* tstruct, string this_name, string this_get, bool is_function) { string name = tstruct->get_name(); string name_u = initial_caps_to_underscores(name); string name_uc = to_upper_case(name_u); const vector& fields = tstruct->get_members(); vector::const_iterator f_iter; int error_ret = 0; if (is_function) { error_ret = -1; indent(out) << "static gint32" << '\n' << this->nspace_lc << name_u << "_write (ThriftStruct *object, ThriftProtocol *protocol, GError **error)" << '\n'; } indent(out) << "{" << '\n'; indent_up(); out << indent() << "gint32 ret;" << '\n' << indent() << "gint32 xfer = 0;" << '\n' << '\n'; indent(out) << this_get << '\n'; // satisfy -Wall in the case of an empty struct if (!this_get.empty()) { indent(out) << "THRIFT_UNUSED_VAR (this_object);" << '\n'; } out << indent() << "if ((ret = thrift_protocol_write_struct_begin (protocol, \"" << name << "\", error)) < 0)" << '\n' << indent() << " return " << error_ret << ";" << '\n' << indent() << "xfer += ret;" << '\n'; for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) { if ((*f_iter)->get_req() == t_field::T_OPTIONAL) { indent(out) << "if (this_object->__isset_" << (*f_iter)->get_name() << " == TRUE) {" << '\n'; indent_up(); } out << indent() << "if ((ret = thrift_protocol_write_field_begin (protocol, " << "\"" << (*f_iter)->get_name() << "\", " << type_to_enum((*f_iter)->get_type()) << ", " << (*f_iter)->get_key() << ", error)) < 0)" << '\n' << indent() << " return " << error_ret << ";" << '\n' << indent() << "xfer += ret;" << '\n'; generate_serialize_field(out, *f_iter, this_name, "", error_ret); out << indent() << "if ((ret = thrift_protocol_write_field_end (protocol, error)) < 0)" << '\n' << indent() << " return " << error_ret << ";" << '\n' << indent() << "xfer += ret;" << '\n'; if ((*f_iter)->get_req() == t_field::T_OPTIONAL) { indent_down(); indent(out) << "}" << '\n'; } } // write the struct map out << indent() << "if ((ret = thrift_protocol_write_field_stop (protocol, error)) < 0)" << '\n' << indent() << " return " << error_ret << ";" << '\n' << indent() << "xfer += ret;" << '\n' << indent() << "if ((ret = thrift_protocol_write_struct_end (protocol, error)) < 0)" << '\n' << indent() << " return " << error_ret << ";" << '\n' << indent() << "xfer += ret;" << '\n' << '\n'; if (is_function) { indent(out) << "return xfer;" << '\n'; } indent_down(); indent(out) << "}" << '\n' << '\n'; } /** * Generates code to read Thrift structures from a stream. */ void t_c_glib_generator::generate_struct_reader(ostream& out, t_struct* tstruct, string this_name, string this_get, bool is_function) { string name = tstruct->get_name(); string name_u = initial_caps_to_underscores(name); string name_uc = to_upper_case(name_u); int error_ret = 0; const vector& fields = tstruct->get_members(); vector::const_iterator f_iter; if (is_function) { error_ret = -1; indent(out) << "/* reads a " << name_u << " object */" << '\n' << "static gint32" << '\n' << this->nspace_lc << name_u << "_read (ThriftStruct *object, ThriftProtocol *protocol, GError **error)" << '\n'; } indent(out) << "{" << '\n'; indent_up(); // declare stack temp variables out << indent() << "gint32 ret;" << '\n' << indent() << "gint32 xfer = 0;" << '\n' << indent() << "gchar *name = NULL;" << '\n' << indent() << "ThriftType ftype;" << '\n' << indent() << "gint16 fid;" << '\n' << indent() << "guint32 len = 0;" << '\n' << indent() << "gpointer data = NULL;" << '\n' << indent() << this_get << '\n'; for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) { if ((*f_iter)->get_req() == t_field::T_REQUIRED) { indent(out) << "gboolean isset_" << (*f_iter)->get_name() << " = FALSE;" << '\n'; } } out << '\n'; // satisfy -Wall in case we don't use some variables out << indent() << "/* satisfy -Wall in case these aren't used */" << '\n' << indent() << "THRIFT_UNUSED_VAR (len);" << '\n' << indent() << "THRIFT_UNUSED_VAR (data);" << '\n'; if (!this_get.empty()) { out << indent() << "THRIFT_UNUSED_VAR (this_object);" << '\n'; } out << '\n'; // read the beginning of the structure marker out << indent() << "/* read the struct begin marker */" << '\n' << indent() << "if ((ret = thrift_protocol_read_struct_begin (protocol, &name, error)) < 0)" << '\n' << indent() << "{" << '\n' << indent() << " if (name) g_free (name);" << '\n' << indent() << " return " << error_ret << ";" << '\n' << indent() << "}" << '\n' << indent() << "xfer += ret;" << '\n' << indent() << "if (name) g_free (name);" << '\n' << indent() << "name = NULL;" << '\n' << '\n'; // read the struct fields out << indent() << "/* read the struct fields */" << '\n' << indent() << "while (1)" << '\n'; scope_up(out); // read beginning field marker out << indent() << "/* read the beginning of a field */" << '\n' << indent() << "if ((ret = thrift_protocol_read_field_begin (protocol, &name, &ftype, &fid, error)) < 0)" << '\n' << indent() << "{" << '\n' << indent() << " if (name) g_free (name);" << '\n' << indent() << " return " << error_ret << ";" << '\n' << indent() << "}" << '\n' << indent() << "xfer += ret;" << '\n' << indent() << "if (name) g_free (name);" << '\n' << indent() << "name = NULL;" << '\n' << '\n'; // check for field STOP marker out << indent() << "/* break if we get a STOP field */" << '\n' << indent() << "if (ftype == T_STOP)" << '\n' << indent() << "{" << '\n' << indent() << " break;" << '\n' << indent() << "}" << '\n' << '\n'; // switch depending on the field type indent(out) << "switch (fid)" << '\n'; // start switch scope_up(out); // generate deserialization code for known types for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) { indent(out) << "case " << (*f_iter)->get_key() << ":" << '\n'; indent_up(); indent(out) << "if (ftype == " << type_to_enum((*f_iter)->get_type()) << ")" << '\n'; indent(out) << "{" << '\n'; indent_up(); // generate deserialize field generate_deserialize_field(out, *f_iter, this_name, "", error_ret, false); indent_down(); out << indent() << "} else {" << '\n' << indent() << " if ((ret = thrift_protocol_skip (protocol, ftype, error)) < 0)" << '\n' << indent() << " return " << error_ret << ";" << '\n' << indent() << " xfer += ret;" << '\n' << indent() << "}" << '\n' << indent() << "break;" << '\n'; indent_down(); } // create the default case out << indent() << "default:" << '\n' << indent() << " if ((ret = thrift_protocol_skip (protocol, ftype, error)) < 0)" << '\n' << indent() << " return " << error_ret << ";" << '\n' << indent() << " xfer += ret;" << '\n' << indent() << " break;" << '\n'; // end switch scope_down(out); // read field end marker out << indent() << "if ((ret = thrift_protocol_read_field_end (protocol, error)) < 0)" << '\n' << indent() << " return " << error_ret << ";" << '\n' << indent() << "xfer += ret;" << '\n'; // end while loop scope_down(out); out << '\n'; // read the end of the structure out << indent() << "if ((ret = thrift_protocol_read_struct_end (protocol, error)) < 0)" << '\n' << indent() << " return " << error_ret << ";" << '\n' << indent() << "xfer += ret;" << '\n' << '\n'; // if a required field is missing, throw an error 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() << ")" << '\n' << indent() << "{" << '\n' << indent() << " g_set_error (error, THRIFT_PROTOCOL_ERROR," << '\n' << indent() << " THRIFT_PROTOCOL_ERROR_INVALID_DATA," << '\n' << indent() << " \"missing field\");" << '\n' << indent() << " return -1;" << '\n' << indent() << "}" << '\n' << '\n'; } } if (is_function) { indent(out) << "return xfer;" << '\n'; } // end the function/structure indent_down(); indent(out) << "}" << '\n' << '\n'; } void t_c_glib_generator::generate_serialize_field(ostream& out, t_field* tfield, string prefix, string suffix, int error_ret) { t_type* type = get_true_type(tfield->get_type()); string name = prefix + tfield->get_name() + suffix; if (type->is_void()) { throw "CANNOT GENERATE SERIALIZE CODE FOR void TYPE: " + name; } if (type->is_struct() || type->is_xception()) { generate_serialize_struct(out, (t_struct*)type, name, error_ret); } else if (type->is_container()) { generate_serialize_container(out, type, name, error_ret); } else if (type->is_base_type() || type->is_enum()) { indent(out) << "if ((ret = thrift_protocol_write_"; if (type->is_base_type()) { t_base_type::t_base tbase = ((t_base_type*)type)->get_base(); switch (tbase) { case t_base_type::TYPE_VOID: throw "compiler error: cannot serialize void field in a struct: " + name; break; case t_base_type::TYPE_BOOL: out << "bool (protocol, " << name; break; case t_base_type::TYPE_I8: out << "byte (protocol, " << name; break; case t_base_type::TYPE_I16: out << "i16 (protocol, " << name; break; case t_base_type::TYPE_I32: out << "i32 (protocol, " << name; break; case t_base_type::TYPE_I64: out << "i64 (protocol, " << name; break; case t_base_type::TYPE_DOUBLE: out << "double (protocol, " << name; break; case t_base_type::TYPE_STRING: if (type->is_binary()) { out << "binary (protocol, " << name << " ? ((GByteArray *) " << name << ")->data : NULL, " << name << " ? ((GByteArray *) " << name << ")->len : 0"; } else { out << "string (protocol, " << name; } break; default: throw "compiler error: no C writer for base type " + t_base_type::t_base_name(tbase) + name; } } else { out << "i32 (protocol, (gint32) " << name; } out << ", error)) < 0)" << '\n' << indent() << " return " << error_ret << ";" << '\n' << indent() << "xfer += ret;" << '\n' << '\n'; } else { throw std::logic_error("DO NOT KNOW HOW TO SERIALIZE FIELD '" + name + "' TYPE '" + type_name(type)); } } void t_c_glib_generator::generate_serialize_struct(ostream& out, t_struct* tstruct, string prefix, int error_ret) { (void)tstruct; out << indent() << "if ((ret = thrift_struct_write (THRIFT_STRUCT (" << prefix << "), protocol, error)) < 0)" << '\n' << indent() << " return " << error_ret << ";" << '\n' << indent() << "xfer += ret;" << '\n' << '\n'; } void t_c_glib_generator::generate_serialize_container(ostream& out, t_type* ttype, string prefix, int error_ret) { scope_up(out); if (ttype->is_map()) { t_type* tkey = ((t_map*)ttype)->get_key_type(); t_type* tval = ((t_map*)ttype)->get_val_type(); string tkey_name = type_name(tkey); string tval_name = type_name(tval); string tkey_ptr; string tval_ptr; string keyname = tmp("key"); string valname = tmp("val"); declore_local_variable_for_write(out, tkey, keyname); declore_local_variable_for_write(out, tval, valname); /* If either the key or value type is a typedef, find its underlying type so we can correctly determine how to generate a pointer to it */ tkey = get_true_type(tkey); tval = get_true_type(tval); tkey_ptr = tkey->is_string() || !tkey->is_base_type() ? "" : "*"; tval_ptr = tval->is_string() || !tval->is_base_type() ? "" : "*"; /* * Some ugliness here. To maximize backwards compatibility, we * avoid using GHashTableIter and instead get a GList of all keys, * then copy it into a array on the stack, and free it. * This is because we may exit early before we get a chance to free the * GList. */ out << indent() << "GList *key_list = NULL, *iter = NULL;" << '\n' << indent() << tkey_name << tkey_ptr << "* keys;" << '\n' << indent() << "int i = 0, key_count;" << '\n' << '\n' << indent() << "if ((ret = thrift_protocol_write_map_begin (protocol, " << type_to_enum(tkey) << ", " << type_to_enum(tval) << ", " << prefix << " ? " << "(gint32) g_hash_table_size ((GHashTable *) " << prefix << ") : 0" << ", error)) < 0)" << '\n'; indent_up(); out << indent() << "return " << error_ret << ";" << '\n'; indent_down(); out << indent() << "xfer += ret;" << '\n' << indent() << "if (" << prefix << ")" << '\n' << indent() << " g_hash_table_foreach ((GHashTable *) " << prefix << ", thrift_hash_table_get_keys, &key_list);" << '\n' << indent() << "key_count = g_list_length (key_list);" << '\n' << indent() << "keys = g_newa (" << tkey_name << tkey_ptr << ", key_count);" << '\n' << indent() << "for (iter = g_list_first (key_list); iter; " "iter = iter->next)" << '\n'; indent_up(); out << indent() << "keys[i++] = (" << tkey_name << tkey_ptr << ") iter->data;" << '\n'; indent_down(); out << indent() << "g_list_free (key_list);" << '\n' << '\n' << indent() << "for (i = 0; i < key_count; ++i)" << '\n'; scope_up(out); out << indent() << keyname << " = keys[i];" << '\n' << indent() << valname << " = (" << tval_name << tval_ptr << ") g_hash_table_lookup (((GHashTable *) " << prefix << "), (gpointer) " << keyname << ");" << '\n' << '\n'; generate_serialize_map_element(out, (t_map*)ttype, tkey_ptr + " " + keyname, tval_ptr + " " + valname, error_ret); scope_down(out); out << indent() << "if ((ret = thrift_protocol_write_map_end (protocol, " "error)) < 0)" << '\n'; indent_up(); out << indent() << "return " << error_ret << ";" << '\n'; indent_down(); out << indent() << "xfer += ret;" << '\n'; } else if (ttype->is_set()) { t_type* telem = ((t_set*)ttype)->get_elem_type(); string telem_name = type_name(telem); string telem_ptr = telem->is_string() || !telem->is_base_type() ? "" : "*"; out << indent() << "GList *key_list = NULL, *iter = NULL;" << '\n' << indent() << telem_name << telem_ptr << "* keys;" << '\n' << indent() << "int i = 0, key_count;" << '\n' << indent() << telem_name << telem_ptr << " elem;" << '\n' << indent() << "gpointer value;" << '\n' << indent() << "THRIFT_UNUSED_VAR (value);" << '\n' << '\n' << indent() << "if ((ret = thrift_protocol_write_set_begin (protocol, " << type_to_enum(telem) << ", " << prefix << " ? " << "(gint32) g_hash_table_size ((GHashTable *) " << prefix << ") : 0" << ", error)) < 0)" << '\n'; indent_up(); out << indent() << "return " << error_ret << ";" << '\n'; indent_down(); out << indent() << "xfer += ret;" << '\n' << indent() << "if (" << prefix << ")" << '\n' << indent() << " g_hash_table_foreach ((GHashTable *) " << prefix << ", thrift_hash_table_get_keys, &key_list);" << '\n' << indent() << "key_count = g_list_length (key_list);" << '\n' << indent() << "keys = g_newa (" << telem_name << telem_ptr << ", key_count);" << '\n' << indent() << "for (iter = g_list_first (key_list); iter; " "iter = iter->next)" << '\n'; indent_up(); out << indent() << "keys[i++] = (" << telem_name << telem_ptr << ") iter->data;" << '\n'; indent_down(); out << indent() << "g_list_free (key_list);" << '\n' << '\n' << indent() << "for (i = 0; i < key_count; ++i)" << '\n'; scope_up(out); out << indent() << "elem = keys[i];" << '\n' << indent() << "value = (gpointer) g_hash_table_lookup " "(((GHashTable *) " << prefix << "), (gpointer) elem);" << '\n' << '\n'; generate_serialize_set_element(out, (t_set*)ttype, telem_ptr + "elem", error_ret); scope_down(out); out << indent() << "if ((ret = thrift_protocol_write_set_end (protocol, " "error)) < 0)" << '\n'; indent_up(); out << indent() << "return " << error_ret << ";" << '\n'; indent_down(); out << indent() << "xfer += ret;" << '\n'; } else if (ttype->is_list()) { string length = "(" + prefix + " ? " + prefix + "->len : 0)"; string i = tmp("i"); out << indent() << "guint " << i << ";" << '\n' << '\n' << indent() << "if ((ret = thrift_protocol_write_list_begin (protocol, " << type_to_enum(((t_list*)ttype)->get_elem_type()) << ", (gint32) " << length << ", error)) < 0)" << '\n'; indent_up(); out << indent() << "return " << error_ret << ";" << '\n'; indent_down(); out << indent() << "xfer += ret;" << '\n' << indent() << "for (" << i << " = 0; " << i << " < " << length << "; " << i << "++)" << '\n'; scope_up(out); generate_serialize_list_element(out, (t_list*)ttype, prefix, i, error_ret); scope_down(out); out << indent() << "if ((ret = thrift_protocol_write_list_end (protocol, " "error)) < 0)" << '\n'; indent_up(); out << indent() << "return " << error_ret << ";" << '\n'; indent_down(); out << indent() << "xfer += ret;" << '\n'; } scope_down(out); } void t_c_glib_generator::generate_serialize_map_element(ostream& out, t_map* tmap, string key, string value, int error_ret) { t_field kfield(tmap->get_key_type(), key); generate_serialize_field(out, &kfield, "", "", error_ret); t_field vfield(tmap->get_val_type(), value); generate_serialize_field(out, &vfield, "", "", error_ret); } void t_c_glib_generator::generate_serialize_set_element(ostream& out, t_set* tset, string element, int error_ret) { t_field efield(tset->get_elem_type(), element); generate_serialize_field(out, &efield, "", "", error_ret); } void t_c_glib_generator::generate_serialize_list_element(ostream& out, t_list* tlist, string list, string index, int error_ret) { t_type* ttype = get_true_type(tlist->get_elem_type()); // cast to non-const string cast = ""; string name = "g_ptr_array_index ((GPtrArray *) " + list + ", " + index + ")"; if (ttype->is_void()) { throw std::runtime_error("compiler error: list element type cannot be void"); } else if (is_numeric(ttype)) { name = "g_array_index (" + list + ", " + base_type_name(ttype) + ", " + index + ")"; } else if (ttype->is_string()) { cast = "(gchar*)"; } else if (ttype->is_map() || ttype->is_set()) { cast = "(GHashTable*)"; } else if (ttype->is_list()) { t_type* etype = ((t_list*)ttype)->get_elem_type(); if (etype->is_void()) { throw std::runtime_error("compiler error: list element type cannot be void"); } cast = is_numeric(etype) ? "(GArray*)" : "(GPtrArray*)"; } t_field efield(ttype, "(" + cast + name + ")"); generate_serialize_field(out, &efield, "", "", error_ret); } /* deserializes a field of any type. */ void t_c_glib_generator::generate_deserialize_field(ostream& out, t_field* tfield, string prefix, string suffix, int error_ret, bool allocate) { t_type* type = get_true_type(tfield->get_type()); if (type->is_void()) { throw std::runtime_error("CANNOT GENERATE DESERIALIZE CODE FOR void TYPE: " + prefix + tfield->get_name()); } string name = prefix + tfield->get_name() + suffix; if (type->is_struct() || type->is_xception()) { generate_deserialize_struct(out, (t_struct*)type, name, error_ret, allocate); } else if (type->is_container()) { generate_deserialize_container(out, type, name, error_ret); } else if (type->is_base_type()) { t_base_type::t_base tbase = ((t_base_type*)type)->get_base(); if (tbase == t_base_type::TYPE_STRING) { indent(out) << "if (" << name << " != NULL)" << '\n' << indent() << "{" << '\n'; indent_up(); indent(out) << "g_free(" << name << ");" << '\n' << indent() << name << " = NULL;" << '\n'; indent_down(); indent(out) << "}" << '\n' << '\n'; } indent(out) << "if ((ret = thrift_protocol_read_"; switch (tbase) { case t_base_type::TYPE_VOID: throw "compiler error: cannot serialize void field in a struct: " + name; break; case t_base_type::TYPE_STRING: if (type->is_binary()) { out << "binary (protocol, &data, &len"; } else { out << "string (protocol, &" << name; } break; case t_base_type::TYPE_BOOL: out << "bool (protocol, &" << name; break; case t_base_type::TYPE_I8: out << "byte (protocol, &" << name; break; case t_base_type::TYPE_I16: out << "i16 (protocol, &" << name; break; case t_base_type::TYPE_I32: out << "i32 (protocol, &" << name; break; case t_base_type::TYPE_I64: out << "i64 (protocol, &" << name; break; case t_base_type::TYPE_DOUBLE: out << "double (protocol, &" << name; break; default: throw "compiler error: no C reader for base type " + t_base_type::t_base_name(tbase) + name; } out << ", error)) < 0)" << '\n'; out << indent() << " return " << error_ret << ";" << '\n' << indent() << "xfer += ret;" << '\n'; // load the byte array with the data if (tbase == t_base_type::TYPE_STRING && type->is_binary()) { indent(out) << name << " = g_byte_array_new();" << '\n'; indent(out) << "g_byte_array_append (" << name << ", (guint8 *) data, (guint) len);" << '\n'; indent(out) << "g_free (data);" << '\n'; } } else if (type->is_enum()) { string t = tmp("ecast"); out << indent() << "gint32 " << t << ";" << '\n' << indent() << "if ((ret = thrift_protocol_read_i32 (protocol, &" << t << ", error)) < 0)" << '\n' << indent() << " return " << error_ret << ";" << '\n' << indent() << "xfer += ret;" << '\n' << indent() << name << " = (" << type_name(type) << ")" << t << ";" << '\n'; } else { throw std::logic_error("DO NOT KNOW HOW TO SERIALIZE FIELD '" + tfield->get_name() + "' TYPE '" + type_name(type)); } // if the type is not required and this is a thrift struct (no prefix), // set the isset variable. if the type is required, then set the // local variable indicating the value was set, so that we can do // validation later. if (prefix != "" && tfield->get_req() != t_field::T_REQUIRED) { indent(out) << prefix << "__isset_" << tfield->get_name() << suffix << " = TRUE;" << '\n'; } else if (prefix != "" && tfield->get_req() == t_field::T_REQUIRED) { indent(out) << "isset_" << tfield->get_name() << " = TRUE;" << '\n'; } } void t_c_glib_generator::generate_deserialize_struct(ostream& out, t_struct* tstruct, string prefix, int error_ret, bool allocate) { string name_uc = to_upper_case(initial_caps_to_underscores(tstruct->get_name())); if (tstruct->is_xception()) { out << indent() << "/* This struct is an exception */" << '\n'; allocate = true; } if (allocate) { out << indent() << "if ( " << prefix << " != NULL)" << '\n' << indent() << "{" << '\n'; indent_up(); out << indent() << "g_object_unref (" << prefix << ");" << '\n'; indent_down(); out << indent() << "}" << '\n' << indent() << prefix << " = g_object_new (" << this->nspace_uc << "TYPE_" << name_uc << ", NULL);" << '\n'; } out << indent() << "if ((ret = thrift_struct_read (THRIFT_STRUCT (" << prefix << "), protocol, error)) < 0)" << '\n' << indent() << "{" << '\n'; indent_up(); if (allocate) { indent(out) << "g_object_unref (" << prefix << ");" << '\n'; if (tstruct->is_xception()) { indent(out) << prefix << " = NULL;" << '\n'; } } out << indent() << "return " << error_ret << ";" << '\n'; indent_down(); out << indent() << "}" << '\n' << indent() << "xfer += ret;" << '\n'; } void t_c_glib_generator::generate_deserialize_container(ostream& out, t_type* ttype, string prefix, int error_ret) { scope_up(out); if (ttype->is_map()) { out << indent() << "guint32 size;" << '\n' << indent() << "guint32 i;" << '\n' << indent() << "ThriftType key_type;" << '\n' << indent() << "ThriftType value_type;" << '\n' << '\n' << indent() << "/* read the map begin marker */" << '\n' << indent() << "if ((ret = thrift_protocol_read_map_begin (protocol, " "&key_type, &value_type, &size, error)) < 0)" << '\n'; indent_up(); out << indent() << "return " << error_ret << ";" << '\n'; indent_down(); out << indent() << "xfer += ret;" << '\n' << '\n'; // iterate over map elements out << indent() << "/* iterate through each of the map's fields */" << '\n' << indent() << "for (i = 0; i < size; i++)" << '\n'; scope_up(out); generate_deserialize_map_element(out, (t_map*)ttype, prefix, error_ret); scope_down(out); out << '\n'; // read map end out << indent() << "/* read the map end marker */" << '\n' << indent() << "if ((ret = thrift_protocol_read_map_end (protocol, " "error)) < 0)" << '\n'; indent_up(); out << indent() << "return " << error_ret << ";" << '\n'; indent_down(); out << indent() << "xfer += ret;" << '\n'; } else if (ttype->is_set()) { out << indent() << "guint32 size;" << '\n' << indent() << "guint32 i;" << '\n' << indent() << "ThriftType element_type;" << '\n' << '\n' << indent() << "if ((ret = thrift_protocol_read_set_begin (protocol, " "&element_type, &size, error)) < 0)" << '\n'; indent_up(); out << indent() << "return " << error_ret << ";" << '\n'; indent_down(); out << indent() << "xfer += ret;" << '\n' << '\n'; // iterate over the elements out << indent() << "/* iterate through the set elements */" << '\n' << indent() << "for (i = 0; i < size; ++i)" << '\n'; scope_up(out); generate_deserialize_set_element(out, (t_set*)ttype, prefix, error_ret); scope_down(out); // read set end out << indent() << "if ((ret = thrift_protocol_read_set_end (protocol, " "error)) < 0)" << '\n'; indent_up(); out << indent() << "return " << error_ret << ";" << '\n'; indent_down(); out << indent() << "xfer += ret;" << '\n' << '\n'; } else if (ttype->is_list()) { out << indent() << "guint32 size;" << '\n' << indent() << "guint32 i;" << '\n' << indent() << "ThriftType element_type;" << '\n' << '\n' << indent() << "if ((ret = thrift_protocol_read_list_begin (protocol, " "&element_type,&size, error)) < 0)" << '\n'; indent_up(); out << indent() << "return " << error_ret << ";" << '\n'; indent_down(); out << indent() << "xfer += ret;" << '\n' << '\n'; // iterate over the elements out << indent() << "/* iterate through list elements */" << '\n' << indent() << "for (i = 0; i < size; i++)" << '\n'; scope_up(out); generate_deserialize_list_element(out, (t_list*)ttype, prefix, "i", error_ret); scope_down(out); // read list end out << indent() << "if ((ret = thrift_protocol_read_list_end (protocol, " "error)) < 0)" << '\n'; indent_up(); out << indent() << "return " << error_ret << ";" << '\n'; indent_down(); out << indent() << "xfer += ret;" << '\n'; } scope_down(out); } void t_c_glib_generator::declare_local_variable(ostream& out, t_type* ttype, string& name, bool for_hash_table) { string tname = type_name(ttype); /* If the given type is a typedef, find its underlying type so we can correctly determine how to generate a pointer to it */ ttype = get_true_type(ttype); string ptr = !is_numeric(ttype) ? "" : "*"; if (ttype->is_map()) { t_map* tmap = (t_map*)ttype; out << indent() << tname << ptr << " " << name << " = " << generate_new_hash_from_type(tmap->get_key_type(), tmap->get_val_type()) << '\n'; } else if (ttype->is_list()) { t_list* tlist = (t_list*)ttype; out << indent() << tname << ptr << " " << name << " = " << generate_new_array_from_type(tlist->get_elem_type()) << '\n'; } else if (for_hash_table && ttype->is_enum()) { out << indent() << tname << " " << name << ";" << '\n'; } else { out << indent() << tname << ptr << " " << name << (ptr != "" ? " = g_new (" + tname + ", 1)" : " = NULL") << ";" << '\n'; } } void t_c_glib_generator::declore_local_variable_for_write(ostream& out, t_type* ttype, string& name) { string tname = type_name(ttype); ttype = get_true_type(ttype); string ptr = ttype->is_string() || !ttype->is_base_type() ? " " : "* "; string init_val = ttype->is_enum() ? "" : " = NULL"; out << indent() << tname << ptr << name << init_val << ";" << '\n'; } void t_c_glib_generator::generate_deserialize_map_element(ostream& out, t_map* tmap, string prefix, int error_ret) { t_type* tkey = tmap->get_key_type(); t_type* tval = tmap->get_val_type(); string keyname = tmp("key"); string valname = tmp("val"); declare_local_variable(out, tkey, keyname, true); declare_local_variable(out, tval, valname, true); /* If either the key or value type is a typedef, find its underlying type so we can correctly determine how to generate a pointer to it */ tkey = get_true_type(tkey); tval = get_true_type(tval); string tkey_ptr = tkey->is_string() || !tkey->is_base_type() ? "" : "*"; string tval_ptr = tval->is_string() || !tval->is_base_type() ? "" : "*"; // deserialize the fields of the map element t_field fkey(tkey, tkey_ptr + keyname); generate_deserialize_field(out, &fkey, "", "", error_ret); t_field fval(tval, tval_ptr + valname); generate_deserialize_field(out, &fval, "", "", error_ret); indent(out) << "if (" << prefix << " && " << keyname << ")" << '\n'; indent_up(); indent(out) << "g_hash_table_insert ((GHashTable *)" << prefix << ", (gpointer) " << keyname << ", (gpointer) " << valname << ");" << '\n'; indent_down(); } void t_c_glib_generator::generate_deserialize_set_element(ostream& out, t_set* tset, string prefix, int error_ret) { t_type* telem = tset->get_elem_type(); string elem = tmp("_elem"); string telem_ptr = telem->is_string() || !telem->is_base_type() ? "" : "*"; declare_local_variable(out, telem, elem, true); t_field felem(telem, telem_ptr + elem); generate_deserialize_field(out, &felem, "", "", error_ret); indent(out) << "if (" << prefix << " && " << elem << ")" << '\n'; indent_up(); indent(out) << "g_hash_table_insert ((GHashTable *) " << prefix << ", (gpointer) " << elem << ", (gpointer) " << elem << ");" << '\n'; indent_down(); } void t_c_glib_generator::generate_deserialize_list_element(ostream& out, t_list* tlist, string prefix, string index, int error_ret) { (void)index; t_type* ttype = get_true_type(tlist->get_elem_type()); string elem = tmp("_elem"); string telem_ptr = !is_numeric(ttype) ? "" : "*"; declare_local_variable(out, ttype, elem, false); t_field felem(ttype, telem_ptr + elem); generate_deserialize_field(out, &felem, "", "", error_ret); if (ttype->is_void()) { throw std::runtime_error("compiler error: list element type cannot be void"); } else if (is_numeric(ttype)) { indent(out) << "g_array_append_vals (" << prefix << ", " << elem << ", 1);" << '\n'; indent(out) << "g_free (" << elem << ");" << '\n'; } else { indent(out) << "g_ptr_array_add (" << prefix << ", " << elem << ");" << '\n'; } } string t_c_glib_generator::generate_free_func_from_type(t_type* ttype) { if (ttype == nullptr) return "NULL"; if (ttype->is_base_type()) { t_base_type::t_base tbase = ((t_base_type*)ttype)->get_base(); switch (tbase) { case t_base_type::TYPE_VOID: throw "compiler error: cannot determine hash type"; break; 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: return "g_free"; case t_base_type::TYPE_STRING: if (((t_base_type*)ttype)->is_binary()) { return "thrift_string_free"; } return "g_free"; default: throw "compiler error: no hash table info for type"; } } else if (ttype->is_enum()) { return "NULL"; } else if (ttype->is_map() || ttype->is_set()) { return "(GDestroyNotify) thrift_safe_hash_table_destroy"; } else if (ttype->is_struct()) { return "g_object_unref"; } else if (ttype->is_list()) { t_type* etype = ((t_list*)ttype)->get_elem_type(); if (etype->is_base_type()) { t_base_type::t_base tbase = ((t_base_type*)etype)->get_base(); switch (tbase) { case t_base_type::TYPE_VOID: throw "compiler error: cannot determine array type"; break; 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: return "(GDestroyNotify) g_array_unref"; case t_base_type::TYPE_STRING: return "(GDestroyNotify) g_ptr_array_unref"; default: throw "compiler error: no array info for type"; } } else if (etype->is_container() || etype->is_struct()) { return "(GDestroyNotify) g_ptr_array_unref"; ; } else if (etype->is_enum()) { return "(GDestroyNotify) g_array_unref"; } printf("Type not expected inside the array: %s\n", etype->get_name().c_str()); throw "Type not expected inside array"; } else if (ttype->is_typedef()) { return generate_free_func_from_type(((t_typedef*)ttype)->get_type()); } printf("Type not expected: %s\n", ttype->get_name().c_str()); throw "Type not expected"; } string t_c_glib_generator::generate_hash_func_from_type(t_type* ttype) { if (ttype == nullptr) return "NULL"; if (ttype->is_base_type()) { t_base_type::t_base tbase = ((t_base_type*)ttype)->get_base(); switch (tbase) { case t_base_type::TYPE_VOID: throw "compiler error: cannot determine hash type"; break; case t_base_type::TYPE_BOOL: return "thrift_boolean_hash"; case t_base_type::TYPE_I8: return "thrift_int8_hash"; case t_base_type::TYPE_I16: return "thrift_int16_hash"; case t_base_type::TYPE_I32: return "g_int_hash"; case t_base_type::TYPE_I64: return "g_int64_hash"; case t_base_type::TYPE_DOUBLE: return "g_double_hash"; case t_base_type::TYPE_STRING: return "g_str_hash"; default: throw "compiler error: no hash table info for type"; } } else if (ttype->is_enum()) { return "g_direct_hash"; } else if (ttype->is_container() || ttype->is_struct()) { return "g_direct_hash"; } else if (ttype->is_typedef()) { return generate_hash_func_from_type(((t_typedef*)ttype)->get_type()); } printf("Type not expected: %s\n", ttype->get_name().c_str()); throw "Type not expected"; } string t_c_glib_generator::generate_cmp_func_from_type(t_type* ttype) { if (ttype == nullptr) return "NULL"; if (ttype->is_base_type()) { t_base_type::t_base tbase = ((t_base_type*)ttype)->get_base(); switch (tbase) { case t_base_type::TYPE_VOID: throw "compiler error: cannot determine hash type"; break; case t_base_type::TYPE_BOOL: return "thrift_boolean_equal"; case t_base_type::TYPE_I8: return "thrift_int8_equal"; case t_base_type::TYPE_I16: return "thrift_int16_equal"; case t_base_type::TYPE_I32: return "g_int_equal"; case t_base_type::TYPE_I64: return "g_int64_equal"; case t_base_type::TYPE_DOUBLE: return "g_double_equal"; case t_base_type::TYPE_STRING: return "g_str_equal"; default: throw "compiler error: no hash table info for type"; } } else if (ttype->is_enum()) { return "g_direct_equal"; } else if (ttype->is_container() || ttype->is_struct()) { return "g_direct_equal"; } else if (ttype->is_typedef()) { return generate_cmp_func_from_type(((t_typedef*)ttype)->get_type()); } printf("Type not expected: %s\n", ttype->get_name().c_str()); throw "Type not expected"; } string t_c_glib_generator::generate_new_hash_from_type(t_type* key, t_type* value) { string hash_func = generate_hash_func_from_type(key); string cmp_func = generate_cmp_func_from_type(key); string key_free_func = generate_free_func_from_type(key); string value_free_func = generate_free_func_from_type(value); return "g_hash_table_new_full (" + hash_func + ", " + cmp_func + ", " + key_free_func + ", " + value_free_func + ");"; } string t_c_glib_generator::generate_new_array_from_type(t_type* ttype) { if (ttype->is_void()) { throw std::runtime_error("compiler error: cannot determine array type"); } else if (is_numeric(ttype)) { return "g_array_new (0, 1, sizeof (" + base_type_name(ttype) + "));"; } else { string free_func = generate_free_func_from_type(ttype); return "g_ptr_array_new_with_free_func (" + free_func + ");"; } } /*************************************** * UTILITY FUNCTIONS * ***************************************/ /** * Upper case a string. */ string to_upper_case(string name) { string s(name); std::transform(s.begin(), s.end(), s.begin(), ::toupper); return s; } /** * Lower case a string. */ string to_lower_case(string name) { string s(name); std::transform(s.begin(), s.end(), s.begin(), ::tolower); return s; } /** * Makes a string friendly to C code standards by lowercasing and adding * underscores, with the exception of the first character. For example: * * Input: "ZomgCamelCase" * Output: "zomg_camel_case" */ string initial_caps_to_underscores(string name) { string ret; const char* tmp = name.c_str(); int pos = 0; /* the first character isn't underscored if uppercase, just lowercased */ ret += tolower(tmp[pos]); pos++; for (unsigned int i = pos; i < name.length(); i++) { char lc = tolower(tmp[i]); if (lc != tmp[i]) { ret += '_'; } ret += lc; } return ret; } /** * Performs the reverse operation of initial_caps_to_underscores: The first * character of the string is made uppercase, along with each character that * follows an underscore (which is removed). Useful for converting Thrift * service-method names into GObject-style class names. * * Input: "zomg_camel_case" * Output: "ZomgCamelCase" */ string underscores_to_initial_caps(string name) { string ret; const char* tmp = name.c_str(); bool uppercase_next = true; for (unsigned int i = 0; i < name.length(); i++) { char c = tmp[i]; if (c == '_') { uppercase_next = true; } else { if (uppercase_next) { ret += toupper(c); uppercase_next = false; } else { ret += c; } } } return ret; } /* register this generator with the main program */ std::string t_c_glib_generator::display_name() const { return "C, using GLib"; } THRIFT_REGISTER_GENERATOR(c_glib, "C, using GLib", "")