Chinaunix首页 | 论坛 | 博客
  • 博客访问: 251140
  • 博文数量: 57
  • 博客积分: 2407
  • 博客等级: 大尉
  • 技术积分: 410
  • 用 户 组: 普通用户
  • 注册时间: 2008-08-07 15:41
文章存档

2021年(1)

2016年(1)

2014年(3)

2012年(10)

2011年(35)

2010年(1)

2009年(3)

2008年(3)

分类: C/C++

2011-08-30 15:49:10

这段时间以来,一直在跟 GObject 死磕。除了有点枯燥与乏味之外,也没什么不适,就是一堆繁琐但还算是直观的 C 代码罢了。现在,我想让 GObject 单调的学习过程略微轻松一下。毕竟春天正在到来,窗外的迎春花已经怒放了。记得前段时间为自己开始学习 GObject 写了一篇序言“”,其中引用了 的一篇文章“”。本文通过一个很小的实例,演示一下 GObject 程序如何通过 GObject Introspection 与 JavaScript 脚本进行结合。

KbBibtex 类的回放

我们曾经在“”这篇文档中构造了一个 KbBibtex 类,其声明文件 kb-bibtex.h 如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
#ifndef KB_BIBTEX_H
#define KB_BIBTEX_H
  
#include
 
#define KB_TYPE_BIBTEX (kb_bibtex_get_type ())
#define KB_BIBTEX(object) \
        G_TYPE_CHECK_INSTANCE_CAST ((object), KB_TYPE_BIBTEX, KbBibtex)
#define KB_IS_BIBTEX(object) \
        G_TYPE_CHECK_INSTANCE_TYPE ((object), KB_TYPE_BIBTEX))
#define KB_BIBTEX_CLASS(klass) \
        (G_TYPE_CHECK_CLASS_CAST ((klass), KB_TYPE_BIBTEX, KbBibtexClass))
#define KB_IS_BIBTEX_CLASS(klass) \
        (G_TYPE_CHECK_CLASS_TYPE ((klass), KB_TYPE_BIBTEX))
#define KB_BIBTEX_GET_CLASS(object) \
        (G_TYPE_INSTANCE_GET_CLASS ((object), KB_TYPE_BIBTEX, KbBibtexClass))
 
 
typedef struct _KbBibtex KbBibtex;
struct _KbBibtex {
    GObject parent;
};
  
typedef struct _KbBibtexClass KbBibtexClass;
struct _KbBibtexClass {
    GObjectClass parent_class;
};
  
GType kb_bibtex_get_type (void);
 
void kb_bibtex_printf (KbBibtex *self);
 
#endif

其定义文件  kb-bibtex.c 如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
#include "kb-bibtex.h"
  
G_DEFINE_TYPE (KbBibtex, kb_bibtex, G_TYPE_OBJECT);
 
#define KB_BIBTEX_GET_PRIVATE(object) (\
        G_TYPE_INSTANCE_GET_PRIVATE ((object), KB_TYPE_BIBTEX, KbBibtexPrivate))
  
typedef struct _KbBibtexPrivate KbBibtexPrivate;
struct _KbBibtexPrivate {
        GString *title;
        GString *author;
        GString *publisher;
        guint   year;
};
 
enum PROPERTY_BIBTEX {
        PROPERTY_0,
        PROPERTY_TITLE,
        PROPERTY_AUTHOR,
        PROPERTY_PUBLISHER,
        PROPERTY_YEAR,
        N_PROPERTIES
};
 
static void
kb_bibtex_set_property (GObject *object, guint property_id,
                        const GValue *value, GParamSpec *pspec)
{     
        KbBibtex *self = KB_BIBTEX (object);
        KbBibtexPrivate *priv = KB_BIBTEX_GET_PRIVATE (self);
           
        switch (property_id) {
        case PROPERTY_TITLE:
                if (priv->title)
                        g_string_free (priv->title, TRUE);
                priv->title = g_string_new (g_value_get_string (value));
                break;
        case PROPERTY_AUTHOR:
                if (priv->author)
                        g_string_free (priv->author, TRUE);
                priv->author = g_string_new (g_value_get_string (value));
                break;
        case PROPERTY_PUBLISHER:
                if (priv->publisher)
                        g_string_free (priv->publisher, TRUE);
                priv->publisher = g_string_new (g_value_get_string (value));
                break;
        case PROPERTY_YEAR:
                priv->year = g_value_get_uint (value);
                break;
        default:
                G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
                break;
        }
}
  
