apue和unp的学习之旅05——包裹函数
? ? ?看到第5章就會發現,作者用了好多包裹函數,封裝原始的api,所以不可避免必須學習下unix里出錯處理方面的知識。
例如:
sockfd = Socket(AF_INET, ? SOCK_STRAM, ? 0);
函數Socket是函數api socket的包裹函數,實現如下:
int Socket(int ?famlily, ?int type, ?int protocol)
{
? ? ? ?int ? ? n;
? ? ? ?if( ?( n = socket(family, type, ?protocol) ?) ?< ?0 )
? ? ? ? ? ? ? err_sys("socket error"); ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?// 作者的自定義函數
}
而作者又自定義了一組err_sys或error_quit出錯包裹函數,原因是這樣可以只用一行C代碼寫出錯誤處理代碼過程:
if(出錯條件)
? ? ?err_sys(帶任意參數數目的printf格式字符串);
而不是使用如下的繁瑣的方式:
if(出錯條件)
{
? ? ?char buff[200];
? ? ?snprintf(buff, ?sizeof(buff), ?帶任意參數數目的printf格式字符串);
? ? ?perror(buff); ? ? ? ?//?perror( ) 用來將上一個函數發生錯誤的原因輸出到標準設備(stderr)。參數 s 所指的字符串會先打印出,后面再加上錯誤原因字符串。此錯誤原因依照全局變量errno 的值來決定要輸出的字符串
? ? ?exit(1);
}
下面貼出作者常用的自定義出錯包裹函數的代碼(在作者給本書附帶源碼里lib目錄里的error.c文件可見):
#include "unp.h"#include <stdarg.h> /* ANSI C header file */ #include <syslog.h> /* for syslog() */int daemon_proc; /* set nonzero by daemon_init() */static void err_doit(int, int, const char *, va_list);/* Nonfatal error related to system call* Print message and return */void err_ret(const char *fmt, ...) {va_list ap;va_start(ap, fmt);err_doit(1, LOG_INFO, fmt, ap);va_end(ap);return; }/* Fatal error related to system call* Print message and terminate */void err_sys(const char *fmt, ...) {va_list ap;va_start(ap, fmt);err_doit(1, LOG_ERR, fmt, ap);va_end(ap);exit(1); }/* Fatal error related to system call* Print message, dump core, and terminate */void err_dump(const char *fmt, ...) {va_list ap;va_start(ap, fmt);err_doit(1, LOG_ERR, fmt, ap);va_end(ap);abort(); /* dump core and terminate */exit(1); /* shouldn't get here */ }/* Nonfatal error unrelated to system call* Print message and return */void err_msg(const char *fmt, ...) {va_list ap;va_start(ap, fmt);err_doit(0, LOG_INFO, fmt, ap);va_end(ap);return; }/* Fatal error unrelated to system call* Print message and terminate */void err_quit(const char *fmt, ...) {va_list ap;va_start(ap, fmt);err_doit(0, LOG_ERR, fmt, ap);va_end(ap);exit(1); }/* Print message and return to caller* Caller specifies "errnoflag" and "level" */static void err_doit(int errnoflag, int level, const char *fmt, va_list ap) {int errno_save, n;char buf[MAXLINE + 1];errno_save = errno; /* value caller might want printed */ #ifdef HAVE_VSNPRINTFvsnprintf(buf, MAXLINE, fmt, ap); /* safe */ #elsevsprintf(buf, fmt, ap); /* not safe */ #endifn = strlen(buf);if (errnoflag)snprintf(buf + n, MAXLINE - n, ": %s", strerror(errno_save));strcat(buf, "\n");if (daemon_proc) {syslog(level, buf);} else {fflush(stdout); /* in case stdout and stderr are the same */fputs(buf, stderr);fflush(stderr);}return; }
? ? ?這些出錯函數只有err_msg是最不嚴重的錯誤,因為他沒有調用exit結束進程,或abort異常終止進程(和exit一樣,也會讓所有的流被關閉和沖洗),而只是return返回一下,沒讓進程終止。
? ? ?這么多函數都圍繞著error_doit 這個邏輯處理中心函數,其中第一個參數errnoflag把錯誤分為2種,和系統相關的錯誤標記為1,和系統無關的錯誤標記為0? ? ?第二個參數level,fatal(致命的)錯誤標記為LOG_ERR,unfatal(不致命的)錯誤標記為LOG_INFO
? ? ?要知道的是,這些包裹函數不見得多省代碼量,以后就會發現線程函數遇到錯誤時并不設置標準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");
如果忘記了C/C++逗號符的使用,可以聯想 ?for( ?; ?; i++, j++),或許就想起來了。
? ? ? ??
自習推敲C代碼的編寫,我們可以用宏來代替函數,從而稍微提高運行時效率,不過包裹函數很少是程序性能的瓶頸所在。使用包裹技術還有助于檢查哪些錯誤返回值通常被忽略的函數是否出錯,例如close和listen。
? ?更多作者寫的包裹函數見作者提供本書的代碼的lib目錄里的帶wrap開頭的文件名的文件。
總結
以上是生活随笔為你收集整理的apue和unp的学习之旅05——包裹函数的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: otg android 键盘,使用USB
- 下一篇: 一句话逗人开怀