gtk+ object type class ---- How Button created

Object and Class Structures

Each GtkObject has two essential components: a struct representing an instance of the object, and a struct representing the class. In general, the instance struct contains the data members for each instance, and the class struct contains class function pointers (which can be overridden by subclasses). The class struct can also contain class data members---however, it's more typical to use static variables in the .c file implementing the object. If you're familiar with C++, the class struct is equivalent to a vtable, only the class struct is written by hand. It stores virtual functions for an object type.

Here are the structs used in GtkButton:

typedef struct _GtkButton       GtkButton;
typedef struct _GtkButtonClass  GtkButtonClass;

struct _GtkButton
{
  GtkBin bin;

  GtkWidget *child;

  guint in_button : 1;
  guint button_down : 1;
  guint relief : 2;
};

struct _GtkButtonClass
{
  GtkBinClass        parent_class;
  
  void (* pressed)  (GtkButton *button);
  void (* released) (GtkButton *button);
  void (* clicked)  (GtkButton *button);
  void (* enter)    (GtkButton *button);
  void (* leave)    (GtkButton *button);
};
Notice that the first member of struct _GtkButton is GtkBin---that's because GtkButton is a subclass of GtkBin. (GtkBin is a GtkContainer that can hold one child.) Since GtkBin is the first member, we can safely cast a GtkButton to GtkBin. In struct _GtkButtonClass, the same principle applies, and GtkBinClass is the first member.

9.2: Type Checking and New Types

GTK+ has an extensive type system, which is to some extent independent of its object system. However, the object system makes use of the larger type system. Every object has a type, and every type has a unique integer identifier. When writing a GtkObject, it's customary to provide a function which returns the type's identifier.

In the case of GtkButton, the relevant function is:

GtkType gtk_button_get_type();

The first time this function is invoked, it will register a GtkButton type with the object system, and in the process obtain a type identifier. On subsequent calls, the type identifier is simply returned. GtkType is a typedef (unsigned int is the actual type of GTK+'s type identifiers).

PRODUCTION: Please add this footnote to the previous sentence: "You should not make assumptions about GtkType values. However, each GtkType has a uniquesequence number associated with it; sequence numbers are optimized to be array indices (that is, they are densely-packed and as small as possible). You can extract sequence numbers with the GTK_TYPE_SEQNO() macro and use them to index a table containing information about types."

The type system allows GTK+ to check the validity of casts. To facilitate this, objects customarily provide macros like these in their header file:

#define GTK_TYPE_BUTTON            (gtk_button_get_type ())
#define GTK_BUTTON(obj)            (GTK_CHECK_CAST ((obj), \
                                    GTK_TYPE_BUTTON, GtkButton))
#define GTK_BUTTON_CLASS(klass)    (GTK_CHECK_CLASS_CAST ((klass), \
                                    GTK_TYPE_BUTTON, GtkButtonClass))
#define GTK_IS_BUTTON(obj)         (GTK_CHECK_TYPE ((obj),  \
                                    GTK_TYPE_BUTTON))
#define GTK_IS_BUTTON_CLASS(klass) (GTK_CHECK_CLASS_TYPE ((klass),  \
                                    GTK_TYPE_BUTTON))


Instead of simply casting an object, you can use the GTK_BUTTON() macro. If GTK_NO_CHECK_CASTS is defined, these macros are equivalent to simple casts. Otherwise, they retrieve the type of the object and compare it to the type you're attempting to cast to.

PRODUCTION: Please add this footnote to the previous sentence: "When you compile GTK+, use the --enable-debug option to configure to enable type checking." Thanks -hp

GTK+ also provides convenient runtime type checking, with the GTK_IS_BUTTON() macro. This is often used in preconditions; for example, a function expecting a button as an argument might have this check at the beginning:

  g_return_if_fail(GTK_IS_BUTTON(widget));

The GTK+ and Gnome library functions have many such checks. You can also use the macro to make certain code conditional on an object's type, though this is most likely a poor idea from a design standpoint.

To give you an idea what sort of information GTK+ stores about each object type, here's the implementation of gtk_button_get_type():

GtkType
gtk_button_get_type (void)
{
  static GtkType button_type = 0;

  if (!button_type)
    {
      static const GtkTypeInfo button_info =
      {
        "GtkButton",
        sizeof (GtkButton),
        sizeof (GtkButtonClass),
        (GtkClassInitFunc) gtk_button_class_init,
        (GtkObjectInitFunc) gtk_button_init,
        /* reserved_1 */ NULL,
        /* reserved_2 */ NULL,
        (GtkClassInitFunc) NULL,
      };

      button_type = gtk_type_unique (GTK_TYPE_BIN, &button_info);
      gtk_type_set_chunk_alloc (button_type, 16);
    }

  return button_type;
}


