void t_c_glib_generator::generate_object()

in compiler/cpp/src/thrift/generate/t_c_glib_generator.cc [2795:3502]


void t_c_glib_generator::generate_object(t_struct* tstruct) {
  string name = tstruct->get_name();
  string name_u = initial_caps_to_underscores(name);
  string name_uc = to_upper_case(name_u);

  string class_name = this->nspace + name;
  string class_name_lc = this->nspace_lc + initial_caps_to_underscores(name);
  string class_name_uc = to_upper_case(class_name_lc);

  string function_name;
  string args_indent;

  // write the instance definition
  f_types_ << "struct _" << this->nspace << name << '\n' << "{ " << '\n'
           << "  ThriftStruct parent; " << '\n' << '\n' << "  /* public */" << '\n';

  // for each field, add a member variable
  vector<t_field*>::const_iterator m_iter;
  const vector<t_field*>& members = tstruct->get_members();
  for (m_iter = members.begin(); m_iter != members.end(); ++m_iter) {
    t_type* t = get_true_type((*m_iter)->get_type());
    f_types_ << "  " << type_name(t) << " " << (*m_iter)->get_name() << ";" << '\n';
    if ((*m_iter)->get_req() != t_field::T_REQUIRED) {
      f_types_ << "  gboolean __isset_" << (*m_iter)->get_name() << ";" << '\n';
    }
  }

  // close the structure definition and create a typedef
  f_types_ << "};" << '\n' << "typedef struct _" << this->nspace << name << " " << this->nspace
           << name << ";" << '\n' << '\n';

  // write the class definition
  f_types_ << "struct _" << this->nspace << name << "Class" << '\n' << "{" << '\n'
           << "  ThriftStructClass parent;" << '\n' << "};" << '\n' << "typedef struct _"
           << this->nspace << name << "Class " << this->nspace << name << "Class;" << '\n' << '\n';

  // write the standard GObject boilerplate
  f_types_ << "GType " << this->nspace_lc << name_u << "_get_type (void);" << '\n' << "#define "
           << this->nspace_uc << "TYPE_" << name_uc << " (" << this->nspace_lc << name_u
           << "_get_type())" << '\n' << "#define " << this->nspace_uc << name_uc
           << "(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), " << this->nspace_uc << "TYPE_" << name_uc
           << ", " << this->nspace << name << "))" << '\n' << "#define " << this->nspace_uc
           << name_uc << "_CLASS(c) (G_TYPE_CHECK_CLASS_CAST ((c), " << this->nspace_uc << "_TYPE_"
           << name_uc << ", " << this->nspace << name << "Class))" << '\n' << "#define "
           << this->nspace_uc << "IS_" << name_uc << "(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), "
           << this->nspace_uc << "TYPE_" << name_uc << "))" << '\n' << "#define " << this->nspace_uc
           << "IS_" << name_uc << "_CLASS(c) (G_TYPE_CHECK_CLASS_TYPE ((c), " << this->nspace_uc
           << "TYPE_" << name_uc << "))" << '\n' << "#define " << this->nspace_uc << name_uc
           << "_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), " << this->nspace_uc << "TYPE_"
           << name_uc << ", " << this->nspace << name << "Class))" << '\n' << '\n';

  // start writing the object implementation .c file

  // generate properties enum
  if (members.size() > 0) {
    f_types_impl_ << "enum _" << class_name << "Properties" << '\n' << "{" << '\n';
    indent_up();
    f_types_impl_ << indent() << "PROP_" << class_name_uc << "_0";
    for (m_iter = members.begin(); m_iter != members.end(); ++m_iter) {
      string member_name_uc
          = to_upper_case(to_lower_case(initial_caps_to_underscores((*m_iter)->get_name())));

      f_types_impl_ << "," << '\n' << indent() << "PROP_" << class_name_uc << "_" << member_name_uc;
    }
    f_types_impl_ << '\n';
    indent_down();
    f_types_impl_ << "};" << '\n' << '\n';
  }

  // generate struct I/O methods
  string this_get = this->nspace + name + " * this_object = " + this->nspace_uc + name_uc
                    + "(object);";
  generate_struct_reader(f_types_impl_, tstruct, "this_object->", this_get);
  generate_struct_writer(f_types_impl_, tstruct, "this_object->", this_get);

  // generate property setter and getter
  if (members.size() > 0) {
    // generate property setter
    function_name = class_name_lc + "_set_property";
    args_indent = string(function_name.length() + 2, ' ');
    f_types_impl_ << "static void" << '\n' << function_name << " (GObject *object," << '\n'
                  << args_indent << "guint property_id," << '\n' << args_indent
                  << "const GValue *value," << '\n' << args_indent << "GParamSpec *pspec)" << '\n';
    scope_up(f_types_impl_);
    f_types_impl_ << indent() << class_name << " *self = " << class_name_uc << " (object);" << '\n'
                  << '\n' << indent() << "switch (property_id)" << '\n';
    scope_up(f_types_impl_);
    for (m_iter = members.begin(); m_iter != members.end(); ++m_iter) {
      t_field* member = (*m_iter);
      string member_name = member->get_name();
      string member_name_uc
          = to_upper_case(to_lower_case(initial_caps_to_underscores(member_name)));
      t_type* member_type = get_true_type(member->get_type());

      string property_identifier = "PROP_" + class_name_uc + "_" + member_name_uc;

      f_types_impl_ << indent() << "case " << property_identifier + ":" << '\n';
      indent_up();

      if (member_type->is_base_type()) {
        t_base_type* base_type = ((t_base_type*)member_type);
        string assign_function_name;

        if (base_type->get_base() == t_base_type::TYPE_STRING) {
          string release_function_name;

          f_types_impl_ << indent() << "if (self->" << member_name << " != NULL)" << '\n';
          indent_up();

          if (base_type->is_binary()) {
            release_function_name = "g_byte_array_unref";
            assign_function_name = "g_value_dup_boxed";
          } else {
            release_function_name = "g_free";
            assign_function_name = "g_value_dup_string";
          }

          f_types_impl_ << indent() << release_function_name << " (self->" << member_name << ");"
                        << '\n';
          indent_down();
        } else {
          switch (base_type->get_base()) {
          case t_base_type::TYPE_BOOL:
            assign_function_name = "g_value_get_boolean";
            break;

          case t_base_type::TYPE_I8:
          case t_base_type::TYPE_I16:
          case t_base_type::TYPE_I32:
            assign_function_name = "g_value_get_int";
            break;

          case t_base_type::TYPE_I64:
            assign_function_name = "g_value_get_int64";
            break;

          case t_base_type::TYPE_DOUBLE:
            assign_function_name = "g_value_get_double";
            break;

          default:
            throw "compiler error: "
                  "unrecognized base type \"" + base_type->get_name() + "\" "
                                                                        "for struct member \""
                + member_name + "\"";
            break;
          }
        }

        f_types_impl_ << indent() << "self->" << member_name << " = " << assign_function_name
                      << " (value);" << '\n';
      } else if (member_type->is_enum()) {
        f_types_impl_ << indent() << "self->" << member_name << " = g_value_get_int (value);"
                      << '\n';
      } else if (member_type->is_container()) {
        string release_function_name;
        string assign_function_name;

        if (member_type->is_list()) {
          t_type* elem_type = ((t_list*)member_type)->get_elem_type();

          // Lists of base types other than strings are represented as GArrays;
          // all others as GPtrArrays
          if (is_numeric(elem_type)) {
            release_function_name = "g_array_unref";
          } else {
            release_function_name = "g_ptr_array_unref";
          }

          assign_function_name = "g_value_dup_boxed";
        } else if (member_type->is_set() || member_type->is_map()) {
          release_function_name = "g_hash_table_unref";
          assign_function_name = "g_value_dup_boxed";
        }

        f_types_impl_ << indent() << "if (self->" << member_name << " != NULL)" << '\n';
        indent_up();
        f_types_impl_ << indent() << release_function_name << " (self->" << member_name << ");"
                      << '\n';
        indent_down();
        f_types_impl_ << indent() << "self->" << member_name << " = " << assign_function_name
                      << " (value);" << '\n';
      } else if (member_type->is_struct() || member_type->is_xception()) {
        f_types_impl_ << indent() << "if (self->" << member_name << " != NULL)" << '\n';
        indent_up();
        f_types_impl_ << indent() << "g_object_unref (self->" << member_name << ");" << '\n';
        indent_down();
        f_types_impl_ << indent() << "self->" << member_name << " = g_value_dup_object (value);"
                      << '\n';
      }

      if (member->get_req() != t_field::T_REQUIRED) {
        f_types_impl_ << indent() << "self->__isset_" << member_name << " = TRUE;" << '\n';
      }

      f_types_impl_ << indent() << "break;" << '\n' << '\n';
      indent_down();
    }
    f_types_impl_ << indent() << "default:" << '\n';
    indent_up();
    f_types_impl_ << indent() << "G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);"
                  << '\n' << indent() << "break;" << '\n';
    indent_down();
    scope_down(f_types_impl_);
    scope_down(f_types_impl_);
    f_types_impl_ << '\n';

    // generate property getter
    function_name = class_name_lc + "_get_property";
    args_indent = string(function_name.length() + 2, ' ');
    f_types_impl_ << "static void" << '\n' << function_name << " (GObject *object," << '\n'
                  << args_indent << "guint property_id," << '\n' << args_indent << "GValue *value,"
                  << '\n' << args_indent << "GParamSpec *pspec)" << '\n';
    scope_up(f_types_impl_);
    f_types_impl_ << indent() << class_name << " *self = " << class_name_uc << " (object);" << '\n'
                  << '\n' << indent() << "switch (property_id)" << '\n';
    scope_up(f_types_impl_);
    for (m_iter = members.begin(); m_iter != members.end(); ++m_iter) {
      t_field* member = (*m_iter);
      string member_name = (*m_iter)->get_name();
      string member_name_uc
          = to_upper_case(to_lower_case(initial_caps_to_underscores(member_name)));
      t_type* member_type = get_true_type(member->get_type());

      string property_identifier = "PROP_" + class_name_uc + "_" + member_name_uc;

      string setter_function_name;

      if (member_type->is_base_type()) {
        t_base_type* base_type = ((t_base_type*)member_type);

        switch (base_type->get_base()) {
        case t_base_type::TYPE_BOOL:
          setter_function_name = "g_value_set_boolean";
          break;

        case t_base_type::TYPE_I8:
        case t_base_type::TYPE_I16:
        case t_base_type::TYPE_I32:
          setter_function_name = "g_value_set_int";
          break;

        case t_base_type::TYPE_I64:
          setter_function_name = "g_value_set_int64";
          break;

        case t_base_type::TYPE_DOUBLE:
          setter_function_name = "g_value_set_double";
          break;

        case t_base_type::TYPE_STRING:
          if (base_type->is_binary()) {
            setter_function_name = "g_value_set_boxed";
          } else {
            setter_function_name = "g_value_set_string";
          }
          break;

        default:
          throw "compiler error: "
                "unrecognized base type \"" + base_type->get_name() + "\" "
                                                                      "for struct member \""
              + member_name + "\"";
          break;
        }
      } else if (member_type->is_enum()) {
        setter_function_name = "g_value_set_int";
      } else if (member_type->is_struct() || member_type->is_xception()) {
        setter_function_name = "g_value_set_object";
      } else if (member_type->is_container()) {
        setter_function_name = "g_value_set_boxed";
      } else {
        throw "compiler error: "
              "unrecognized type for struct member \"" + member_name + "\"";
      }

      f_types_impl_ << indent() << "case " << property_identifier + ":" << '\n';
      indent_up();
      f_types_impl_ << indent() << setter_function_name << " (value, self->" << member_name << ");"
                    << '\n' << indent() << "break;" << '\n' << '\n';
      indent_down();
    }
    f_types_impl_ << indent() << "default:" << '\n';
    indent_up();
    f_types_impl_ << indent() << "G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);"
                  << '\n' << indent() << "break;" << '\n';
    indent_down();
    scope_down(f_types_impl_);
    scope_down(f_types_impl_);
    f_types_impl_ << '\n';
  }

  // generate the instance init function

  f_types_impl_ << "static void " << '\n' << this->nspace_lc << name_u << "_instance_init ("
                << this->nspace << name << " * object)" << '\n' << "{" << '\n';
  indent_up();

  // generate default-value structures for container-type members
  bool constant_declaration_output = false;
  bool string_list_constant_output = false;
  for (m_iter = members.begin(); m_iter != members.end(); ++m_iter) {
    t_field* member = *m_iter;
    t_const_value* member_value = member->get_value();

    if (member_value != nullptr) {
      string member_name = member->get_name();
      t_type* member_type = get_true_type(member->get_type());

      if (member_type->is_list()) {
        const vector<t_const_value*>& list = member_value->get_list();
        t_type* elem_type = ((t_list*)member_type)->get_elem_type();

        // Generate an array with the list literal
        indent(f_types_impl_) << "static " << type_name(elem_type, false, true) << " __default_"
                              << member_name << "[" << list.size() << "] = " << '\n';
        indent_up();
        f_types_impl_ << indent() << constant_literal(member_type, member_value) << ";" << '\n';
        indent_down();

        constant_declaration_output = true;

        // If we are generating values for a pointer array (i.e. a list of
        // strings), set a flag so we know to also declare an index variable to
        // use in pre-populating the array
        if (elem_type->is_string()) {
          string_list_constant_output = true;
        }
      }

      // TODO: Handle container types other than list
    }
  }
  if (constant_declaration_output) {
    if (string_list_constant_output) {
      indent(f_types_impl_) << "unsigned int list_index;" << '\n';
    }

    f_types_impl_ << '\n';
  }

  // satisfy compilers with -Wall turned on
  indent(f_types_impl_) << "/* satisfy -Wall */" << '\n' << indent()
                        << "THRIFT_UNUSED_VAR (object);" << '\n';

  for (m_iter = members.begin(); m_iter != members.end(); ++m_iter) {
    t_type* member_type = (*m_iter)->get_type();
    t_type* t = get_true_type(member_type);
    if (t->is_base_type()) {
      string dval = " = ";
      if (t->is_enum()) {
        dval += "(" + type_name(t) + ")";
      }
      t_const_value* cv = (*m_iter)->get_value();
      if (cv != nullptr) {
        dval += constant_value("", t, cv);
      } else {
        dval += t->is_string() ? "NULL" : "0";
      }
      indent(f_types_impl_) << "object->" << (*m_iter)->get_name() << dval << ";" << '\n';
    } else if (t->is_struct()) {
      string name = (*m_iter)->get_name();
      t_program* type_program = member_type->get_program();
      string type_nspace = type_program ? type_program->get_namespace("c_glib") : "";
      string type_nspace_prefix =
        type_nspace.empty() ? "" : initial_caps_to_underscores(type_nspace) + "_";
      string type_name_uc = to_upper_case(initial_caps_to_underscores(member_type->get_name()));
      indent(f_types_impl_) << "object->" << name << " = g_object_new ("
                            << to_upper_case(type_nspace_prefix) << "TYPE_" << type_name_uc
                            << ", NULL);" << '\n';
    } else if (t->is_xception()) {
      string name = (*m_iter)->get_name();
      indent(f_types_impl_) << "object->" << name << " = NULL;" << '\n';
    } else if (t->is_container()) {
      string name = (*m_iter)->get_name();
      string init_function;
      t_type* etype = nullptr;

      if (t->is_map()) {
        t_type* key = ((t_map*)t)->get_key_type();
        t_type* value = ((t_map*)t)->get_val_type();
        init_function = generate_new_hash_from_type(key, value);
      } else if (t->is_set()) {
        etype = ((t_set*)t)->get_elem_type();
        init_function = generate_new_hash_from_type(etype, nullptr);
      } else if (t->is_list()) {
        etype = ((t_list*)t)->get_elem_type();
        init_function = generate_new_array_from_type(etype);
      }

      indent(f_types_impl_) << "object->" << name << " = " << init_function << '\n';

      // Pre-populate the container with the specified default values, if any
      if ((*m_iter)->get_value()) {
        t_const_value* member_value = (*m_iter)->get_value();

        if (t->is_list()) {
          const vector<t_const_value*>& list = member_value->get_list();

          if (is_numeric(etype)) {
            indent(f_types_impl_) <<
              "g_array_append_vals (object->" << name << ", &__default_" <<
              name << ", " << list.size() << ");" << '\n';
          }
          else {
            indent(f_types_impl_) <<
              "for (list_index = 0; list_index < " << list.size() << "; " <<
              "list_index += 1)" << '\n';
            indent_up();
            indent(f_types_impl_) <<
              "g_ptr_array_add (object->" << name << "," << '\n' <<
              indent() << string(17, ' ') << "g_strdup (__default_" <<
              name << "[list_index]));" << '\n';
            indent_down();
          }
        }

        // TODO: Handle container types other than list
      }
    }

    /* if not required, initialize the __isset variable */
    if ((*m_iter)->get_req() != t_field::T_REQUIRED) {
      indent(f_types_impl_) << "object->__isset_" << (*m_iter)->get_name() << " = FALSE;" << '\n';
    }
  }

  indent_down();
  f_types_impl_ << "}" << '\n' << '\n';

  /* create the destructor */
  f_types_impl_ << "static void " << '\n' << this->nspace_lc << name_u
                << "_finalize (GObject *object)" << '\n' << "{" << '\n';
  indent_up();

  f_types_impl_ << indent() << this->nspace << name << " *tobject = " << this->nspace_uc << name_uc
                << " (object);" << '\n' << '\n';

  f_types_impl_ << indent() << "/* satisfy -Wall in case we don't use tobject */" << '\n'
                << indent() << "THRIFT_UNUSED_VAR (tobject);" << '\n';

  for (m_iter = members.begin(); m_iter != members.end(); ++m_iter) {
    t_type* t = get_true_type((*m_iter)->get_type());
    if (t->is_container()) {
      string name = (*m_iter)->get_name();
      if (t->is_map() || t->is_set()) {
        f_types_impl_ << indent() << "if (tobject->" << name << " != NULL)" << '\n';
        f_types_impl_ << indent() << "{" << '\n';
        indent_up();
        f_types_impl_ << indent() << "g_hash_table_destroy (tobject->" << name << ");" << '\n';
        f_types_impl_ << indent() << "tobject->" << name << " = NULL;" << '\n';
        indent_down();
        f_types_impl_ << indent() << "}" << '\n';
      } else if (t->is_list()) {
        t_type* etype = ((t_list*)t)->get_elem_type();
        string destructor_function = "g_ptr_array_unref";

        if (etype->is_base_type()) {
          t_base_type::t_base tbase = ((t_base_type*)etype)->get_base();
          switch (tbase) {
          case t_base_type::TYPE_VOID:
            throw "compiler error: cannot determine array type";
          case t_base_type::TYPE_BOOL:
          case t_base_type::TYPE_I8:
          case t_base_type::TYPE_I16:
          case t_base_type::TYPE_I32:
          case t_base_type::TYPE_I64:
          case t_base_type::TYPE_DOUBLE:
            destructor_function = "g_array_unref";
            break;
          case t_base_type::TYPE_STRING:
            break;
          default:
            throw "compiler error: no array info for type";
          }
        } else if (etype->is_enum()) {
          destructor_function = "g_array_unref";
        }

        f_types_impl_ << indent() << "if (tobject->" << name << " != NULL)" << '\n';
        f_types_impl_ << indent() << "{" << '\n';
        indent_up();
        f_types_impl_ << indent() << destructor_function << " (tobject->" << name << ");" << '\n';
        f_types_impl_ << indent() << "tobject->" << name << " = NULL;" << '\n';
        indent_down();
        f_types_impl_ << indent() << "}" << '\n';
      }
    } else if (t->is_struct() || t->is_xception()) {
      string name = (*m_iter)->get_name();
      // TODO: g_clear_object needs glib >= 2.28
      // f_types_impl_ << indent() << "g_clear_object (&(tobject->" << name << "));" << '\n';
      // does g_object_unref the trick?
      f_types_impl_ << indent() << "if (tobject->" << name << " != NULL)" << '\n';
      f_types_impl_ << indent() << "{" << '\n';
      indent_up();
      f_types_impl_ << indent() << "g_object_unref(tobject->" << name << ");" << '\n';
      f_types_impl_ << indent() << "tobject->" << name << " = NULL;" << '\n';
      indent_down();
      f_types_impl_ << indent() << "}" << '\n';
    } else if (t->is_string()) {
      string name = (*m_iter)->get_name();
      f_types_impl_ << indent() << "if (tobject->" << name << " != NULL)" << '\n';
      f_types_impl_ << indent() << "{" << '\n';
      indent_up();
      f_types_impl_ << indent() << generate_free_func_from_type(t) << "(tobject->" << name << ");"
                    << '\n';
      f_types_impl_ << indent() << "tobject->" << name << " = NULL;" << '\n';
      indent_down();
      f_types_impl_ << indent() << "}" << '\n';
    }
  }

  indent_down();
  f_types_impl_ << "}" << '\n' << '\n';

  // generate the class init function

  f_types_impl_ << "static void" << '\n' << class_name_lc << "_class_init (" << class_name
                << "Class * cls)" << '\n';
  scope_up(f_types_impl_);

  f_types_impl_ << indent() << "GObjectClass *gobject_class = G_OBJECT_CLASS (cls);" << '\n'
                << indent() << "ThriftStructClass *struct_class = "
                << "THRIFT_STRUCT_CLASS (cls);" << '\n' << '\n' << indent()
                << "struct_class->read = " << class_name_lc << "_read;" << '\n' << indent()
                << "struct_class->write = " << class_name_lc << "_write;" << '\n' << '\n'
                << indent() << "gobject_class->finalize = " << class_name_lc << "_finalize;"
                << '\n';
  if (members.size() > 0) {
    f_types_impl_ << indent() << "gobject_class->get_property = " << class_name_lc
                  << "_get_property;" << '\n' << indent()
                  << "gobject_class->set_property = " << class_name_lc << "_set_property;" << '\n';

    // install a property for each member
    for (m_iter = members.begin(); m_iter != members.end(); ++m_iter) {
      t_field* member = (*m_iter);
      string member_name = member->get_name();
      string member_name_uc
          = to_upper_case(to_lower_case(initial_caps_to_underscores(member_name)));
      t_type* member_type = get_true_type(member->get_type());
      t_const_value* member_value = member->get_value();

      string property_identifier = "PROP_" + class_name_uc + "_" + member_name_uc;

      f_types_impl_ << '\n' << indent() << "g_object_class_install_property" << '\n';
      indent_up();
      args_indent = indent() + ' ';
      f_types_impl_ << indent() << "(gobject_class," << '\n' << args_indent << property_identifier
                    << "," << '\n' << args_indent;

      if (member_type->is_base_type()) {
        t_base_type::t_base base_type = ((t_base_type*)member_type)->get_base();

        if (base_type == t_base_type::TYPE_STRING) {
          if (((t_base_type*)member_type)->is_binary()) {
            args_indent += string(20, ' ');
            f_types_impl_ << "g_param_spec_boxed (\"" << member_name << "\"," << '\n' << args_indent
                          << "NULL," << '\n' << args_indent << "NULL," << '\n' << args_indent
                          << "G_TYPE_BYTE_ARRAY," << '\n' << args_indent << "G_PARAM_READWRITE));"
                          << '\n';
          } else {
            args_indent += string(21, ' ');
            f_types_impl_ << "g_param_spec_string (\"" << member_name << "\"," << '\n'
                          << args_indent << "NULL," << '\n' << args_indent << "NULL," << '\n'
                          << args_indent
                          << ((member_value != NULL) ? "\"" + member_value->get_string() + "\""
                                                     : "NULL") << "," << '\n' << args_indent
                          << "G_PARAM_READWRITE));" << '\n';
          }
        } else if (base_type == t_base_type::TYPE_BOOL) {
          args_indent += string(22, ' ');
          f_types_impl_ << "g_param_spec_boolean (\"" << member_name << "\"," << '\n' << args_indent
                        << "NULL," << '\n' << args_indent << "NULL," << '\n' << args_indent
                        << (((member_value != NULL) && (member_value->get_integer() != 0))
                                ? "TRUE"
                                : "FALSE") << "," << '\n' << args_indent << "G_PARAM_READWRITE));"
                        << '\n';
        } else if ((base_type == t_base_type::TYPE_I8) || (base_type == t_base_type::TYPE_I16)
                   || (base_type == t_base_type::TYPE_I32) || (base_type == t_base_type::TYPE_I64)
                   || (base_type == t_base_type::TYPE_DOUBLE)) {
          string param_spec_function_name = "g_param_spec_int";
          string min_value;
          string max_value;
          ostringstream default_value;

          switch (base_type) {
          case t_base_type::TYPE_I8:
            min_value = "G_MININT8";
            max_value = "G_MAXINT8";
            break;

          case t_base_type::TYPE_I16:
            min_value = "G_MININT16";
            max_value = "G_MAXINT16";
            break;

          case t_base_type::TYPE_I32:
            min_value = "G_MININT32";
            max_value = "G_MAXINT32";
            break;

          case t_base_type::TYPE_I64:
            param_spec_function_name = "g_param_spec_int64";
            min_value = "G_MININT64";
            max_value = "G_MAXINT64";
            break;

          case t_base_type::TYPE_DOUBLE:
            param_spec_function_name = "g_param_spec_double";
            min_value = "-INFINITY";
            max_value = "INFINITY";
            break;

          default:
            throw "compiler error: "
                  "unrecognized base type \"" + member_type->get_name() + "\" "
                                                                          "for struct member \""
                + member_name + "\"";
            break;
          }

          if (member_value != nullptr) {
            default_value << (base_type == t_base_type::TYPE_DOUBLE ? member_value->get_double()
                                                                    : member_value->get_integer());
          } else {
            default_value << "0";
          }

          args_indent += string(param_spec_function_name.length() + 2, ' ');
          f_types_impl_ << param_spec_function_name << " (\"" << member_name << "\"," << '\n'
                        << args_indent << "NULL," << '\n' << args_indent << "NULL," << '\n'
                        << args_indent << min_value << "," << '\n' << args_indent << max_value
                        << "," << '\n' << args_indent << default_value.str() << "," << '\n'
                        << args_indent << "G_PARAM_READWRITE));" << '\n';
        }

        indent_down();
      } else if (member_type->is_enum()) {
        t_enum_value* enum_min_value = ((t_enum*)member_type)->get_min_value();
        t_enum_value* enum_max_value = ((t_enum*)member_type)->get_max_value();
        int min_value = (enum_min_value != nullptr) ? enum_min_value->get_value() : 0;
        int max_value = (enum_max_value != nullptr) ? enum_max_value->get_value() : 0;

        args_indent += string(18, ' ');
        f_types_impl_ << "g_param_spec_int (\"" << member_name << "\"," << '\n' << args_indent
                      << "NULL," << '\n' << args_indent << "NULL," << '\n' << args_indent
                      << min_value << "," << '\n' << args_indent << max_value << "," << '\n'
                      << args_indent << min_value << "," << '\n' << args_indent
                      << "G_PARAM_READWRITE));" << '\n';
        indent_down();
      } else if (member_type->is_struct() || member_type->is_xception()) {
        t_program* type_program = member_type->get_program();
        string type_nspace = type_program ? type_program->get_namespace("c_glib") : "";
        string type_nspace_prefix =
          type_nspace.empty() ? "" : initial_caps_to_underscores(type_nspace) + "_";

        string param_type = to_upper_case(type_nspace_prefix) + "TYPE_"
                            + to_upper_case(initial_caps_to_underscores(member_type->get_name()));

        args_indent += string(20, ' ');
        f_types_impl_ << "g_param_spec_object (\"" << member_name << "\"," << '\n' << args_indent
                      << "NULL," << '\n' << args_indent << "NULL," << '\n' << args_indent
                      << param_type << "," << '\n' << args_indent << "G_PARAM_READWRITE));" << '\n';
        indent_down();
      } else if (member_type->is_list()) {
        t_type* elem_type = ((t_list*)member_type)->get_elem_type();
        string param_type;

        if (elem_type->is_base_type() && !elem_type->is_string()) {
          param_type = "G_TYPE_ARRAY";
        } else {
          param_type = "G_TYPE_PTR_ARRAY";
        }

        args_indent += string(20, ' ');
        f_types_impl_ << "g_param_spec_boxed (\"" << member_name << "\"," << '\n' << args_indent
                      << "NULL," << '\n' << args_indent << "NULL," << '\n' << args_indent
                      << param_type << "," << '\n' << args_indent << "G_PARAM_READWRITE));" << '\n';
        indent_down();
      } else if (member_type->is_set() || member_type->is_map()) {
        args_indent += string(20, ' ');
        f_types_impl_ << "g_param_spec_boxed (\"" << member_name << "\"," << '\n' << args_indent
                      << "NULL," << '\n' << args_indent << "NULL," << '\n' << args_indent
                      << "G_TYPE_HASH_TABLE," << '\n' << args_indent << "G_PARAM_READWRITE));"
                      << '\n';
        indent_down();
      }
    }
  }
  scope_down(f_types_impl_);
  f_types_impl_ << '\n';

  f_types_impl_ << "GType" << '\n' << this->nspace_lc << name_u << "_get_type (void)" << '\n' << "{"
                << '\n' << "  static GType type = 0;" << '\n' << '\n' << "  if (type == 0) " << '\n'
                << "  {" << '\n' << "    static const GTypeInfo type_info = " << '\n' << "    {"
                << '\n' << "      sizeof (" << this->nspace << name << "Class)," << '\n'
                << "      NULL, /* base_init */" << '\n' << "      NULL, /* base_finalize */"
                << '\n' << "      (GClassInitFunc) " << this->nspace_lc << name_u << "_class_init,"
                << '\n' << "      NULL, /* class_finalize */" << '\n'
                << "      NULL, /* class_data */" << '\n' << "      sizeof (" << this->nspace
                << name << ")," << '\n' << "      0, /* n_preallocs */" << '\n'
                << "      (GInstanceInitFunc) " << this->nspace_lc << name_u << "_instance_init,"
                << '\n' << "      NULL, /* value_table */" << '\n' << "    };" << '\n' << '\n'
                << "    type = g_type_register_static (THRIFT_TYPE_STRUCT, " << '\n'
                << "                                   \"" << this->nspace << name << "Type\","
                << '\n' << "                                   &type_info, 0);" << '\n' << "  }"
                << '\n' << '\n' << "  return type;" << '\n' << "}" << '\n' << '\n';
}