工学1号馆

home

C语言包裹函数

By Wu Yudong on August 05, 2016

包裹函数其实就是封装函数,调用一个函数来实现这个功能,但是我们通常不在这个函数里面来定义它,只是调用,把一个函数做好封装后,以后到哪里都可以用这个函数,只要知道这个函数派什么用处,理解接口就可以了,不需要知道函数是怎么做的。其实是也可以有上锁机制在里面,具有排他性,不让别人来修改它。

本文地址:http://wuyudong.com/2016/08/05/2447.html,转载请注明源地址。

任何现实世界的程序都必须检查每个函数调用是否返回错误。例如在unix网络编程的时候,我们检查socket、inet_pton、connect、read和fputs函数是否返回错误,当发生错误时,就调用我们自己的err_quit或err_sys函数输出一个出错消息并终止程序的运行。我们发现绝大多数情况下这正是我们想做的事。个别情况下,当这些函数返回错误时,我们想做的事并非简单地终止程序的运行,我们必须检查系统调用是否被中断了。

既然发生错误时终止程序的运行是普遍的情况,我们可以通过定义包裹函数(wrapper function)来缩短程序。每个包裹函数完成实际的函数调用,检查返回值,并在发生错误时终止进程。我们约定包裹函数名是实际函数名的首字母大写形式。例如,在语句

sockfd = Socket(AF_INET, SOCK_STREAM, 0);

中,函数Socket是函数socket的包裹函数,如下代码所示:

/* include Socket */
int
Socket(int family, int type, int protocol)
{
    int        n;
    if ( (n = socket(family, type, protocol)) < 0)
        err_sys("socket error");
    return(n);
}

可以使用一种规定来约定包裹函数例如:将函数名第一个字母大写,它调用的实际函数的名字与包裹函数名相同,不过以对应的小写字母开头。

这些包裹函数不见得多节省代码量,但当我们在讨论线程时,将会发现线程函数遇到错误时并不设置标准Unix的errno变量,而是把errno的值作为函数返回值返回调用者。这意味着每次调用以pthread_开头的某个函数时,我们必须分配一个变量来存放函数返回值,以便在调用err_sys前把errno变量设置成该值。为避免引入花括号把代码弄得很混乱,我们可以使用C语言的逗号操作符,把errno的赋值与err_sys的调用组合成一条语句,如下所示:

int n;
if ((n = pthread_mutex_lock(&ndone_mutex)) != 0)
    errno = n, err_sys("pthread_mutex_lock error");

我们也可以为此定义一个新的错误处理函数,它取系统的错误号作为一个参数,不过通过定义下面的包裹函数,

/* include Pthread_mutex_lock */
void
Pthread_mutex_lock(pthread_mutex_t *mptr)
{
    int        n;
    if ( (n = pthread_mutex_lock(mptr)) == 0)
        return;
    errno = n;
    err_sys("pthread_mutex_lock error");
}
/* end Pthread_mutex_lock */

我们可以让以上这段代码更为易读:

Pthread_mutex_lock(&ndone_mutex);

要是仔细推敲C代码的编写,我们可以用宏来替代函数,从而稍微提高运行时效率,不过包裹函数很少是程序性能的瓶颈所在。

选择首字母大写一个函数名作为其包裹函数名是一种折中的方法。其他方法也考虑过,譬如给函数名加一个"e"前缀,给函数名加一个"_e"后缀,等等。这些方法都能明显地提示调用了其他函数,但我们前面约定的首字母大写,这种风格看来是最少分散注意力的。

这种技术还有助于检查那些错误返回值通常被忽略的函数是否出错,例如close和listen。

如果文章对您有帮助,欢迎点击下方按钮打赏作者

Comments

No comments yet.
To verify that you are human, please fill in "七"(required)