static void
kb_bibtex_get_property (GObject *object, guint property_id,
                        GValue *value, GParamSpec *pspec)
{
        KbBibtex *self = KB_BIBTEX (object);
        KbBibtexPrivate *priv = KB_BIBTEX_GET_PRIVATE (self);
        GString *similar = NULL;
           
        switch (property_id) {
        case PROPERTY_TITLE:
                g_value_set_string (value, priv->title->str);
                break;
        case PROPERTY_AUTHOR:
                g_value_set_string (value, priv->author->str);
                break;
        case PROPERTY_PUBLISHER:
                g_value_set_string (value, priv->publisher->str);
                break;
        case PROPERTY_YEAR:
                g_value_set_uint (value, priv->year);
                break;
        default:
                G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
                break;
        }
}
 
static void
kb_bibtex_init (KbBibtex *self)
{
}
 
static void
kb_bibtex_class_init (KbBibtexClass *klass)
{
        g_type_class_add_private (klass, sizeof (KbBibtexPrivate));
  
        GObjectClass *base_class = G_OBJECT_CLASS (klass);
        base_class->set_property = kb_bibtex_set_property;
        base_class->get_property = kb_bibtex_get_property;
                GParamSpec *properties[N_PROPERTIES] = {NULL,};
        properties[PROPERTY_TITLE] =
                g_param_spec_string ("title",
                                     "Title",
                                     "Bibliography title",
                                     NULL,
                                     G_PARAM_READWRITE);
        properties[PROPERTY_AUTHOR] =
                g_param_spec_string ("author",
                                     "Author",
                                     "Bibliography author",
                                     NULL,
                                     G_PARAM_READWRITE);
        properties[PROPERTY_PUBLISHER] =
                g_param_spec_string ("publisher",
                                     "Publisher",
                                     "Bibliography Publisher",
                                     NULL,
                                     G_PARAM_READWRITE);
        properties[PROPERTY_YEAR] =
                g_param_spec_uint ("year",
                                   "Year",
                                   "Bibliography year",
                                   0,
                                   G_MAXUINT,
                                   0,
                                   G_PARAM_READWRITE);
        g_object_class_install_properties (base_class, N_PROPERTIES, properties);
}
 
void
kb_bibtex_printf (KbBibtex *self)
{
        gchar *title, *author, *publisher;
        guint year;
  
        g_object_get (G_OBJECT (self),
                      "title",     &title,
                      "author",    &author,
                      "publisher", &publisher,
                      "year",      &year,
                      NULL);
  
        g_printf ("    Title: %s\n"
                  "   Author: %s\n"
                  "Publisher: %s\n"
                  "     Year: %d\n", title, author, publisher, year);
  
        g_free (title);
        g_free (author);
        g_free (publisher);
}

