void t_c_glib_generator::generate_service_client()

in compiler/cpp/src/thrift/generate/t_c_glib_generator.cc [1265:1830]


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<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 << ";" << '\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<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" << '\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<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)"
                 << '\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<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))" << '\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<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))" << '\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';
}