`
cloverprince
  • 浏览: 127353 次
  • 性别: Icon_minigender_1
  • 来自: 北京
社区版块
存档分类
最新评论

GObject 2-01: A convention-compliant minimal complete working example.

阅读更多
My coding style has been so weird that no proper GObject programmers would ever use.

GObject do have their coding/naming convention which is "designed by both smart and experienced people" and I am supposed "to put my ego aside".

Here are the official document about the "conventions":
:: http://library.gnome.org/devel/gobject/stable/gtype-conventions.html

-----------------------

Here is a minimal working example that is compliant to the coding convention.  Yes, it compiles and runs!  I admit I had a hard time making my first GObject program running.  These code is mostly based on the "boilerplate" examples in the official GObject reference.

Comments omitted for cleanness and minimality.

Save this as maman-bar.h:
#ifndef __MAMAN_BAR_H__
#define __MAMAN_BAR_H__

#include <glib-object.h>

#define MAMAN_TYPE_BAR                  (maman_bar_get_type ())
#define MAMAN_BAR(obj)                  (G_TYPE_CHECK_INSTANCE_CAST ((obj), MAMAN_TYPE_BAR, MamanBar))
#define MAMAN_IS_BAR(obj)               (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MAMAN_TYPE_BAR))
#define MAMAN_BAR_CLASS(klass)          (G_TYPE_CHECK_CLASS_CAST ((klass), MAMAN_TYPE_BAR, MamanBarClass))
#define MAMAN_IS_BAR_CLASS(klass)       (G_TYPE_CHECK_CLASS_TYPE ((klass), MAMAN_TYPE_BAR))
#define MAMAN_BAR_GET_CLASS(obj)        (G_TYPE_INSTANCE_GET_CLASS ((obj), MAMAN_TYPE_BAR, MamanBarClass))

typedef struct _MamanBar        MamanBar;
typedef struct _MamanBarClass   MamanBarClass;

struct _MamanBar
{
  GObject parent_instance;
};

struct _MamanBarClass
{
  GObjectClass parent_class;
};

GType maman_bar_get_type (void);

#endif /* __MAMAN_BAR_H__ */


Save this as maman-bar.c:
#include "maman-bar.h"

G_DEFINE_TYPE (MamanBar, maman_bar, G_TYPE_OBJECT);

static void maman_bar_init (MamanBar *self)
{
}

static void maman_bar_class_init (MamanBarClass *klass)
{
}


Save this as main.c:
#include <glib-object.h>
#include "maman-bar.h"

int main() {
    g_type_init();  /* NOTE: Don't forget this!!!! */

    MamanBar *bar = g_object_new(MAMAN_TYPE_BAR, NULL);

    g_object_unref(bar);

    return 0;
}


Compile with:
引用

gcc $(pkg-config --cflags --libs gobject-2.0) *.c -o main


There should be no warnings or errors.

------------------------

Let's now have a look at our code.

We are creating a class named Bar in the namespace "Maman".  Quite different from how we would define a class in C++:
namespace Maman {
class Bar : public GObject {
   ...
};
}

we instead follow the "convention" of GObject.  Convention is important since we are writing in C.  There is no language-level notion of "object", "type", "class", "instance", "type-id", "dynamic type casting", "runtime type query", "runtime membership checking" or all other object-oriented buzzwords.

Remember how we "define" a class in GObject?  (see http://cloverprince.iteye.com/blog/498390 if you don't)  We register our new class with the g_type_register_static function, passing a filled GTypeInfo struct and some other parameters into it and it is done.  After this, GObject will recognize our new class.

How could we define a class in a "convention"-compliant way?  Let's look at our maman-bar.c.  There is a macro:
G_DEFINE_TYPE (MamanBar, maman_bar, G_TYPE_OBJECT);

See the reference about how to use it.
This will expand to (almost):
static void maman_bar_init(MamanBar * self);
static void maman_bar_class_init(MamanBarClass * klass);
static gpointer maman_bar_parent_class = ((void *) 0);

static void maman_bar_class_intern_init(gpointer klass)
{
    maman_bar_parent_class = g_type_class_peek_parent(klass);
    maman_bar_class_init((MamanBarClass *) klass);
}

GType maman_bar_get_type(void)
{
    static volatile gsize g_define_type_id__volatile = 0;
    if (g_once_init_enter(&g_define_type_id__volatile)) {
        GType g_define_type_id =
            g_type_register_static_simple(((GType) ((20) << (2))),
                                          g_intern_static_string
                                          ("MamanBar"),
                                          sizeof(MamanBarClass),
                                          (GClassInitFunc)
                                          maman_bar_class_intern_init,
                                          sizeof(MamanBar),
                                          (GInstanceInitFunc)
                                          maman_bar_init,
                                          (GTypeFlags) 0);
        {
            {
                {
                };
            }
        }
        g_once_init_leave(&g_define_type_id__volatile, g_define_type_id);
    }
}

You could apply the -E option of gcc to see yourself.

It generates a function named maman_bar_get_type.  Our new type-id is stored as a static variable.  During the first time we call this function, it will be initialized.  On subsequent calls, this type-id will be simply returned.

Such a function is always (by convention) named <namespace>_<classname>_get_type and takes no argument.

Also in maman-bar.c, there are:
static void maman_bar_init (MamanBar *self)
{
}

static void maman_bar_class_init (MamanBarClass *klass)
{
}

What are they?  They are initializer functions, much like "constructors" in C++.  Well, you could omit the "constructor" in C++.  Actually you COULD omit those init functions if you do not use the G_DEFINE_TYPE macro and register your type with g_type_register_static by yourself.  Since we are using G_DEFINE_TYPE, we need to supply them.

By the way, initializing functions are (by convention) named <namespace>_<class>_init and <namespace>_<class>_class_init for instance init function and class init function, respectively.  See my previous articles if you are not familiar with these concepts.

The complete maman-bar.c should look like this:
/*
 * Copyright information
 */

/* Include the header file (see below) */
#include "maman-bar.h"

G_DEFINE_TYPE (MamanBar, maman_bar, G_TYPE_OBJECT);

static void maman_bar_init (MamanBar *self)
{
    /* TODO: Initialize *self here */
}

static void maman_bar_class_init (MamanBarClass *klass)
{
    GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
    /* TODO: Initialize *klass here */
}



The above maman-bar.c implements the class.  Now we need an interface to the outer world so that other people could use our class.

Just look into the well-commented version of maman-bar.h:
/*
 * Copyright/Licensing information.
 */

/* Inclusion guard.
 * So that this header file will be included twice. */
#ifndef __MAMAN_BAR_H__
#define __MAMAN_BAR_H__

#include <glib-object.h>
/*
 * TODO: Potentially, include other headers on which this header depends.
 */

/*
 * Type macros.
 *
 * What are they?
 *
 * They are not mandatory.  I deliberately omitted them in my
 * previous articles in order to show their unnecessity.
 * However, other GObject programmers are expecting me to
 * provide these CONVENIENCE macros.  I could also safely assume
 * that other (properly written) GObject programs provide
 * similar macros, too.
 */

/* This macro returns the type-id.  It just call the maman_bar_get_type() */
#define MAMAN_TYPE_BAR            (maman_bar_get_type ())

/* This macro casts obj (an instance) to this type.
 * Run-time type checking is performs to make sure the conversion
 * is successful. */
#define MAMAN_BAR(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), MAMAN_TYPE_BAR, MamanBar))

/* This macro checks whether obj (an instance) is an instance of
 * MamanBar or its derived classes. */
#define MAMAN_IS_BAR(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MAMAN_TYPE_BAR))

/* These two macros are similar, but for classes, not instances. */
#define MAMAN_BAR_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass), MAMAN_TYPE_BAR, MamanBarClass))
#define MAMAN_IS_BAR_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), MAMAN_TYPE_BAR))

/* This macro obtains the class struct (MamanBarClass) from obj (an instance). */
#define MAMAN_BAR_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS ((obj), MAMAN_TYPE_BAR, MamanBarClass))

/* typedef the instance struct and class struct.  By convention, 
 * these two struct types are named in PascalCase and accessed
 * without the "struct" keyword. */
typedef struct _MamanBar        MamanBar;
typedef struct _MamanBarClass   MamanBarClass;

/* The actual instance struct. */
struct _MamanBar
{
  GObject parent_instance;

  /* TODO: Add instance members (member variables) here. */
};

/* The actual class struct. */
struct _MamanBarClass
{
  GObjectClass parent_class;

  /* TODO: Add class members (virtual member functions) here. */
};

/* used by MAMAN_TYPE_BAR */
GType maman_bar_get_type (void);

/*
 * TODO: Add method definitions.
 */

#endif /* __MAMAN_BAR_H__ */


In the main.c, #include the maman-bar.h to use this class.  Don't forget to call g_type_init(), which is required by all GObject-related libraries.


Summerize:
In order to define and use a class, you:
  • Define the .h file, which includes an instance struct, a class struct, some convenience macros and prototypes of member functions.  (Copy and paste the boilerplate is not a sin.  Some code-generators also work well.)
  • Define the .c file, which #include the .h file, uses the G_DEFINE_TYPE macro, and defines the two init functions.
  • Call g_type_init() in main().
  • #include the .h file and use it.
分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics