1.4.8 异常处理错误报告
      在上一节里面我们我们诊断贺消除程序运行中的严重错误。然而,那些非致命的错误上一节的做法就太激进了。你的程序自己对待一些特殊方式有克服、忽视或者处理等办法。例如,如果一个图形应用程序不能打开一个弹出的文件浏览器选择了一个文件,你通常不希望中止整个应用程序。相反,你可能想找出是什么问题,然后进行处理,可以弹出出了一个对话框或者让你点击文件浏览器中的“取消”按钮。
      人们往往希望在不同错误类型的时候用异常处理来进行补救。(GLib的在线手册使用的术语的错误报告)传统的C风格的函数,在测试调用结束以后返回特殊的错误代码,一些功能提供了额外的细节(例如,通过errno这个全局变量)。而C++和Java这类高级语言提供了一种特殊的语法来进行异常处理,像try{}, throw(), catch(){}.
      GLib中的异常处理没有像那样复杂的功能,因为GLib用的是C。但是它确实提供了一个称为GError系统,比自己重新动手写一个C的方法要容易使用的多。GError的数据结构是核心:你如何使用这种数据结构和它的实施是同样重要的。
 
      GError和GError函数
      使用GError的函数都把GError指针的地址作为最后一个参数。如果你想用一个GError的变量err要定义为GError *err,传递的时候用&err。此外你要设置指针的值为0。你还可以指定这个参数为NULL,这时该函数将禁用错误报告。
      一个GError结构体有以下字段:
domain(GQuark类型):一个标签代表模块或者子系统错误,域名或者类的错误。每一个错误域必须有一个宏定义的格式 PREFIX_MODULE_ERROR(例如,G_FILE_ERROR)。宏扩展到夸克的数值的形式返回。
code(gint类型):错误代码;这就是错误域内的特定错误。每一个可能的错误代码需要一个相应的符号称为PrefixModuleError枚举类型的形式PREFIX_MODULE_ERROR_CODE(例如,在GFileError G_FILE_ERROR_TYPE)。
message(gchar*类型):用通俗易懂的语言完整描述一个错误。
 
      下面的代码片段演示了如何从一个函数的错误条件(do_something())使用GError:
 GError *error = NULL;

      /* use this GError variable as last argument */
      do_something(arg1, arg2, &error);

      /* was there an error? */
      if (error != NULL)
      {
	/* report the message */
  	g_printerr("Error when doing something: %s\n", error->message);

  	/* free error structure */
  	g_error_free(error);
      }

 

 你可以看到这个代码需要你完成后用g_error_free()释放GError。因此,如果你提供一个GError参数的函数,你应该经常检查,否则,你的内存有泄漏的危险。
      如果你想要做报告错误以外的东西,你可能想知道错误的domain和code。而不是用手工去检查,你应该使用g_error_matches()函数匹配错误中的domain和code。第一个参数是GError结构,第二个是错误的domain,第三个是一个特定的错误code。如果错误匹配domain和code,该函数返回TRUE,否则返回FALSE。下面是一个例子:
	GError *error = NULL;
	gchar *filename;
	BluesGuitar *fender, *bender;

	<< .. >>
	
	filename = blues_improvise(fender, BLUES_A_MAJOR, &error);
	if (error != NULL)
	{
		/* see if the expensive guitar is broken */
  		if (g_error_matches(error, BLUES_GUITAR_ERROR, BLUES_GUITAR_ERROR_BROKEN))
  		{
			/* if so, try the cheap guitar */
    			g_clear_error(&error);
    			filename = blues_improvise(bender, BLUES_A_MAJOR, &error);
  		}
	}

	/* if nothing's working, default to Clapton */
	if (error != NULL)
	{
		filename = g_strdup("clapton-1966.wav");
  		g_error_free(error);
	}

	blues_play(filename);

 

 

  在这个例子中,函数blues_improvise()运行没有问题的情况下后返回一个文件名。如果出现错误,程序就检查错误code BLUES_GUITAR_ERROR_BROKEN在domain BLUES_GUITAR_ERROR里么。如果是这个问题,程序就尝试不同时间的不同参数。试图在此之前,它会调用g_clear_error()来清除错误,这个函数释放GError结构体并且把指针重置为NULL。
      如果有错误的东西,这个第二次尝试后,显示的东西仍然无法正常工作的权利,该方案放弃了。而不是试图再blues_improvise()调用,它使用默认的文件名(“clapton-1966.wav”),所以blues_play()可以做它的事。
      警告:
您使用GError*结构后,应该立即释放并复位指针。 GError启用的功能可以不使用相同的指针,但是在同一时间的几个错误,只有一个错误的空间。如前所述,如果你不释放GError内存​​,你的程序将有内存泄漏。
 
      定义自己的错误条件
      要在自己的函数里面使用GError系统来报告错误,要做到以下几点:
      1.创造一个适当命名的宏,扩展到一个唯一的GQuark值定义错误domain。
      2.定义一个枚举类型的所有错误code。
      3.在你的函数里面增加一个GError**类型的参数(也就是说,这个参数是一个指针到GError结构的指针)。如果该函数使用可变参数,这个参数要在va_args(...)之前.
      4.在你的函数检测到一个错误的地方,创建一个新的GError结构,并填写在相应的情况。
 
 
      这里是一个错误的域名和一些代码的定义:
 /* define the error domain */
      #define MAWA_DOSOMETHING_ERROR (mawa_dosomething_error_quark())
      
      GQuark mawa_dosomething_error_quark(void)
      {
        static GQuark q = 0;
	if (q == 0)
	{
		q = g_quark_from_static_string("mawa-dosomething-error");
	}
  	return(q);
      }

      /* and the error codes */
      typedef enum {
       MAWA_DOSOMETHING_ERROR_PANIC,
       MAWA_DOSOMETHING_ERROR_NO_INPUT,
       MAWA_DOSOMETHING_ERROR_INPUT_TOO_BORING,
       MAWA_DOSOMETHING_ERROR_FAILED   /* abort code */
      }

 

  前面这个例子要注意的是mawa_dosomething_error_quark()。它创建新的错误域夸克如果不存在,但一个静态变量q的结果就没有改变,所以,它并没有执行任何额外的计算连续调用。
      这个片段说明了如何使用新的域名和代码:
 void mawa_dosomething_simple(GError **error)
      {     
        gint i;
	gboolean it_worked;

	<< do something that sets it_worked to TRUE or FALSE >>

	if (!it_worked)
	{
		g_set_error(error, MAWA_DOSOMETHING_ERROR,
		            MAWA_DOSOMETHING_ERROR_PANIC,
			    "Panic in do_something_simple(), i = %d", i);
	}
      }

 

 这个函数“做了一些事情”如果失败。调用g_set_error()来设置返回的错误结果。此函数作为第一个参数错误指针的地址,如果不为NULL,则设置了一个新分配的GError结构的指针。 g_set_error()函数填充这个结构的第三个和第四个参数(错误域和码),其余的参数的printf()格式字符串和参数列表,成为GError的消息字段。
      如果你想从另一个函数使用错误代码,您需要特别小心:
 void mawa_dosomething_nested(GError **error)
      {
	gint i;
      	gboolean it_worked;
  	GError *simple_error = NULL;

  	<< do something >>

  	if (!it_worked)
  	{
		g_set_error(error,
			MAWA_DOSOMETHING_ERROR,
                	MAWA_DOSOMETHING_ERROR_PANIC,
                	"Panic in do_something_nested(), i = %d", i);
    		return;
  	}

  	do_something_simple(&simple_error);
  	if (simple_error != NULL)
  	{

		<< additional error handling >>

        	g_propagate_error(error, simple_error);
  	}
      }

 

 mawa_dosomething_nested()中,如果该函数的第一部分失效,那么类似的错误初始化会出现。然而,如果第一部分正常工作的话,这个函数会继续下去并调用do_something_simple()。因为该函数能够设置一个错误条件,使发送给最初调用者的错误条件有意义。为了做到这一点,这个函数首先收集do_something_simple()条件到simple_error,然后调用g_propagate_error()从simple_error到error传递GError结构体。
      警告:
不能将作为参数得到的GError **作为指针传递给任何其他函数。如果GError **为NULL,当你试图引用它时,你的程序将会崩溃。
 
      如果要发送从别的函数得到的错误到一些其他地方,使用g_propagate_error(error_dest, error_src)在这里,error_dest是GError **的错误的目的地(destination),error_src是GError **的源(source)。如果目的地(destination)不为NULL,该函数会简单地将源(source)复制给目的地(destination)。然而,如果目的地(destination)实际为NULL,该函数会释放源错误(source error)。
      你现在可能已经注意到,GError力图通过对NULL的重视实现透明度,所以当你不关心错误的特定性质时,你不必担心内存泄露和额外的GError指针。另外,如果你的函数中的一个遇见作为错误条件的NULL,你可以将“用户不希望任何特定的错误处理,也许是想该函数可以尽可能的修补问题”作为一个提示。
      你需要时刻记住,仅当你坚持规则时,GError是一种相当可行的处理异常的工具

1.4.7 调试功能

 

    GLib有几个东西让你在你的程序中找到bug。其中有两个宏定义是返回普通的声明。除了中断退出程序以外,能还记录在G_LOG_LEVEL_CRITICAL中的消息。因此你可以在你程序有可能出现问题的地方使用他们:
    g_return_if_reached() 记录一个重要的信息,用于没有返回值的函数。
    g_return_val_if_reached(val) 记录一个重要的信息,用于有返回值的函数,返回val。
 
    其他两个类似也很方便的宏:
    g_return_if_fail(test)
    g_return_val_if_fail(test, val)
    如果test失败,记录消息在日志里面并返回。你可以在GNOME的函数里看到在开始的地方用来检查参数的有效性。
 
    在基本的调试上有两个以上的宏被展开成——断言。断言是在一定的某些条件下必须保存程序中进程的任何状态:
    g_assert() 如果程序的返回参数是FALSE时候,跟g_error()一样暂停程序。 
 
    g_assert_not_reached()不需要参数,只是简单的停止有错误消息的程序。
 
    你会发现整本书和绝大部分的GNOME应用中使用断言。此外,绝大部分的GNOME平台库使用这些安全功能,防止不适当的参数被传入。
    如果你的程序不是很长,你可以禁用任何断言,你能设置编译参数G_DISABLE_ASSERT这个宏就行了(例子:-DG_DISABLE_ASSERT)。禁用所有的断言可以节约一小点处理器的时间,因为消除了测试(消除测试。。窘。。我也不太懂。。。反正就是编译的时候,省略了一些东西,提高了速度)。
 

 

1.4.6 消息记录
    为了在程序运行的时候能诊断错误,GLib在系统控制台提供了几个有用的小工具。下面是他们按照优先级来描述:(PS:优先级从低到高)
    g_message() 表示程序在正常运行时候的行为提示。
 
    g_warning() 不会导致程序错误的行为信息(至少调用它的时候没有错误)(PS:程序员只关系error不关心warning)
 
    g_critical() 这个警告可能会导致错误。
 
    g_error() 对于程序来说最严重的错误,调用它会立刻终止程序。
 
    这些函数的参数和printf()的很像,都是一个格式化字符串和一个替代格式的参数列表。虽然你不必在每一个信息的最后都用换行符结束,但是为了确保你和你程序的用户知道这些信息是从你的程序发出的以区别其他程序,在编译的时候你就必须设置G_LOG_DOMAIN这个宏。用一个简单的字符串来定义你的应用软件或者是库。大部分的GNOME基本程序都是在编译选项的时候定义G_LOG_DOMAIN的。具体这样做: -DG_LOG_DOMAIN=\"名字\"。
    下面这个程序演示了这四个函数:
 
    /* messagedemo.c -- show logging features */

    #include <glib.h>

    #define NR 42

    int main(int argc, char **argv)
    {
            g_message("Coffee preparation engaged");
            g_warning("Bean canister #%d empty", NR);
            g_critical("Water flow failure");
            g_error("Heating element incinerated");

            /* this program shouldn't reach this point */
            return 0;
    }
 
    (PS:这个输出是我改过的)
gcc `pkg-config --libs --cflags glib-2.0` -DG_LOG_DOMAIN=\"yu\" message.c -o message
    这个程序的输出看来想这样:
    yu-Message: Coffee preparation engaged
 
    (process:3976): yu-WARNING **: Bean canister #42 empty
 
    (process:3976): yu-CRITICAL **: Water flow failure
 
    yu-ERROR **: Heating element incinerated
    aborting...
    Aborted
    (PS:说明修改生效了)
 
    为致命的错误标记错误等级
    g_error()函数调用会在之前的程序中产生一个错误的信息。告诉你这个程序被终止了(也可能是core dump[1])。你还可以配置致命错误在其他日志里面的优先级是他们表现的一样。例如:
    [code]
    g_log_set_always_fatal(G_LOG_LEVEL_WARNING|G_LOG_LEVEL_CRITICAL)
    [/code]
 
    这个函数的参数是一个掩码,用来设置这些警告和严重消息的行为。你可以用下列常量按位进行或运算:
     G_LOG_LEVEL_CRITICAL
 
    G_LOG_LEVEL_WARNING
 
    G_LOG_LEVEL_MESSAGE
 
    G_LOG_LEVEL_INFO
 
    G_LOG_LEVEL_DEBUG
 
注意:如果应用程序是用的GTK+或者是GNOME的库,你可以用命令行的选项 --g-fatal-warnings 来改变所有的警告等级。
 
    
    自由消息
 
    如果你有一条消息是不适合用预格式化的日志函数来输出,那你可以将它用g_print(), g_printerr()输出。g_print()输出到标准输出端(stdout),g_printerr()输出到标准错误端(stderr)。
    注意:不同于日志消息函数(g_message())。函数g_print()和g_printerr()需要自己去指定消息中换行符的位置。
    
    你可能会奇怪,为什么不用函数fprintf()来完成这个工作。不管你信不信(ps:反正我是信了),这个函数可能在Windows系统上无法正常工作。另外一个原因是英文,你可以定义自己的消息处理方式和可以改变的日志消息。并可以把他们输出到你想输出的任何地方(如一个窗口或者日志文件)。下面是一个例子:
 
    
    /* printhandlerdemo.c */

    #include <stdio.h>
    #include <glib.h>

    #define N 1

    /* print messages in ALL CAPS */
    void my_printerr_handler(gchar *string)
    {
        GString *msg;

        msg = g_string_new(string);
        msg = g_string_ascii_up(msg);
        fprintf(stderr, "%s\n", msg->str);
        g_string_free(msg, TRUE);
    }

    int main(int argc, char **argv)
    {
        /* print to stdout */
        g_print("If you lie %d time, no one believes you.\n", N);

        /* print to stderr */
        g_printerr("Ouch.\n");

        /* but if you lie all of the time... */
        g_set_printerr_handler((GPrintFunc)my_printerr_handler);
        g_printerr("%d. Ouch. Ouch. Ouch. (Hey, that really hurts.)", N);

        return 0;
    }
 
 
 
    你可以在后面1.5.1的小节里面看到函数my_printerr_handler()是怎么工作的。下面是这个程序的输出:
    If you lie 1 time, no one believes you.
    Ouch.
    1. OUCH. OUCH. OUCH. (HEY, THAT REALLY HURTS.)
 
    正如你看到的那样,你可以用g_set_print_handler() 和 g_set_printerr_handler()这两个函数来设置你的打印函数。他们唯一的参数是一个GPrintFunc函数。定义如下:
typedef void (*GPrintFunc) (const gchar *string);
    所以,你的回调函数必须定义为返回值是void而且仅有一个字符串参数的函数。
    当你要构建自己的错误消息要用到两个或者两个以上的函数时候:g_sterror()和g_strsignal()。strerror()和strsignal()都是独立于平台的实现。g_strerror()函数接受一个如EBADF或EINVAL错误代码,并将其转换到一个更易于理解的消息“错误的文件描述符”或“无效的参数。”同样,g_strsignal()返回的一个信号的名称时,给出一个数字信号代码。
    相对于strerror()和strsignal()来说它们的优势不是独立于不同平台,而是创造了一个UTF-8的输入输出库。例如:GTK+
 
 
 
 
 
 
 
 
 
 
    [1]什么是Core Dump?
    Core的意思是内存, Dump的意思是扔出来, 堆出来.开发和使用Unix程序时, 有时程序莫名其妙的down了, 却没有任何的提示(有时候会提示core dumped). 这时候可以查看一下有没有形如core.进程号的文件生成, 这个文件便是操作系统把程序down掉时的内存内容扔出来生成的, 它可以做为调试程序的参考.core dump又叫核心转储, 当程序运行过程中发生异常, 程序异常退出时, 由操作系统把程序当前的内存状况存储在一个core文件中, 叫core dump.
 
 
 

 

1.4.5 定时器
 
只要你系统上的时钟够准确,Gtimer的精度可以超过了秒表。
下面是一个例子:
 
/* gtimerdemo.c -- demonstration of GTimer */


#include <glib.h>
#define DURATION 200000

int main(int argc, char **argv)
{
        GTimer *clock = NULL;
        gint i;
        gdouble elapsed_time;
        gulong us;  /* microseconds */

        clock = g_timer_new();
        g_timer_start(clock);
        g_print("Timer started.\n");

        g_print("Loop started.. ");
        for (i = 0; i < DURATION; i++) { ; }
        /* wasting CPU time like this is only allowed in programming examples */

        g_print("and finished.\n");
        g_timer_stop(clock);
        g_print("Timer stopped.\n");

        elapsed_time = g_timer_elapsed(clock, &us);
        g_print("Elapsed: %g s\n", elapsed_time);
        g_print("         %ld us\n", us);

        g_timer_destroy(clock);

        return 0;
}
 
    你需要了解的Gtimer都在这个小例子里面。clock变量是一个Gtimer数据结构的指针,一开始我们设置为NULL。为了创建Gtimer这个结构,我们调用g_timer_new()函数,返回值就是我们要的clock指针。(一个Gtimer结构的新指针)。
    我们用函数g_timer_start()来启动我们刚刚生成的时钟。事实上这个程序在运行着一个循环,除此之外什么也没有做,就是浪费cpu的时间而已。[1]。之后,我们用函数g_timer_stop()来停止及时。
    当我要获得此刻计数器的计数结果的时候,调用:
   
   time = g_timer_elapsed(timer, us_ptr);
 
    它的返回值是经过时间的秒数,并且是一个双精度的浮点数。此外,如果你需要获得微妙级的小数部分,你可以传入一个gulong变量的指针作为us_ptr。因为这个数字没有包括完整的秒数树脂,所以第二部分的积分要乘以100万来进行类型转换。如果你想要一个微秒的总数的话。(ps:后面这句翻译的@#¥#@¥@!#¥)
 
    注意:
如果你不关心到微秒,us_ptr可以设置为NULL。
 
    最后,你可以用函数g_timer_reset(timer)重新设置定时器。不用定时器的时候,你需要销毁它g_timer_destroy(timer)。
 
[1] 如果在编译这个程序的时候没有打开编译优化,这个循环就会被执行。

其实之前两篇都是在csdn一个博客上搞过来的,感谢那兄弟。以后我来接着翻译把 。。争取一年可以翻译完。。我的英文很菜,翻译的不好,轻喷。。。。。

 

 

1.4.4 Unicode和字符编码
    C标准的字符串和上一节的字符串。在这些函数里面都不用担心长度的问题,因为每一个C标准的字符和gchar字符都是一个字节长。(PS:gchar只是char的一个define)
    在本节中的函数跟上一节的有所不同,因为他们都是针对Unicode的字符和字符串。Unicode是一个广泛统一的字符集,可以表示地球上任何语言中的任何字符。Unicode可以用作通用字符集。(UCS;see ISO 10646)Unicode原本只有16bit的编码。经过不断的扩张。我们GLib可以支持三种不同的编码方案。(详见 man utf-8)
    UCS-4是一个完整的32bit UCS兼容编码。(其实就是UTF-32,我就不翻译了直接贴wiki)
      UTF-32 (或 UCS-4)是一種將Unicode字符編碼的協定,對每一個Unicode碼位使用恰好32位元。其它的Unicode transformation formats則使用不定長度編碼。
    因為UTF-32對每個字符都使用4位元組,就空間而言,是非常沒有效率的。特別地,非基本多文種平面的字符在大部分文件中通常很罕見,以致於它們通常被認為不存在佔用空間大小的討論,使得UTF-32通常會是其它編碼的二到四倍。
    雖然每一個碼位使用固定長定的位元組看似方便,它並不如其它Unicode編碼使用得廣泛。與UTF-8及UTF-16相比,它有點更容易遭截斷。即使使用了"定寬"字型,除非在一些非常有限的清況下,否則它並不會使得計算顯示一個字串的寬度更加容易。主要原因是,存在著一個字符位置會有多於一種可能的碼點(結合字符)或一個碼點用多於一個字符位置(如CJK表意字符)。結合符號也意味著,文書編輯者不能不能將一個碼點視同一個編輯上的單位。
    
   (不过后面他注解了一些。我还是翻译吧)每一个UCS-4字符有4个字节长。用两个低位字节来表示Unicode(一般情况下高位的字节就为0).在GLib的数据类型UCS-4是gunichar。一个gunichar是标准的Unicode类型有32bit宽。在glib中某些函数用UCS-4字符串(gunichar *)。
    UTF-16是原生编码().每一个UTF-16字符有2个字节长。GLib使用gunichar2类型来表示UTF-16字符。跟UCS-4一样,你可以看到UTF-16的字符串是(gunichar2 *)。
    UTF-8是在实践中最重要的。因为UTF-8跟ASCII是兼容的。普通的ASCII有8bit长。但是其他的字符可能有2个字符长或者更长。所有文件里面ascii编码的格式都包含在utf-8的有效区间里面,但不能反过来。UTF-8因为在文件里面存储是不均匀的,如果随机读取就不可能得到一个完整的某一部分,每次都必须从头开始遍历字符,这是一个很明显的缺点。因为UTF-8没有一个同意的大小,GLib里面没有一个数据类型来特殊的表示UTF-8。但是,你可以用正常的gchar字符和字符串来表示(gchar *)UTF-8.
    GLib中普遍使用Unicode标准的32位UCS- 4 gunichar类型。
    以下函数针对单个的Unicode字符:
    gboolean g_unichar_validate(gunichar c)
    如果字符c是Unicode,返回真。
    gboolean g_unichar_isdefined(gunichar c) 
    如果字符c是Unicode标准分配的,返回真。
    gboolean g_unichar_isalnum(gunichar c)
    如果字符c是字母或者数字,返回真。
    gboolean g_unichar_islower(gunichar c)
    如果字符c是小写字母,返回真。
    gboolean g_unichar_isupper(gunichar c) 
    如果字符c是大写字母,返回真。
    gboolean g_unichar_istitle(gunichar c)
    如果字符c是标题字符(titlecase?)返回真。(ps:啥叫标题字符 贴devhelp解释来看,就不翻译了)
 
    Determines if a character is titlecase. Some characters in Unicode which are composites, such as the DZ digraph have three case variants instead of just two. The titlecase form is used at the beginning of a word where only the first letter is capitalized. The titlecase form of the DZ digraph is U+01F2 LATIN CAPITAL LETTTER D WITH SMALL LETTER Z.
 
    注意
    Titlecase doesn't appear much in English (or many other languages, for that matter). A titlecase letter is usually some sort of composite character or ligature where the first part of the composite goes to uppercase when the letter is capitalized at the start of a word. An example is the Lj in the Croatian word Ljubija.(估计用不上不翻译了)
    gboolean g_unichar_isalpha(gunichar c)
    如果字符c是一个字母,返回真。
    gboolean g_unichar_isdigit(gunichar c)
    如果字符c是一个十进制字符,返回真。
    gboolean g_unichar_isxdigit(gunichar c)
    如果字符c是一个十六进制字符,返回真。
    gboolean g_unichar_ispunct(gunichar c)
    如果字符c是某种符号或标点符号,返回真。
    gboolean g_unichar_isspace(gunichar c)
    如果字符c是一个空白的形式,包括空格,制表符和换行符。返回真。
    gboolean g_unichar_iswide(gunichar c)
    如果字符c通常需要两倍的一个正常的字符在屏幕上绘制的空间。返回真。
    gboolean g_unichar_iscntrl(gunichar C)
    如果字符c是一个Unicode控制字符。返回真。
    gboolean g_unichar_isgraph(gunichar C)
    如果你可以打印字符c,就是说如果它不是一个控制字符,格式字符,或空间。返回真。
    gboolean g_unichar_isprint(gunichar c) 
    这个函数跟g_unichar_isgraph()很像,除了空格是返回的真。
 
    如果你有一个gunichar的字符,而你想知道这个字符在Unicode是怎么分类的。你可以用:
    g_unichar_type(c)
    这个函数返回的一个常量(跟多信息在[TUC])
    G_UNICODE_LOWERCASE_LETTER:小写字母
 
    G_UNICODE_UPPERCASE_LETTER:大写字母
 
    G_UNICODE_TITLECASE_LETTER:标题字符信
 
    G_UNICODE_CONTROL:Unicode控制字符
 
    G_UNICODE_FORMAT:使用Unicode格式字符
 
    G_UNICODE_MODIFIER_LETTER:修饰符(看起来很奇怪的字母发音修改)
 
    G_UNICODE_SURROGATE:两个16位表示一个字符的Unicode字符的复合
 
    G_UNICODE_UNASSIGNED:目前未分配的字符
 
    G_UNICODE_PRIVATE_USE:保留给私人,内部使用的字符
 
    G_UNICODE_OTHER_LETTER:杂信
 
    G_UNICODE_COMBINING_MARK:马克可能与另一个字母组合
 
    G_UNICODE_ENCLOSING_MARK:马克,其中包含的另一封信
 
    G_UNICODE_NON_SPACING_MARK:马克,通常需要没有空间来打印;上的立场取决于另一个基本字符
 
    G_UNICODE_DECIMAL_NUMBER:数字
 
    G_UNICODE_DECIMAL_LETTER_NUMBER:一个字母的数字
 
    G_UNICODE_OTHER_NUMBER:任何其他的数字
 
    G_UNICODE_CONNECTION_PUNCTUATION:绑定的标点符号
 
    Dashlike G_UNICODE_DASH_PUNCTUATION:标点符号
 
    G_UNICODE_OPEN_PUNCTUATION:开幕标点符号(如一个左括号)
 
    G_UNICODE_CLOSE_PUNCTUATION:结束标点符号
 
    G_UNICODE_INITIAL_PUNCTUATION:标点符号
 
    G_UNICODE_FINAL_PUNCTUATION:终端标点符号
 
    G_UNICODE_OTHER_PUNCTUATION:任何其他标点符号
 
    G_UNICODE_CURRENCY_SYMBOL:货币货币符号
 
    G_UNICODE_MODIFIER_SYMBOL:修饰符号(例如,一个口音)
 
    G_UNICODE_MATH_SYMBOL:数学符号
 
    G_UNICODE_OTHER_SYMBOL:任何其他奇怪的符号
 
    G_UNICODE_LINE_SEPARATOR:换行符(例如,一个换行符)
 
    G_UNICODE_PARAGRAPH_SEPARATOR:划分段落
 
    G_UNICODE_SPACE_SEPARATOR:一个空的空间
 
    下面是转换成单个gunichar字符的函数:
    gunichar g_unichar_toupper(gunichar c)
    返回字符c的大写字母,如果c已经是大写了,就不改变。
    gunichar g_unichar_tolower(gunichar c)
    返回c的小写字母。
    gunichar g_unichar_totitle(gunichar c)
    返回c的titlecase。
    gint g_unichar_digit_value(gunichar c)
    如果这个c是数字返回gint的数字,如果不是数字则返回-1.
    gint g_unichar_xdigit_value(gunichar c)
    跟上一个一样,不过返回的是十六进制。
 
    现在你已经知道关于gunichar的很多有趣的事情,你可以会想知道你现在能对这种类型的能做什么事情了。大多数情况下,你必须冲UTF-8字符串中把Unicode字符给提取出来,在提取的过程没读取一个字符,你可能要对字符进行一定的有效性验证。
    注意:你看到的函数里面,你可能会有一个带NULL结尾的字符串,但是这并不是一直必要的。如果一个函数需要gssize这种类型的参数,可以指定在UTF -8字符串函数应该处理的字节数。如果你要告诉一个函数来处理整个NULL结尾的字符串的大小就使用-1。(ps:窘,翻译的乱七八糟)
 
 
(1.4.4太长了 翻译起来 太有挫败感了。一下午就到这里。。。我现弄着后面比较上的把。。。窘。。。。而且感觉用的机会也不多)
 
 
 
 
 
 
 
 
 
    

继续阅读

 

 

1.4 Basic Utilities

(基本函数,这个Utilities不知道如何译,就写成函数吧,因为后面确实在讲函数,嘿嘿……)

为了简化你的程序与C语言以及系统的交互,GLib提供了大量的函数。 要了解GLib的函数处理数据结构部分,请看1.5节。

 

1.4.1 内存管理

如果你使用GLib提供的内存管理例程,它可以避免你处理很多让你头痛的事。 GLib提供了的附加的错误检查与侦测。在下面的表格中为C程序员提供了一个参考。

你可以使用g_malloc(),g_realloc(),g_free()来代替malloc(),realloc(),free(),它们提供了相同的处理方式。为了将申请的内存在使用前清零,你可以使用g_malloc0()。 注意,它的语法看起来像malloc,而不是calloc()。

 

GLib Function

Corresponding C Function

gpointer g_malloc(gulong n_bytes)

void *malloc(size_t size) with error handling

gpointer g_malloc0(gulong n_bytes)

like malloc(), but initializes memory as in calloc()

gpointer g_try_malloc(gulong n_bytes)

like malloc() without error checking

gpointer g_realloc(gpointer mem, gulong n_bytes)

void *realloc(void *ptr, size_t size) with error checking

gpointer g_try_realloc(gpointer mem, gulong n_bytes)

realloc() without error checking

void g_free(gpointer mem)

void free(void *ptr)

 

 

 

注意: 如果你有一些特殊的原因要使用函数的返回值的话,你可以使用g_try_malloc()和g_try_realloc(),如果出错的时候,它们会返回NULL。 你可以在某些不是非常关键的地方使用(如某些用来提高性能的额外缓冲区),或者某些测试的时候。

自然,如果你想覆盖GLib的保护机制,你必须要清楚你在做什么。 对大多数程序来说,这些像g_malloc()的普通函数能节约你大量的代码、错误、以及时间。

一般情况下,你没有必要为malloc或g_malloc指定具体的要申请的块的大小。一般都是使用sizeof()来告诉编译器或运行时系统申请某种类型的某个倍数。 为了与数据类型相符,你必须要将malloc()返回值进行强制转换。 强制转换要用大量的括号和星号,所以GLib提供了一些宏,如g_new(),g_new0()以及g_renew()。 下面是一些示例代码。

 

 

 

typedef struct _footype footype;
footype *my_data;
/* Allocate space for three footype structures (long version) */
my_data = (footype *) g_malloc(sizeof(footype)*3);
/* The abbreviated version using g_new */
my_data = g_new(footype, 3);
/* To initialize the memory to 0, use g_new0 */
my_data = g_new0(footype, 3);
/* Expand this block of memory to four structures (long version) */
my_data = (footype *) g_realloc(my_data, sizeof(footype)*4);
/* Shorter version */
my_data = g_renew(my_data, 4);

 

在上面的代码段中你可以清楚地看出,g_new()是g_malloc()的简化,g_renew()是g_realloc()的简易形式,g_new0()是g_malloc0()的简易形式。

警告:记住你在使用g_new()时需要一个类型,就像使用sizeof()一样。 有时候像这样的的语句会产生编译错误:

b = g_new(a, 1) (a只是一个变量,而不是类型)

产生错误的原因就是因为g_new只是一个宏,应该给它传递类型,而不是某个类型的变量。

 

内存块

 

GUI程序一般倾向于重复申请大小相同的内存块(原子)。而且,那儿有一些相关的原子内存(atom)。GLib使用了一种称为“memory chunks”的方法为程序提供了相应的原子内存。一个内存块由一些原子内存组成的;所以块的大小肯定是原子内存大小的整数倍。

这儿有一个使用g_mem_chunk_new()来申请块的例子: 

 

GMemChunk my_chunk;
my_chunk = g_mem_chunk_new("My Chunk",         /* name */
                           42,                 /* atom size */
                           42*16,              /* block size */
                           G_ALLOC_AND_FREE);  /* access mode */

g_mem_chunk_new()有四个参数,第一个参数是内存块的名字,第二个参数是原子内存的大小(在这里是42),第三个参数是块的总共大小,最后一个参数是访问模式。这个函数的返回值是指向GMemChunk的指针。

注意: GMemChunk并不是一个数据结构。它是一个内存管理系统,它管理的内存片断里面含有数据结构。

第四个参数“访问模式”为你提供了如何创建和申请原子内存。一共有两种方式:

G_ALLOCC_AND_FREE允许内存池在任何时间返回单个的原子内存。

G_ALLOC_ONLY仅允许在处理整个内存块的时候申请原子内存。使用这个模式要比使用G_ALLOC_AND_FREE高效。

下面的例子指示了如何申请或释放原子内存。

 

gchar *data[50000];
gint i;
/* allocate 40,000 atoms */
for(i = 0; i < 40000; i++)
{
  data[i] = g_mem_chunk_alloc(my_chunk);
}
/* allocate 10,000 more atoms and initialize them */
for(i = 40000; i < 50000; i++)
{
  data[i] = g_mem_chunk_alloc0(my_chunk);
}
/* free one atom */
g_mem_chunk_free(my_chunk, data[42]);

 

在这里,g_mem_chunk_alloc()和g_mem_chunk_alloc0()都可以申请单独的原子内存。这两个函数像g_malloc()和g_malloc0()一样,返回一个指向原子内存的指针,但是,他们使用GMemChunk结构而不是大小作为参数。g_mem_chunk_free()函数使用指向单独原子内存的指针来做为参数,它将释放的内存返回到内存池中。

警告: 使用g_mem_chunk_free来释放原子内存的时候,该原子内存所在的内存块必须是使用G_ALLOC_AND_FREE模式创建的。除此之外,使用g_free()来释放原子内存的时候将会引起一个段错误。产生段错误的原因是因为内存块的释放函数将会导致两次调用free()。

 

有一些函数会一次性的在整个内存块上操作原子内存。下面的例子展示了这些函数的用法。

 

/* free up any unused atoms */
g_mem_chunk_clean(my_chunk);
/* free all unused atoms in all memory chunks */
g_blow_chunks();
/* deallocate all atoms in a chunk */
g_mem_chunk_reset(my_chunk);
/* deallocate a memory chunk */
g_mem_chunk_destroy(my_chunk);

 

g_mem_chunk_clean(chunk)会检查chunk并且释放那些不再使用的内存。这个例程给你提供了一些手工管理基本内存的方式。g_mem_chunk_free()函数并不必须立即释放原子内存。只有在方便或者必须的时候,GLib才会释放它。g_mem_chunk_clean()强迫它立即释放。

g_blow_chunks()会在程序中所有的outstanding内存块中运行g_mem_chunk_clean()。

g_mem_chunk_reset(chunk)会释放chunk中的所有原子内存,包括那些在使用的。你要小心使用这个函数,因为他可能会释放掉仍要使用的原子内存。

g_mem_chunk_destroy(chunk)会释放包括chunk本身以及chunk中的所有原子内存。

 

在内存管理上面,GLib为你提供了一些宏来减少你的输入。

 

typedef struct _footype footype;
GMemChunk *pile_of_mem;
footype *foo;
/* create a memory chunk with space for 128 footype atoms */
pile_of_mem = g_mem_chunk_new("A pile of memory",
                             sizeof(footype),
                             sizeof(footype)*128,
                             G_ALLOC_AND_FREE);
/* the same thing, with g_mem_chunk_create */
/* the name will be "footype mem chunks (128)" */
pile_of_mem = g_mem_chunk_create(footype, 128, G_ALLOC_AND_FREE);
/* allocate an atom */
foo = (footype *) g_mem_chunk_alloc(pile_of_mem);
/* the same thing, with g_mem_chunk_new */
foo = g_mem_chunk_new(footype, pile_of_mem);
/* the same thing, but zero out the memory */
foo = g_mem_chunk_new0(footype, pile_of_mem);

 

从上面这些代码中,可以很容易的明白这些宏的意图。注意,如果你知道原子内存的类型的话,g_mem_chunk_create()是一个比g_mem_chunk_new()更简单的方法。还有,每个宏都会自动地将块名字拼凑起来。 g_mem_chunk_new()和g_mem_chunk_new0()是与g_new()和g_new0()相对应的用来操作内存块的函数。

如果你想知道当前内存块的统计数字,使用g_mem_chunk_print(chunk)可以得到一个简单的报告。 使用g_mem_chunk_info()可以得到所有内存块的详细信息。

 

 

 

1.4.2 Quarks (夸克)

为了在程序中标识一块数据,你一般有两种方式可选:数字或字符串。但是这两者都有一些缺点。数字是非常难以辨认的。如果你开始粗略的知道需要多少标签,你就可以定义一个枚举类型和一些字符符号。但是,你没法在运行的时候动态添加标签。

另一方面,你可以在运行的时候动态的添加或修改字符串,而且它们是很容易理解的。 但是,字符串比较要比数字比较花更长的时间,而且在内存中管理字符串有一些你可能不愿意处理的额外麻烦。

 

GLib提供了GQuark类型,它整合了数字的简单和字符串的易用。在它内部,它就是一个易于比较和复制的整形数。GLib将这些数字映射为你的字符串,并且你可以在任何时间取得字符串所对应的值。

要创建一个quark,使用下面两个函数之一:

 

GQuark quark;
gchar *string;
quark = g_quark_from_string(string);
quark = g_quark_from_static_string("string");

 

这两个函数都是以字符串作为它们唯一的参数。它们的区别是g_quark_from_string()会在映射的时候创建一个字符串的拷贝,但是g_quark_from_static_string()并不会。

警告:小心使用g_quark_from_static_string()。在每次执行它的时候会节约很少的CPU和内存,但是,在你的程序中增加了附加的依赖性可能会导致你的程序有一些bug,所以,或许节约的那一点开销并不值得你去使用该函数。

 

如果你想检验某个字符串是否有一个quark值,调用:

g_quark_try_string(string)

这个函数的返回值是该字符串所对应的quark值。如果返回0的话,说明没有与这个字符串相对应的quark值。

从quark恢复到string使用:

string = g_quark_to_string(quark);

如果它执行成功,它将会返回quark对应的字符串的指针。但是你不能在这个指针上调用free(),因为这个字符串并不是一个拷贝。

下面是一个简短的示例代码:

 

GQuark *my_quark = 0;
my_quark = g_quark_from_string("Chevre");
if (!g_quark_try("Cottage Cheese"))
{
  g_print("There isn't any quark for /"Cottage Cheese/"/n");
}
g_print("my_quark is a representation of %s/n", g_quark_to_string(my_quark));

 

注意:GQuark值是分配给字符串的数字,并且很容易测试它们的等同性。然而,它们并没有数字顺序。你不能用quark值来进行字母顺序测试。因此,你不能用它们作为排序关键字。如果你想比较quark所代表的字符串,你必须先用g_quark_to_string()来提取相应的字符串,然后才可以使用strcmp()或g_ascii_strcasecmp()。

 

 

 

1.4.3 C字符串

GLib提供了一些字符串函数来与标准C库进行交互(不要对GString疑惑,在1.5.1节将会讲到)。你可以用这些字符串函数来扩充或代替stringf()、strdup()或strstr()等。

下面的这些函数会返回一个新的字符串缓冲区的指针,所以你在使用完后必须要释放它们。

gchar *g_strdup(const gchar *str)

复制str并返回它的一个拷贝。

gchar *g_strndup(const gchar *str, gsize n)

返回str中前n个字符的一个拷贝。这个拷贝总会在最后附加一个NULL结束符。

gchar *strnfill(gsize length, gchar *fill_char)

创建一个长度为length并被fill_char填充的字符串。

gchar *g_strdup_printf(const gchar *format, ...)

像sprintf()一样格式化字符串和参数。但是,你没必要像sprintf()一样创建和指定一个缓冲区,GLib将这些自动做了。

gchar *g_strdup_vprintf(const gchar *format, va_list args)

类似于上面的那个函数,它跟vsprintf()类似,使用C的可变参数能力,有关可变参数可以在stdarg(3)的手册中找到。

gchar *g_strescape(const gchar *source, const gchar *exception)

将一些特殊控制字符转换成相应的ASCII,如,将Tab转成/t,这些转换有:退格(/b)、换页(/f)、换行(/n)、回车(/r)、反斜线(/变成//),双引号(" 变成 /")。任何附加的非ASCII字符会转换成相应的8进制表示(例如escape会变成/27)。你可以在字符串的exceptions中指定任何特定的例外。

gchar *g_strcompress(const gchar *source)

与g_strescape()相反,它是将ASCII格式的字符串转为真正的控制字符。

gchar *g_strconcat(const gchar *string1, ..., NULL)

它接受任意数量的string作为参数,并返回它们的连接后的字符串。你必须将NULL作为这个函数的最后一个参数。

gchar *g_strjoin(const gchar *separator, ..., NULL)

连接一些字符串,并且添加分隔符在每个字符串之间。如gstrjoin("|", "foo", "bar", NULL)会产生"foo|bar"。像g_strconcat(),你也必须将NULL作为最后一个参数传给这个函数。如果将separtor参数设为NULL的话,g_strjoin()就会等同于g_strconcat()了。

 

在下面的函数中,你应该为返回的结果申请空间。GLib并不会返回一个拷贝给你。它们与C相对应的函数非常像,参数要包含一个足够大的缓冲区来进行字符串处理。

gchar *g_stpcpy(gchar *dest, const gchar *src)

拷贝src到dest,包括最后的NULL字符。如果它执行成功,会返回dest中结束符拷贝的指针。它在进行高效的字符串连接时是非常有用的。

gint g_snprintf(gchar *string, gulong n, const gchar *format, ...)

像snprintf()一样,你必须确保string有足够大的空间。而且你必须要用n来指定这个缓冲区的大小。返回值是输出字符串的长度,也有可能这个输出字符串为了适应缓冲区的大小而被截断。这是C99的标准,并不只是你机子上传统C库的行为。

gint g_vsnprintf(gchar *string, gulong n, const gchar *format, va_list list)

跟上个函数类似,不过是变长参数。

gchar *g_strreverse(gchar *string)

将string里面的字符顺序反转。返回值仍然是string。

gchar *g_strchug(gchar *string)

将string开头的空白字符都移除。将string中相应的字符进行左移,返回string。

gchar *g_strchomp(gchar *string)

将string结尾的空格都删掉,返回string

gchar *g_strstrip(gchar *string)

将string开头的结尾的空白字符都删掉,返回string。

gchar *g_strdelimit(gchar *string, const gchar *delimiters, gchar *new_delimiter)

将string中的delimiters替换为new_delimiter。如果delimiters是NULL的话,这个函数会使用" _-|<>. "; 这些是G_STR_DELIMITERS中的标准集。返回值是string。

gchar *g_strcanon(gchar *string, const gchar *valid_chars, gchar *substituter)

将string中的,不属于valid_chars中字符的那些字符都替换为substituer。返回string。注意,这个函数是g_strdelimit的一个补充。

 

 

在下面的函数中,除了g_ascii_dtostr()之外,都不改变它们的参数。

gchar *g_strstr_len(const gchar *haystack, gssize haystack_len, const gchar *needle)

在haystack中遍历haystack_len长度,如果找到了needle字串,则返回这个位置的指针,如果没有找到则返回NULL。

gchar *g_strrstr(const gchar *haystack, const gchar *needle)

类似于上个函数,这个函数将会从后面开始查找,但是它并没有haystack_len参数。

gchar *g_strrstr_len(gchar *haystack, gssize haystack_len, gchar *needle)

与g_strrstr()相同,但是它只在前haystack_len个字符中查找。

gsize g_printf_string_upper_bound(const gchar *format, va_list args)

检查format和args,返回格式化后所需要缓冲区的最大值。

gdouble g_ascii_strtod(const gchar *nptr, gchar **endptr)

将string转为双字长度的浮点数。如果你提供了一个有效的endptr指针地址,这个函数会将指针设置到string中被转换的最后一个字符的位置。与strtod()的区别是这个函数忽略了C locale。

gchar *g_ascii_dtostr(gchar *buffer, gint buf_len, gdouble d)

将d转换为ASCII字串。将转换后的忽略C locale然后写入到buffer中,最大长度为buf_len。目标字串的长度永远不会超过G_ASCII_DTOSTR_BUF_SIZE。这个函数返回buffer的指针。

注意:使用g_ascii_strtod()和g_ascii_dtostr()来读写文件或数据流,并不都是人可读的。 因为这些函数使用统一的标准,是区域无关的格式,为了解决某些特定的问题。 例如,一些人将本地设为German,然后运行你的程序,你无须担心本地化的数字之间逗号和句点的意义转换。

 

 

最后,这儿列出一些操作字符串数组的函数。 NULL指针来表示这些数组的结束。

gchar **g_strsplit(const gchar *string, const gchar *delimiter, gint max_tokens)

使用delimiter来将string切割成至多max_tokens个部分。返回值是新申请的一个字符串数组,用来保存被切割的这些部分。这个字符串数组必须由你自己释放。 如果输入字符串是空的,这个返回值也是一个空的数组。

gchar *g_str_joinv(const gchar *separator, gchar **str_array)

将字符串数组组合成单个字符串,并将这个新申请的字符串返回。如果separator不空,g_str_joinv()会在每个字符串之间添加上一个separator分隔符。

gchar **g_strdupv(gchar **str_array)

返回str_array的一个完整拷贝。

void **g_strfreev(gchar **str_array)

释放str_array数组以及这些字符串。

警告: 除了g_strfreev()之外,不要使用其它的函数来释放像g_strsplit()或g_strdupv()创建的字符数组。

 

 

 

 

GLib

1.1 Introduction

在开源世界中,G中很常见的。 它代表了GNU ("GNU's Not Unix")。 像GTK+,GLib,GObject,以及GNOME,还有一些其它的软件包,如Ghostscript和gcc中都充满了G。

为了理解后面的章节,你必须学习一些GLib的基础知识(libglib-2.0)。它为GTK+和GNOME程序提供了基础的数据结构和实用函数。在本章中将会涉及到GLib的结构和API的介绍。 你将会在第二章中学习GLib's object system(GObject)。

在你使用GNOME和GTK+的时候,会不可避免的使用GLib。 其它的一些库,如ORBit除了GLib并没有引用其它的库。 GLib所提供的abstractions(这个不知道应该译成什么)和实用工具编程程序提供了方便, 而且很容易移植到其它平台。

在本章中并没有包含图形操作代码,它只是一个简单的,逐步讲述了GLib的函数和数据结构。 可能读起来有点枯燥,不过你也可以直接跳到第三章去阅读GTK+的内容。 但是你会经常的返回来查看这前两章的内容。

 

1.2 GLib Naming Coventions (GLib命名规则)

就像许多其它的库一下,GLib也为了一致性和易读性规范了命名规则。

函数的名字一般都是小写的,并且在每部分名字之间加下划线, 如g_timer_new(),g_list_append()。并且所有的函数名字都是以g_开头。

在GLib中,所有的函数都有前缀g_。

类型名并不包含下划线,并且GLib里面的所有类型组件都是以大写字母G开头的,如GTimer,GList。但GLib中的基本类型是值得注意的例外。在第三节中将会有介绍。

如果某个函数主要是操作某个特定的类型的话,这个函数的前缀就与相应的类型相匹配。例如,g_timer_*就是操作GTimer类型。 g_list_*就是操作GList类型。

这些规则听起来比实际要复杂。

 

 

 

1.3 Basic Types (基本类型)

在开始使用GLib之前,你首先要适应GLib的基本类型。 你或许很想知道为什么使用guchar要比使用unsigned char好。 如果你的程序一直待在同一个平台上执行,那么使用guchar与使用unsigned char并没有实质的差别。 但是,如果你想编写出在不同平台之间移植的程序,如Windows和Unix之间。 那你会很感谢GLib将基本类型给你抽象出来了。

例如,你想在所有可能的平台上定义16位的无符号整型,使用C语言的话可能看起来很麻烦。但幸运的是GLib帮你处理了这些。 所有的基本类型在下面的表格中列出。

要使用GLib和它的类型,必须要在源码中包含glib.h

#include <glib.h>

gpointer和gconstpointer类型在GLib的数据结构中经常出现,这两个是无类型的内存指针。在GLib中,函数负责检查这两个指针的类型,程序员和编译器并不管这些。这在回调函数以及排序和遍历中比较的时候尤其方便。

在GLib的头文件中为gboolean类型定义了TRUE和FALSE常量。 在使用这些常量的时候,不要使用比较运算符。例如要用:if (my_gboolean),而不是:if (my_gboolean == TRUE)。

 

 

 

GLib Type

Corresponding Type in C

gchar

char

guchar

unsigned char

gint

int

guint

unsigned int

gshort

short

gushort

unsigned short

glong

long

gulong

unsigned long

gfloat

float

gdouble

double

gint8

int, 8 bits wide

guint8

unsigned int, 8 bits wide

gint16

int, 16 bits wide

guint16

unsigned int, 16 bits wide

gint32

int, 32 bits wide

guint32

unsigned int, 32 bits wide

gint64

int, 64 bits wide

guint64

unsigned int, 64 bits wide

gpointer

void *, untyped pointer

gconstpointer

const void *, constant untyped pointer

gboolean

Boolean value, either TRUE or FALSE