The code fills in a struct with information about the class, then hands that struct to GTK+ to get a type identifier (GtkType). Only six components of theGtkTypeInfo struct are important. GtkButton gives GTK+ a human-readable name for the class, used in error messages and the like; the size of the instance and class structs; then a function to initialize the class struct and another to initialize each new instance. The sixth and seventh members of the struct (reserved_1 and reserved_2) are obsolete and preserved only for compatibility. The final member is a pointer to a base class initialization function, used to initialize the class struct of any subclasses.

gtk_type_unique() registers the new type and obtains a type identifier. The GTK_TYPE_BIN argument is a macro containing the type of GtkButton's parent class,GtkBin. The call to gtk_type_set_chunk_alloc() optimizes memory allocation for this type; it is never required, and should only be used for frequently-allocated types like GtkButton.

Given a registered GtkButton type, the following code creates a type instance:

GtkWidget*
gtk_button_new (void)
{
  return GTK_WIDGET (gtk_type_new (gtk_button_get_type ()));
}


The newborn GtkButton will be initialized by its instance initializer. The instance initialization function is called each time an instance of the type is created; it gives the object's data members reasonable default values:

static void
gtk_button_init (GtkButton *button)
{
  GTK_WIDGET_SET_FLAGS (button, GTK_CAN_FOCUS);
  GTK_WIDGET_UNSET_FLAGS (button, GTK_NO_WINDOW);

  button->child = NULL;
  button->in_button = FALSE;
  button->button_down = FALSE;
  button->relief = GTK_RELIEF_NORMAL;
}


Remember that gtk_button_init() was passed to gtk_type_unique() when the GtkButton type was created. GTK+ stores the function pointer and uses it to createGtkButton instances.

Instance structs are created with all bits set to 0; so settings members to 0 or NULL is not strictly necessary. Still, most GTK+ code does initialize the members, for clarity.

The class initialization and base class initialization functions require some background information to understand fully; you will know how to write them after you read this chapter.

Initializing a New Class

When a type is first used, GTK+ creates an instance of its class struct (using the information supplied to gtk_type_unique()). To initialize the class struct for a type, GTK+ first checks that all parent classes are initialized and initializes them if not. Then it fills the top portion of the class struct with a byte-for-byte copy of the parent's class struct. This means the subclass inherits any function pointers found in the parent class.

Next, the base class initialization functions of each parent class and that of the class itself are called in order, starting with GtkObject. (The base class init function is the last argument to gtk_type_unique()). A base class initializer is optional; in the GtkButton case, there is none. If present, the base class initializer supplements the byte-for-byte copy of the class struct; for example, some functions should not be inherited. To prevent class function inheritance, the base class initializer can zero certain function pointers. Normally you do not need a base class initializer.

Finally, GTK+ calls the type's own class init function. The class init function can override functions from the parent class by replacing them in the class struct. It should also fill in any functions unique to the subclass, and register signals and object arguments (discussed later in the chapter).

A concrete example should make the class creation process clear. The class hierarchy for GtkButton is shown in Figure 9.1. When the GtkButton type is registered, an empty GtkButtonClass is created. This class struct is initialized as follows:

  1. The class struct for GtkBinGtkButton's immediate parent, is copied into it. This means GtkButton inherits class functions from GtkBin.
  2. The base class initialization function for GtkObject is called on it. This zeroes some GtkObject class functions that should not be inherited.
  3. There is no base class initializer for GtkWidget, or it would be called.
  4. The base class initializer for GtkContainer is called. This zeroes some GtkContainer class functions that should not be inherited, and initializes aGtkContainerClass data member.
  5. There is no base class initializer for GtkBin, or it would be called.
  6. There is no base class initializer for GtkButton, or it would be called.
  7. The class initializer is called for GtkButton. This fills in the GtkButtonClass structure, registers signals, and registers object arguments.

When writing a new class, you only need to concern yourself with the final two steps---you should consider whether a base class initializer is needed, and supply it if so; you must supply a class initializer in all cases.

Figure 9.1: GtkButton Ancestry

PRODUCTION: The "GtkButton Ancestry" figure should show the following information, drawn nicely:

GtkObject
   |
GtkWidget
   | 
GtkContainer
   | 
GtkBin
   | 
GtkButton

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值