http://git-wip-us.apache.org/repos/asf/incubator-hawq/blob/d709f67d/depends/thirdparty/thrift/compiler/cpp/src/generate/t_c_glib_generator.cc ---------------------------------------------------------------------- diff --git a/depends/thirdparty/thrift/compiler/cpp/src/generate/t_c_glib_generator.cc b/depends/thirdparty/thrift/compiler/cpp/src/generate/t_c_glib_generator.cc new file mode 100644 index 0000000..47aa4d2 --- /dev/null +++ b/depends/thirdparty/thrift/compiler/cpp/src/generate/t_c_glib_generator.cc @@ -0,0 +1,4565 @@ +/* + * 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 <fstream> +#include <iostream> +#include <string> +#include <vector> + +#include <ctype.h> + +#include "platform.h" +#include "t_oop_generator.h" + +using std::map; +using std::ofstream; +using std::ostringstream; +using std::string; +using std::stringstream; +using std::vector; + +static const string endl = "\n"; // avoid ostream << std::endl flushes + +/* 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<string, string>& parsed_options, + const string& option_string) + : t_oop_generator(program) { + (void)parsed_options; + (void)option_string; + /* set the output directory */ + this->out_dir_base_ = "gen-c_glib"; + + /* 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(); + void close_generator(); + + /* generation functions */ + void generate_typedef(t_typedef* ttypedef); + void generate_enum(t_enum* tenum); + void generate_consts(vector<t_const*> consts); + void generate_struct(t_struct* tstruct); + void generate_service(t_service* tservice); + void generate_xception(t_struct* tstruct); + +private: + /* file streams */ + ofstream f_types_; + ofstream f_types_impl_; + ofstream f_header_; + ofstream f_service_; + + /* namespace variables */ + string nspace; + string nspace_u; + string nspace_uc; + string nspace_lc; + + /* helper functions */ + bool is_complex_type(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_base_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 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(ofstream& out, t_type* ttype, string& base_name); + + /* generation functions */ + void generate_const_initializer(string name, t_type* type, t_const_value* value); + 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(ofstream& out, + t_struct* tstruct, + string this_name, + string this_get = "", + bool is_function = true); + void generate_struct_reader(ofstream& out, + t_struct* tstruct, + string this_name, + string this_get = "", + bool is_function = true); + + void generate_serialize_field(ofstream& out, + t_field* tfield, + string prefix, + string suffix, + int error_ret); + void generate_serialize_struct(ofstream& out, t_struct* tstruct, string prefix, int error_ret); + void generate_serialize_container(ofstream& out, t_type* ttype, string prefix, int error_ret); + void generate_serialize_map_element(ofstream& out, + t_map* tmap, + string key, + string value, + int error_ret); + void generate_serialize_set_element(ofstream& out, t_set* tset, string element, int error_ret); + void generate_serialize_list_element(ofstream& out, + t_list* tlist, + string list, + string index, + int error_ret); + + void generate_deserialize_field(ofstream& out, + t_field* tfield, + string prefix, + string suffix, + int error_ret, + bool allocate = true); + void generate_deserialize_struct(ofstream& out, + t_struct* tstruct, + string prefix, + int error_ret, + bool allocate = true); + void generate_deserialize_container(ofstream& out, t_type* ttype, string prefix, int error_ret); + void generate_deserialize_map_element(ofstream& out, t_map* tmap, string prefix, int error_ret); + void generate_deserialize_set_element(ofstream& out, t_set* tset, string prefix, int error_ret); + void generate_deserialize_list_element(ofstream& 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" << endl << "#define " + << this->nspace_uc << program_name_uc << "_TYPES_H" << endl << endl; + + /* include base types */ + f_types_ << "/* base includes */" << endl << "#include <glib-object.h>" << endl + << "#include <thrift/c_glib/thrift_struct.h>" << endl + << "#include <thrift/c_glib/protocol/thrift_protocol.h>" << endl; + + /* include other thrift includes */ + const vector<t_program*>& includes = program_->get_includes(); + for (size_t i = 0; i < includes.size(); ++i) { + f_types_ << "/* other thrift includes */" << endl << "#include \"" << this->nspace_lc + << initial_caps_to_underscores(includes[i]->get_name()) << "_types.h\"" << endl; + } + f_types_ << endl; + + /* include custom headers */ + const vector<string>& c_includes = program_->get_c_includes(); + f_types_ << "/* custom thrift includes */" << endl; + for (size_t i = 0; i < c_includes.size(); ++i) { + if (c_includes[i][0] == '<') { + f_types_ << "#include " << c_includes[i] << endl; + } else { + f_types_ << "#include \"" << c_includes[i] << "\"" << endl; + } + } + f_types_ << endl; + + /* include math.h (for "INFINITY") in the implementation file, in case we + encounter a struct with a member of type double */ + f_types_impl_ << endl << "#include <math.h>" << endl; + + // include the types file + f_types_impl_ << endl << "#include \"" << this->nspace_lc << program_name_u << "_types.h\"" + << endl << "#include <thrift/c_glib/thrift.h>" << endl << endl; + + f_types_ << "/* begin types */" << endl << endl; +} + +/** + * 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 */" << endl; + + /* close output file */ + f_types_.close(); + f_types_impl_.close(); +} + +/** + * Generates a Thrift typedef in C code. For example: + * + * Thrift: + * typedef map<i32,i32> 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() << ";" << endl << endl; +} + +/** + * 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 << " {" << endl; + + indent_up(); + + vector<t_enum_value*> constants = tenum->get_constants(); + vector<t_enum_value*>::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_ << "," << endl; + } + + f_types_ << indent() << this->nspace_uc << name_uc << "_" << (*c_iter)->get_name(); + f_types_ << " = " << (*c_iter)->get_value(); + } + + indent_down(); + f_types_ << endl << "};" << endl << "typedef enum _" << this->nspace << name << " " + << this->nspace << name << ";" << endl << endl; + + f_types_ << "/* return the name of the constant */" << endl; + f_types_ << "const char *" << endl; + f_types_ << "toString_" << name << "(int value); " << endl << endl; + ; + f_types_impl_ << "/* return the name of the constant */" << endl; + f_types_impl_ << "const char *" << endl; + f_types_impl_ << "toString_" << name << "(int value) " << endl; + f_types_impl_ << "{" << endl; + f_types_impl_ << " static __thread char buf[16] = {0};" << endl; + f_types_impl_ << " switch(value) {" << endl; + for (c_iter = constants.begin(); c_iter != constants.end(); ++c_iter) { + f_types_impl_ << " case " << this->nspace_uc << name_uc << "_" << (*c_iter)->get_name() << ":" + << "return \"" << this->nspace_uc << name_uc << "_" << (*c_iter)->get_name() + << "\";" << endl; + } + f_types_impl_ << " default: g_snprintf(buf, 16, \"%d\", value); return buf;" << endl; + f_types_impl_ << " }" << endl; + f_types_impl_ << "}" << endl << endl; +} + +/** + * Generates Thrift constants in C code. + */ +void t_c_glib_generator::generate_consts(vector<t_const*> consts) { + f_types_ << "/* constants */" << endl; + f_types_impl_ << "/* constants */" << endl; + + vector<t_const*>::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(); + + f_types_ << indent() << "#define " << this->nspace_uc << name_uc << " " + << constant_value(name_lc, type, value) << endl; + + generate_const_initializer(name_lc, type, value); + } + + f_types_ << endl; + f_types_impl_ << endl; +} + +/** + * 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() << " */" << endl; + 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" << endl << "#define " << svcname_uc << "_H" << endl + << endl; + + // add standard includes + f_header_ << "#include <thrift/c_glib/processor/thrift_dispatch_processor.h>" << endl << endl; + f_header_ << "#include \"" << this->nspace_lc << program_name_lc << "_types.h\"" << endl; + + // if we are inheriting from another service, include its header + t_service* extends_service = tservice->get_extends(); + if (extends_service != NULL) { + f_header_ << "#include \"" << this->nspace_lc + << to_lower_case(initial_caps_to_underscores(extends_service->get_name())) << ".h\"" + << endl; + } + f_header_ << endl; + + // 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 <string.h>" << endl << "#include <thrift/c_glib/thrift.h>" << endl + << "#include <thrift/c_glib/thrift_application_exception.h>" << endl << "#include \"" + << filename << ".h\"" << endl << endl; + + // 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 */" << endl; + + // 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 */" << endl + << "typedef enum" << endl + << "{" << endl; + indent_up(); + f_types_ << indent() << this->nspace_uc << name_uc << "_ERROR_CODE" << endl; + indent_down(); + f_types_ << "} " << this->nspace << name << "Error;" << endl + << endl + << "GQuark " << this->nspace_lc << name_lc + << "_error_quark (void);" << endl + << "#define " << this->nspace_uc << name_uc << "_ERROR (" + << this->nspace_lc << name_lc << "_error_quark())" << endl + << endl + << endl; + + f_types_impl_ << "/* define the GError domain for exceptions */" << endl << "#define " + << this->nspace_uc << name_uc << "_ERROR_DOMAIN \"" << this->nspace_lc << name_lc + << "_error_quark\"" << endl << "GQuark" << endl << this->nspace_lc << name_lc + << "_error_quark (void)" << endl << "{" << endl + << " return g_quark_from_static_string (" << this->nspace_uc << name_uc + << "_ERROR_DOMAIN);" << endl << "}" << endl << endl; +} + +/******************** + * 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(); +} + +/** + * 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((t_base_type*)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()) { + // TODO: investigate other implementations besides GPtrArray + cname = "GPtrArray"; + 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"; + case t_base_type::TYPE_BOOL: + case t_base_type::TYPE_BYTE: + case t_base_type::TYPE_I16: + case t_base_type::TYPE_I32: + case t_base_type::TYPE_I64: + case t_base_type::TYPE_DOUBLE: + cname = "GArray"; + break; + case t_base_type::TYPE_STRING: + break; + default: + throw "compiler error: no array info for type"; + } + } + } + + /* 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 + string pname = this->nspace + 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_BYTE: + 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_base_type* type) { + t_base_type::t_base tbase = type->get_base(); + + switch (tbase) { + case t_base_type::TYPE_VOID: + return "void"; + case t_base_type::TYPE_STRING: + if (type->is_binary()) { + return "GByteArray *"; + } else { + return "gchar *"; + } + case t_base_type::TYPE_BOOL: + return "gboolean"; + case t_base_type::TYPE_BYTE: + 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 "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_BYTE: + 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"; + } + } 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_BYTE: + 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<t_const_value*>& list = value->get_list(); + vector<t_const_value*>::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_BYTE: + 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: + 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 (type->is_struct() || type->is_xception() || type->is_list() || type->is_set() + || type->is_map()) { + 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<t_field*>& fields = tstruct->get_members(); + vector<t_field*>::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<t_field*>& fields = tstruct->get_members(); + vector<t_field*>::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_BYTE: + 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; +} + +/** + * Generates C code that initializes complex constants. + */ +void t_c_glib_generator::generate_const_initializer(string name, + t_type* type, + t_const_value* value) { + 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); + + if (type->is_struct() || type->is_xception()) { + const vector<t_field*>& fields = ((t_struct*)type)->get_members(); + vector<t_field*>::const_iterator f_iter; + const map<t_const_value*, t_const_value*>& val = value->get_map(); + map<t_const_value*, t_const_value*>::const_iterator v_iter; + 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 = NULL; + 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(); + } + } + if (field_type == NULL) { + 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) << ";" << endl + << " constant->__isset_" << v_iter->first->get_string() + << " = TRUE;" << endl; + } + + // implement the initializer + f_types_impl_ << "static " << this->nspace << type->get_name() << " *" + << endl + << this->nspace_lc << name_lc << "_constant (void)" << endl; + scope_up(f_types_impl_); + f_types_impl_ << indent() << "static " << this->nspace << type->get_name() + << " *constant = NULL;" << endl + << indent() << "if (constant == NULL)" << endl; + scope_up(f_types_impl_); + f_types_impl_ << indent() << "constant = g_object_new (" << this->nspace_uc + << "TYPE_" << type_uc << ", NULL);" << endl + << initializers.str(); + scope_down(f_types_impl_); + f_types_impl_ << indent() << "return constant;" << endl; + scope_down(f_types_impl_); + f_types_impl_ << endl; + } else if (type->is_list()) { + string list_type = "GPtrArray *"; + // TODO: This initialization should contain a free function for container + string list_initializer = "g_ptr_array_new();"; + string list_appender = "g_ptr_array_add"; + bool list_variable = false; + + t_type* etype = ((t_list*)type)->get_elem_type(); + const vector<t_const_value*>& val = value->get_list(); + vector<t_const_value*>::const_iterator v_iter; + 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_BYTE: + 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"; + } + } + + 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)) << ";" + << endl; + appenders << " " << list_appender << "(constant, " << fname << ");" + << endl; + } else { + appenders << " " << list_appender << "(constant, " + << constant_value(fname, (t_type*)etype, (*v_iter)) << ");" + << endl; + } + } + + f_types_impl_ << "static " << list_type << endl + << this->nspace_lc << name_lc << "_constant (void)" << endl; + scope_up(f_types_impl_); + f_types_impl_ << indent() << "static " << list_type << " constant = NULL;" + << endl + << indent() << "if (constant == NULL)" << endl; + scope_up(f_types_impl_); + if (!initializers.str().empty()) { + f_types_impl_ << initializers.str() + << endl; + } + f_types_impl_ << indent() << "constant = " << list_initializer << endl + << appenders.str(); + scope_down(f_types_impl_); + f_types_impl_ << indent() << "return constant;" << endl; + scope_down(f_types_impl_); + f_types_impl_ << endl; + } else if (type->is_set()) { + t_type* etype = ((t_set*)type)->get_elem_type(); + const vector<t_const_value*>& val = value->get_list(); + vector<t_const_value*>::const_iterator v_iter; + ostringstream initializers; + ostringstream appenders; + + for (v_iter = val.begin(); v_iter != val.end(); ++v_iter) { + string fname = tmp(name); + generate_const_initializer(fname, etype, (*v_iter)); + initializers << " " << type_name(etype) << " " << fname << " = " + << constant_value(fname, (t_type*)etype, (*v_iter)) << ";" + << endl; + appenders << " g_hash_table_insert (constant, &" << fname << ", &" + << fname << ");" << endl; + } + + f_types_impl_ << "static GHashTable *" << endl + << this->nspace_lc << name_lc << "_constant (void)" << endl; + scope_up(f_types_impl_); + f_types_impl_ << indent() << "static GHashTable *constant = NULL;" << endl + << indent() << "if (constant == NULL)" << endl; + scope_up(f_types_impl_); + f_types_impl_ << initializers.str() + << endl + // TODO: This initialization should contain a free function + // for elements + << indent() << "constant = g_hash_table_new (NULL, NULL);" + << endl + << appenders.str(); + scope_down(f_types_impl_); + f_types_impl_ << indent() << "return constant;" << endl; + scope_down(f_types_impl_); + f_types_impl_ << endl; + } else if (type->is_map()) { + t_type* ktype = ((t_map*)type)->get_key_type(); + t_type* vtype = ((t_map*)type)->get_val_type(); + const vector<t_const_value*>& val = value->get_list(); + vector<t_const_value*>::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)); + generate_const_initializer(vname, vtype, (*v_iter)); + + initializers << " " << type_name(ktype) << " " << kname << " = " + << constant_value(kname, (t_type*)ktype, (*v_iter)) << ";" + << endl + << " " << type_name(vtype) << " " << vname << " = " + << constant_value(vname, (t_type*)vtype, (*v_iter)) << ";" + << endl; + appenders << " g_hash_table_insert (constant, &" << fname << ", &" + << fname << ");" + << endl; + } + + f_types_impl_ << "static GHashTable *" << endl + << this->nspace_lc << name_lc << "_constant (void)" << endl; + scope_up(f_types_impl_); + f_types_impl_ << indent() << "static GHashTable *constant = NULL;" << endl + << indent() << "if (constant == NULL)" << endl; + scope_up(f_types_impl_); + f_types_impl_ << initializers.str() + << endl + // TODO: This initialization should contain a free function + // for elements + << indent() << "constant = g_hash_table_new (NULL, NULL);" + << endl + << appenders.str(); + scope_down(f_types_impl_); + f_types_impl_ << indent() << "return constant;" << endl; + scope_down(f_types_impl_); + f_types_impl_ << endl; + } +} + +/** + * 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<t_function*> functions = tservice->get_functions(); + vector<t_function*>::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<t_field*>& fields = xs->get_members(); + vector<t_field*>::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 NULL 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 */" << endl << "typedef struct _" + << this->nspace << service_name_ << "If " << this->nspace << service_name_ << "If; " + << " /* dummy object */" << endl << endl; + + // Generate the client interface object in the header. + f_header_ << "struct _" << this->nspace << service_name_ << "IfInterface" << endl << "{" << endl + << " GTypeInterface parent;" << endl << endl; + + /* write out the functions for this interface */ + indent_up(); + vector<t_function*> functions = tservice->get_functions(); + vector<t_function*>::const_iterator f_iter; + for (f_iter = functions.begin(); f_iter != functions.end(); ++f_iter) { + /* 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 << ";" << endl; + } + indent_down(); + + f_header_ << "};" << endl << "typedef struct _" << this->nspace << service_name_ << "IfInterface " + << this->nspace << service_name_ << "IfInterface;" << endl << endl; + + // generate all the interface boilerplate + f_header_ << "GType " << this->nspace_lc << service_name_lc << "_if_get_type (void);" << endl + << "#define " << this->nspace_uc << "TYPE_" << service_name_uc << "_IF " + << "(" << this->nspace_lc << service_name_lc << "_if_get_type())" << endl << "#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))" << endl + << "#define " << this->nspace_uc << "IS_" << service_name_uc << "_IF(obj) " + << "(G_TYPE_CHECK_INSTANCE_TYPE ((obj), " << this->nspace_uc << "TYPE_" + << service_name_uc << "_IF))" << endl << "#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))" << endl << endl; + + // 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 << ";" << endl; + } + f_header_ << endl; + + // Generate the client object instance definition in the header. + f_header_ << "/* " << service_name_ << " service client */" << endl << "struct _" << this->nspace + << service_name_ << "Client" << endl << "{" << endl << " " << parent_class_name + << " parent;" << endl; + if (!extends_service) { + // Define "input_protocol" and "output_protocol" properties only + // for base services; child service-client classes will inherit + // these + f_header_ << endl << " ThriftProtocol *input_protocol;" << endl + << " ThriftProtocol *output_protocol;" << endl; + } + f_header_ << "};" << endl << "typedef struct _" << this->nspace << service_name_ << "Client " + << this->nspace << service_name_ << "Client;" << endl << endl; + + // Generate the class definition in the header. + f_header_ << "struct _" << this->nspace << service_name_ << "ClientClass" << endl << "{" << endl + << " " << parent_class_name << "Class parent;" << endl << "};" << endl + << "typedef struct _" << this->nspace << service_name_ << "ClientClass " << this->nspace + << service_name_ << "ClientClass;" << endl << endl; + + // Create all the GObject boilerplate + f_header_ << "GType " << this->nspace_lc << service_name_lc << "_client_get_type (void);" << endl + << "#define " << this->nspace_uc << "TYPE_" << service_name_uc << "_CLIENT " + << "(" << this->nspace_lc << service_name_lc << "_client_get_type())" << endl + << "#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))" << endl + << "#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))" << endl << "#define " + << this->nspace_uc << service_name_uc << "_IS_CLIENT(obj) " + << "(G_TYPE_CHECK_INSTANCE_TYPE ((obj), " << this->nspace_uc << "TYPE_" + << service_name_uc << "_CLIENT))" << endl << "#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))" << endl << "#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))" + << endl << endl; + + /* 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) << ";" << endl; + + 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) << ";" << endl; + + // 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) << ";" << endl; + } + } + + /* 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);" + << endl; + f_header_ << "void " + service_name_lc + "_client_get_property (GObject *object, guint " + "property_id, GValue *value, GParamSpec *pspec);" + << endl; + + f_header_ << endl; + // 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<t_field*>& fields = arglist->get_members(); + vector<t_field*>::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<t_field*>& xceptions = xlist->get_members(); + vector<t_field*>::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" << endl << this->nspace_lc << service_name_lc << "_if_" << funname + << " " << params << endl << "{" << endl << " return " << this->nspace_uc + << service_name_uc << "_IF_GET_INTERFACE (iface)->" << funname << " (" + << params_without_type << "error);" << endl << "}" << endl << endl; + } + + // Generate interface boilerplate + f_service_ << "GType" << endl << this->nspace_lc << service_name_lc << "_if_get_type (void)" + << endl << "{" << endl << " static GType type = 0;" << endl << " if (type == 0)" + << endl << " {" << endl << " static const GTypeInfo type_info =" << endl << " {" + << endl << " sizeof (" << this->nspace << service_name_ << "IfInterface)," << endl + << " NULL, /* base_init */" << endl << " NULL, /* base_finalize */" << endl + << " NULL, /* class_init */" << endl << " NULL, /* class_finalize */" + << endl << " NULL, /* class_data */" << endl + << " 0, /* instance_size */" << endl << " 0, /* n_preallocs */" + << endl << " NULL, /* instance_init */" << endl + << " NULL /* value_table */" << endl << " };" << endl + << " type = g_type_register_static (G_TYPE_INTERFACE," << endl + << " \"" << this->nspace << service_name_ << "If\"," + << endl << " &type_info, 0);" << endl << " }" + << endl << " return type;" << endl << "}" << endl << endl; + + // Generate client boilerplate + f_service_ << "static void " << endl << this->nspace_lc << service_name_lc + << "_if_interface_init (" << this->nspace << service_name_ << "IfInterface *iface);" + << endl << endl << "G_DEFINE_TYPE_WITH_CODE (" << this->nspace << service_name_ + << "Client, " << this->nspace_lc << service_name_lc << "_client," << endl + << " " << parent_type_name << ", " << endl + << " G_IMPLEMENT_INTERFACE (" << this->nspace_uc << "TYPE_" + << service_name_uc << "_IF," << endl + << " " << this->nspace_lc + << service_name_lc << "_if_interface_init))" << endl << endl; + + // 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" << endl << "{" + << endl << " PROP_0," << endl << " PROP_" << this->nspace_uc << service_name_uc + << "_CLIENT_INPUT_PROTOCOL," << endl << " PROP_" << this->nspace_uc + << service_name_uc << "_CLIENT_OUTPUT_PROTOCOL" << endl << "};" << endl << endl; + + // generate property setter + f_service_ << "void" << endl << this->nspace_lc << service_name_lc << "_client_set_property (" + << "GObject *object, guint property_id, const GValue *value, " + << "GParamSpec *pspec)" << endl << "{" << endl << " " << this->nspace + << service_name_ << "Client *client = " << this->nspace_uc << service_name_uc + << "_CLIENT (object);" << endl << endl << " THRIFT_UNUSED_VAR (pspec);" << endl + << endl << " switch (property_id)" << endl << " {" << endl << " case PROP_" + << this->nspace_uc << service_name_uc << "_CLIENT_INPUT_PROTOCOL:" << endl + << " client->input_protocol = g_value_get_object (value);" << endl + << " break;" << endl << " case PROP_" << this->nspace_uc << service_name_uc + << "_CLIENT_OUTPUT_PROTOCOL:" << endl + << " client->output_protocol = g_value_get_object (value);" << endl + << " break;" << endl << " }" << endl << "}" << endl << endl; + + // generate property getter + f_service_ << "void" << endl << this->nspace_lc << service_name_lc << "_client_get_property (" + << "GObject *object, guint property_id, GValue *value, " + << "GParamSpec *pspec)" << endl << "{" << endl << " " << this->nspace + << service_name_ << "Client *client = " << this->nspace_uc << service_name_uc + << "_CLIENT (object);" << endl << endl << " THRIFT_UNUSED_VAR (pspec);" << endl + << endl << " switch (property_id)" << endl << " {" << endl << " case PROP_" + << this->nspace_uc << service_name_uc << "_CLIENT_INPUT_PROTOCOL:" << endl + << " g_value_set_object (value, client->input_protocol);" << endl + << " break;" << endl << " case PROP_" << this->nspace_uc << service_name_uc + << "_CLIENT_OUTPUT_PROTOCOL:" << endl + << " g_value_set_object (value, client->output_protocol);" << endl + << " break;" << endl << " }" << endl << "}" << endl << endl; + } + + // 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) << endl; + scope_up(f_service_); + + string reqType = (*f_iter)->is_oneway() ? "T_ONEWAY" : "T_CALL"; + + // Serialize the request + f_service_ << indent() << "gint32 cseqid = 0;" << endl << indent() + << "ThriftProtocol * protocol = " << this->nspace_uc << base_service_name_uc + << "_CLIENT (iface)->output_protocol;" << endl << endl << indent() + << "if (thrift_protocol_write_message_begin (protocol, \"" << name << "\", " + << reqType << ", cseqid, error) < 0)" << endl << indent() << " return FALSE;" + << endl << endl; + + generate_struct_writer(f_service_, arg_struct, "", "", false); + + f_service_ << indent() << "if (thrift_protocol_write_message_end (protocol, error) < 0)" << endl + << indent() << " return FALSE;" << endl << indent() + << "if (!thrift_transport_flush (protocol->transport, error))" << endl << indent() + << " return FALSE;" << endl << indent() + << "if (!thrift_transport_write_end (protocol->transport, error))" << endl + << indent() << " return FALSE;" << endl << endl << indent() << "return TRUE;" + << endl; + + scope_down(f_service_); + f_service_ << endl; + + // 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) << endl; + scope_up(f_service_); + + f_service_ << indent() << "gint32 rseqid;" << endl + << indent() << "gchar * fname = NULL;" << endl + << indent() << "ThriftMessageType mtype;" << endl + << indent() << "ThriftProtocol * protocol = " + << this->nspace_uc << base_service_name_uc + << "_CLIENT (iface)->input_protocol;" << endl + << indent() << "ThriftApplicationException *xception;" << endl + << endl + << indent() << "if (thrift_protocol_read_message_begin " + "(protocol, &fname, &mtype, &rseqid, error) < 0) {" << endl; + indent_up(); + f_service_ << indent() << "if (fname) g_free (fname);" << endl + << indent() << "return FALSE;" << endl; + indent_down(); + f_service_ << indent() << "}" << endl + << endl + << indent() << "if (mtype == T_EXCEPTION) {" << endl; + indent_up(); + f_service_ << indent() << "if (fname) g_free (fname);" << endl + << indent() << "xception = g_object_new " + "(THRIFT_TYPE_APPLICATION_EXCEPTION, NULL);" << endl + << indent() << "thrift_struct_read (THRIFT_STRUCT (xception), " + "protocol, NULL);" << endl + << indent() << "thrift_protocol_read_message_end " + "(protocol, NULL);" << endl + << indent() << "thrift_transport_read_end " + "(protocol->transport, NULL);" << endl + << indent() << "g_set_error (error, " + "THRIFT_APPLICATION_EXCEPTION_ERROR,xception->type, " + "\"application error: %s\", xception->message);" << endl + << indent() << "g_object_unref (xception);" << endl + << indent() << "return FALSE;" << endl; + indent_down(); + f_service_ << indent() << "} else if (mtype != T_REPLY) {" << endl; + indent_up(); + f_service_ << indent() << "if (fname) g_free (fname);" << endl + << indent() << "thrift_protocol_skip (protocol, T_STRUCT, " + "NULL);" << endl + << indent() << "thrift_protocol_read_message_end (protocol, " + "NULL);" << endl + << indent() << "thrift_transport_read_end (" + "protocol->transport, NULL);" << endl + << indent() << "g_set_error (error, " + "THRIFT_APPLICATION_EXCEPTION_ERROR, " + "THRIFT_APPLICATION_EXCEPTION_ERROR_INVALID_MESSAGE_TYPE, " + "\"invalid message type %d, expected T_REPLY\", mtype);" + << endl + << indent() << "return FALSE;" << endl; + indent_down(); + f_service_ << indent() << "} else if (strncmp (fname, \"" << name + << "\", " << name.length() << ") != 0) {" << endl; + indent_up(); + f_service_ << indent() << "thrift_protocol_skip (protocol, T_STRUCT, " + "NULL);" << endl + << indent() << "thrift_protocol_read_message_end (protocol," + "error);" << endl + << indent() << "thrift_transport_read_end (" + "protocol->transport, error);" << endl + << indent() << "g_set_error (error, " + "THRIFT_APPLICATION_EXCEPTION_ERROR, " + "THRIFT_APPLICATION_EXCEPTION_ERROR_WRONG_METHOD_NAME, " + "\"wrong method name %s, expected " << name + << "\", fname);" << endl + << indent() << "if (fname) g_free (fname);" << endl + << indent() << "return FALSE;" << endl; + indent_down(); + f_service_ << indent() << "}" << endl + << indent() << "if (fname) g_free (fname);" << endl + << endl; + + t_struct* xs = (*f_iter)->get_xceptions(); + const std::vector<t_field*>& xceptions = xs->get_members(); + vector<t_field*>::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)" + << endl << indent() << " return FALSE;" << endl << endl << indent() + << "if (!thrift_transport_read_end (protocol->transport, error))" << endl + << indent() << " return FALSE;" << endl << endl; + + // 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)" << endl + << indent() << "{" << endl << 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() << "\");" << endl + << indent() << " return FALSE;" << endl << indent() << "}" << endl; + } + // Close function + indent(f_service_) << "return TRUE;" << endl; + scope_down(f_service_); + f_service_ << endl; + } + + // 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) << endl; + 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<t_field*>& fields = arg_struct->get_members(); + vector<t_field*>::const_iterator fld_iter; + for (fld_iter = fields.begin(); fld_iter != fields.end(); ++fld_iter) { + f_service_ << ", " << (*fld_iter)->get_name(); + } + f_service_ << ", error))" << endl << indent() << " return FALSE;" << endl; + + // if not oneway, implement recv + if (!(*f_iter)->is_oneway()) { + string ret = (*f_iter)->get_returntype()->is_void() ? "" : "_return, "; + + const vector<t_field*>& xceptions = (*f_iter)->get_xceptions()->get_members(); + vector<t_field*>::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))" << endl << indent() + << " return FALSE;" << endl; + } + + // return TRUE which means all functions were called OK + indent(f_service_) << "return TRUE;" << endl; + scope_down(f_service_); + f_service_ << endl; + } + + // create the interface initializer + f_service_ << "static void" << endl + << this->nspace_lc << service_name_lc << "_if_interface_init (" + << this->nspace << service_name_ << "IfInterface *iface)" << endl; + 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 << ";" << endl; + } + } + else { + f_service_ << indent() << "THRIFT_UNUSED_VAR (iface);" << endl; + } + scope_down(f_service_); + f_service_ << endl; + + // create the client instance initializer + f_service_ << "static void" << endl + << this->nspace_lc << service_name_lc << "_client_init (" + << this->nspace << service_name_ << "Client *client)" << endl; + scope_up(f_service_); + if (!extends_service) { + f_service_ << indent() << "client->input_protocol = NULL;" << endl + << indent() << "client->output_protocol = NULL;" << endl; + } + else { + f_service_ << indent() << "THRIFT_UNUSED_VAR (client);" << endl; + } + scope_down(f_service_); + f_service_ << endl; + + // create the client class initializer + f_service_ << "static void" << endl << this->nspace_lc << service_name_lc + << "_client_class_init (" << this->nspace << service_name_ << "ClientClass *cls)" + << endl << "{" << endl; + if (!extends_service) { + f_service_ << " GObjectClass *gobject_class = G_OBJECT_CLASS (cls);" << endl + << " GParamSpec *param_spec;" << endl << endl + << " gobject_class->set_property = " << this->nspace_lc << service_name_lc + << "_client_set_property;" << endl + << " gobject_class->get_property = " << this->nspace_lc << service_name_lc + << "_client_get_property;" << endl << endl + << " param_spec = g_param_spec_object (\"input_protocol\"," << endl + << " \"input protocol (construct)\"," << endl + << " \"Set the client input protocol\"," << endl + << " THRIFT_TYPE_PROTOCOL," << endl + << " G_PARAM_READWRITE);" << endl + << " g_object_class_install_property (gobject_class," << endl + << " PROP_" << this->nspace_uc << service_name_uc + << "_CLIENT_INPUT_PROTOCOL, param_spec);" << endl << endl + << " param_spec = g_param_spec_object (\"output_protocol\"," << endl + << " \"output protocol (construct)\"," << endl + << " \"Set the client output protocol\"," << endl + << " THRIFT_TYPE_PROTOCOL," << endl + << " G_PARAM_READWRITE);" << endl + << " g_object_class_install_property (gobject_class," << endl + << " PROP_" << this->nspace_uc << service_name_uc + << "_CLIENT_OUTPUT_PROTOCOL, param_spec);" << endl; + } + else { + f_service_ << " THRIFT_UNUSED_VAR (cls);" << endl; + } + f_service_ << "}" << endl << endl; +} + +/** + * 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<t_function*> functions = tservice->get_functions(); + vector<t_function*>::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 class_name = this->nspace + service_name_ + "Handler"; + string class_name_lc = to_lower_case(initial_caps_to_underscores(class_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 NULL 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) */" << endl << "struct _" + << class_name << endl << "{" << endl; + indent_up(); + f_header_ << indent() << parent_class_name << " parent;" << endl; + indent_down(); + f_header_ << "};" << endl << "typedef struct _" << class_name << " " << class_name << ";" << endl + << endl; + + // Generate the handler class definition, including its class members + // (methods) + f_header_ << "struct _" << class_name << "Class" << endl << "{" << endl; + indent_up(); + f_header_ << indent() << parent_class_name << "Class parent;" << endl << endl; + + 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 << ";" << endl; + } + indent_down(); + + f_header_ << "};" << endl << "typedef struct _" << class_name << "Class " << class_name + << "Class;" << endl << endl; + + // Generate the remaining header boilerplate + f_header_ << "GType " << class_name_lc << "_get_type (void);" << endl << "#define " + << this->nspace_uc << "TYPE_" << service_name_uc << "_HANDLER " + << "(" << class_name_lc << "_get_type())" << endl << "#define " << class_name_uc + << "(obj) " + << "(G_TYPE_CHECK_INSTANCE_CAST ((obj), " << this->nspace_uc << "TYPE_" + << service_name_uc << "_HANDLER, " << class_name << "))" << endl << "#define " + << this->nspace_uc << "IS_" << service_name_uc << "_HANDLER(obj) " + << "(G_TYPE_CHECK_INSTANCE_TYPE ((obj), " << this->nspace_uc << "TYPE_" + << service_name_uc << "_HANDLER))" << endl << "#define " << class_name_uc + << "_CLASS(c) (G_TYPE_CHECK_CLASS_CAST ((c), " << this->nspace_uc << "TYPE_" + << service_name_uc << "_HANDLER, " << class_name << "Class))" << endl << "#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))" << endl << "#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))" << endl << endl; + + // 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 << ";" << endl; + } + f_header_ << endl; + + // Generate the handler's implementation in the implementation file + + // Generate the implementation boilerplate + f_service_ << "static void" << endl << class_name_lc << "_" << service_name_lc + << "_if_interface_init (" << this->nspace << service_name_ << "IfInterface *iface);" + << endl << endl; + + args_indent = string(25, ' '); + f_service_ << "G_DEFINE_TYPE_WITH_CODE (" << class_name << ", " << endl << args_indent + << class_name_lc << "," << endl << args_indent << parent_type_name << "," << endl + << args_indent << "G_IMPLEMENT_INTERFACE (" << this->nspace_uc << "TYPE_" + << service_name_uc << "_IF," << endl; + args_indent += string(23, ' '); + f_service_ << args_indent << class_name_lc << "_" << service_name_lc << "_if_interface_init))" + << endl << endl; + + // 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<t_field*>& args = arg_list->get_members(); + const vector<t_field*>& xceptions = x_list->get_members(); + + vector<t_field*>::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) << endl; + scope_up(f_service_); + f_service_ << indent() << "g_return_val_if_fail (" << this->nspace_uc << "IS_" + << service_name_uc << "_HANDLER (iface), FALSE);" << endl << endl << 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);" << endl; + scope_down(f_service_); + f_service_ << endl; + } + + // Generate the handler interface initializer + f_service_ << "static void" << endl << class_name_lc << "_" << service_name_lc + << "_if_interface_init (" << this->nspace << service_name_ << "IfInterface *iface)" + << endl; + 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 << ";" << endl; + } + } + else { + f_service_ << "THRIFT_UNUSED_VAR (iface);" << endl; + } + scope_down(f_service_); + f_service_ << endl; + + // Generate the handler instance initializer + f_service_ << "static void" << endl << class_name_lc << "_init (" << class_name << " *self)" + << endl; + scope_up(f_service_); + f_service_ << indent() << "THRIFT_UNUSED_VAR (self);" << endl; + scope_down(f_service_); + f_service_ << endl; + + // Generate the handler class initializer + f_service_ << "static void" << endl + << class_name_lc << "_class_init (" << class_name << "Class *cls)" + << endl; + 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;" << endl; + } + } + else { + f_service_ << indent() << "THRIFT_UNUSED_VAR (cls);" << endl; + } + scope_down(f_service_); + f_service_ << endl; +} + +/** + * 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<t_function*> functions = tservice->get_functions(); + vector<t_function*>::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 class_name = this->nspace + service_name_ + "Processor"; + string class_name_lc = to_lower_case(initial_caps_to_underscores(class_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 NULL 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 */" << endl << "struct _" << class_name << endl + << "{" << endl; + indent_up(); + f_header_ << indent() << parent_class_name << " parent;" << endl << endl << indent() + << "/* protected */" << endl << indent() + << this->nspace + service_name_ + "Handler *handler;" << endl << indent() + << "GHashTable *process_map;" << endl; + indent_down(); + f_header_ << "};" << endl << "typedef struct _" << class_name << " " << class_name << ";" << endl + << endl; + + // Generate the processor class definition + f_header_ << "struct _" << class_name << "Class" << endl << "{" << endl; + indent_up(); + f_header_ << indent() << parent_class_name << "Class parent;" << endl << endl << indent() + << "/* protected */" << endl << indent() + << "gboolean (*dispatch_call) (ThriftDispatchProcessor *processor," << endl; + args_indent = indent() + string(27, ' '); + f_header_ << args_indent << "ThriftProtocol *in," << endl << args_indent << "ThriftProtocol *out," + << endl << args_indent << "gchar *fname," << endl << args_indent << "gint32 seqid," + << endl << args_indent << "GError **error);" << endl; + indent_down(); + f_header_ << "};" << endl << "typedef struct _" << class_name << "Class " << class_name + << "Class;" << endl << endl; + + // Generate the remaining header boilerplate + f_header_ << "GType " << class_name_lc << "_get_type (void);" << endl << "#define " + << this->nspace_uc << "TYPE_" << service_name_uc << "_PROCESSOR " + << "(" << class_name_lc << "_get_type())" << endl << "#define " << class_name_uc + << "(obj) " + << "(G_TYPE_CHECK_INSTANCE_CAST ((obj), " << this->nspace_uc << "TYPE_" + << service_name_uc << "_PROCESSOR, " << class_name << "))" << endl << "#define " + << this->nspace_uc << "IS_" << service_name_uc << "_PROCESSOR(obj) " + << "(G_TYPE_CHECK_INSTANCE_TYPE ((obj), " << this->nspace_uc << "TYPE_" + << service_name_uc << "_PROCESSOR))" << endl << "#define " << class_name_uc + << "_CLASS(c) (G_TYPE_CHECK_CLASS_CAST ((c), " << this->nspace_uc << "TYPE_" + << service_name_uc << "_PROCESSOR, " << class_name << "Class))" << endl << "#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))" << endl << "#define " << this->nspace_uc << service_name_uc + << "_PROCESSOR_GET_CLASS(obj) " + << "(G_TYPE_INSTANCE_GET_CLASS ((obj), " << this->nspace_uc <
<TRUNCATED>