通过这次的回放,我不禁自得于我还不错的体力,可以写这么多的 C 代码 :(

KbBibtex 类的调用者

在“”文档中,KbBibtex 类的调用者是 main.c 源文件中的 main 函数。但是,在本文中,KbBibtex 类的调用者则另有其人,见下面的代码:

1
2
3
4
5
6
7
8
9
10
11
const Kb = imports.gi.Kb;
 
function start()
{
    let bibtex = new Kb.Bibtex ({title:"The {\\TeX}Book",
                 author:"Knuth, D. E.",
                 publisher:"Addison-Wesley Professional",
                 year:1984});
     
    bibtex.printf ();
}

@#¥%……这就是传说中的 JavaScript 代码,它调用了 KbBibtex 类。这些代码等价于下面的 C 代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
#include "kb-bibtex.h"
  
int
main (void)
{
        g_type_init ();
  
        KbBibtex *entry = g_object_new (KB_TYPE_BIBTEX,
                                        "title", "The {\\TeX}Book",
                                        "author", "Knuth, D. E.",
                                        "publisher", "Addison-Wesley Professional",
                                        "year", 1984,
                                        NULL);
  
        kb_bibtex_printf (entry);
          
        g_object_unref (entry);
        return 0;
}

现在,我们将上述的 JavaScript 脚本命名为 main.js。

JavaScript 的解析程序

我们常见的 JavaScript 代码是嵌在网页里被浏览器解析执行的,虽然 main.js 中的 JavaScript 代码和网页中的 JavaScript 代码是两码事,但是它也需要解析器,这个解析器的名字叫做 。

Gjs 主要基于 Firefox 的 JavaScript 引擎 和 实现,前者可以解析执行 JavaScript 脚本,后者可以帮助 JavaScript 脚本调用 GObject 子类的方法。

下面,便基于 Gjs 构建一个可执行 main.js 中的 start 函数的 C 程序:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
#include
#include
 
int
main(int argc, char *argv[])
{
        g_type_init ();
         
        gchar *js_path[] = {"./"};
        gchar *gir_path  = "./";
         
        GOptionContext *ctx = g_option_context_new (NULL);
        g_option_context_add_group (ctx, g_irepository_get_option_group ());
        g_option_context_parse (ctx, &argc, &argv, NULL);
 
        /* 设置 typelib 文件查询路径 */
        g_irepository_prepend_search_path (gir_path);
 
        /* 设置 JavaScript 文件路径并解析执行指定的 JavaScript 脚本 */
        GjsContext *gjs_ctx = gjs_context_new_with_search_path (js_path);
        gjs_context_eval (gjs_ctx,
                          "const Main = imports.main; Main.start();", /* 要执行的代码 */
                          -1,
                          "
", /* main.js 文件名 */
                          NULL,
                          NULL);
         
        return 0;
}

上述代码中,我添加了一些注释。对于 Gjs 和 GObject Introspection 的用法,目前我也仅知道这些。现在,我们只需要关注一点,那就是在 main.c 文件中,我们没有一行代码是与前面的 KbBibtex 类显式相关。这个 main.c 完全是一个独立的程序,它的主要职责就是解析执行一个 JavaScript 脚本。

编译与运行

下面,开始编译这个混杂了 JavaScript 脚本的程序。

首先,将上述所有代码的相关文件放在同一个目录下(主要是方便测试,实际工程中则不然),文件清单如下:

1
2
$ cd test && ls
kb-bibtex.c  kb-bibtex.h  main.c  main.js

编译 kb-bibtex.c 与 main.c,并将它们的中间文件连到一起:

1
2
3
4
5
6
7
8
9
# 编译 kb-bibtex.o
$ gcc -c kb-bibtex.c $(pkg-config --cflags gobject-2.0)
 
# 编译 main.o
$ gcc -c main.c $(pkg-config --cflags gjs-1.0)
 
# 连接 main.o 与 kb-bibtex.o,输出 test 程序
$ gcc main.o kb-bibtex.o -o test \
          $(pkg-config --libs gjs-1.0 gobject-introspection-1.0)

当然,我们可以不必像上面那么麻烦,直接执行下面的命令即可:

1
2
$ gcc kb-bibtex.c main.c -o test $(pkg-config --cflags --libs \
                                   gjs-1.0 gobject-introspection-1.0)

然后使用 g-ir-scanner 工具产生 Kb-1.0.gir 文件:

1
2
3
4
$ g-ir-scanner --namespace=Kb --nsversion=1.0 \
               --include=GObject-2.0 --pkg=gobject-2.0 \
               --program=test kb-bibtex.h kb-bibtex.c \
               -o Kb-1.0.gir

生成的 Kb-1.0.gir 主要是被 g-ir-compiler 工具使用,产生二进制文件 Kb-1.0.typelib,如下:

1
$ g-ir-compiler Kb-1.0.gir -o Kb-1.0.typelib

Kb-1.0.gir 文件是 XML 格式,主要用于记录 KbBibtex 类的详细信息,g-ir-compiler 所做的工作是将其处理为二进制格式的 Kb-1.0.typelib 文件,目的在于后者体积更小,解析更快!

上述的编译过程,主要是获得了一个 test 程序和一个 Kb-1.0.typelib 二进制文件。现在,执行 test 程序可得到以下输出:

1
2
3
4
5
$ ./test
    Title: The {\TeX}Book
   Author: Knuth, D. E.
Publisher: Addison-Wesley Professional
     Year: 1984

这与“”文档中测试程序的输出结果是相同的。

现在,我们一定要回想一下,test 程序是对 kb-bibtex.c 与 main.c 的编译和连接而成的,其中 kb-bibtex.c 中没有调用 KbBibtex 类的的任何方法,main.c 也同样,并且 main.c 中只是解析执行了 main.js 脚本。所以,可以肯定 main.js 不仅完成了一个 KbBibtex 对象的构造还调用了它的 printf 方法。

那么,这一切意味着什么?

这当然是意味着所有基于 GObject 库的 C 程序 {@#¥%} 都 可交由 g-ir-scanner 与 g-ir-compiler 生成 *.gir 与 *.typelib 文件,然后所有的编程语言(Python、Lua、Ruby、Haskell、JavaScript……),只要它们可以与 C 语言混合编程,那么都可以基于 GObject Introspection 与一些 *.typelib 文件调用 {@#¥%} 程序中的函数。

嗯,这样究竟有什么好处?

自然是 C 程序员任劳任怨的去做最底层的工作,动态语言和函数语言的爱好者有机会使用他们最衷爱的语言去写上层模块,比如大量的扩展/插件。例如 GNOME 3 桌面的核心组件 gnome shell 的外围部分(例如 Overview 视图、工作区、面板等),都是使用 JavaScript 脚本写的。

阅读(2681) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~