2009年12月14日 星期一

Delegation in C - 在 C 語言當中實作委派方法 (2)

延續前一篇文章的想法 Delegation in C - 在 C 語言當中實作委派方法 (1)
像是
invoke_all_func(&list, "Hello World");
這樣的函式顯然不夠彈性
於是想到一個解決方法就是傳入一個 function pointer 給 invoke_all_func() 於是就變成像是下面這樣
invoke_all_func(&list, my_cb_func, "Hello World");
讓 invoke_all_func() 內部去 callback 這個傳入的 function pointer
再利用 <stdarg.h> 所提供相關的函式將傳入的參數取回
因為只有呼叫 invoke_all_func() 的地方才知道傳入的參數有哪些
所以負責將參數取來出也是相當合理的於是 my_cb_func() 的實作就會像下面這樣
static bool my_cb_func(func_t func, va_list ap)
{
    my_func_t my_func = (my_func_t) func;
    const char* value = va_arg(ap, const char*);
    return my_func(value);
}
其中的 my_func_t 是
typedef bool (*my_func_t)(const char*);
於是就可以定義不一樣的 my_func_t 跟 my_cb_func() 來使用同樣 event bubbling 的方法
達到程式碼可以重複使用的好處
詳細的程式碼實作放在 http://fd.idv.tw/tux/callback.tar.bz2
沒有版權也不負責任更不保證沒有長蟲~ :P

2009年12月13日 星期日

Delegation in C - 在 C 語言當中實作委派方法 (1)

原本的想法是想要能夠在 C 語言中實現 event bubbling
提供一些函式去註冊需要被 callback 的函式
然後在 event 發生時去呼叫所有註冊過的函式
於是就有了以下的程式碼

首先來看一段程式碼片段
int main(int argc, char* argv[])
{
    list_t* list = NULL;

    add_func(&list, funcA);
    add_func(&list, funcB);
    add_func(&list, funcC);

    invoke_all_func(&list, "Hello World");

    return 0;
}
這邊利用 add_func() 將 funcA, funcB, funcC 註冊到某個 double linked list 上面
然後呼叫 invoke_all_func() 去呼叫註冊好的 funcA, funcB, funcC
static bool funcA(const char* str)
{
    fprintf(stderr, "%s says %s\n", __FUNCTION__,  str);
    return true;
}

static bool funcB(const char* str)
{
    fprintf(stderr, "%s says %s\n", __FUNCTION__,  str);
    return true;
}

static bool funcC(const char* str)
{
    fprintf(stderr, "%s says %s\n", __FUNCTION__,  str);
    return true;
}
執行後的結果是
funcC says Hello World
funcB says Hello World
funcA says Hello World
這邊的設計上是越後面註冊的函式越先被呼叫到
如果把 funcC() 的回傳值改成 false 結果就會變成
funcC says Hello World
只要某個註冊過的函式先回傳了 false 接下去的函式就不必再去執行了
於是這兩個負責註冊跟呼叫的函式的 prototype 應該就長得像是下面這樣
bool add_func(list_t** list, bool (*func)(const char*));
bool invoke_all_func(list_t** list, const char* msg);
然後再去實作這些函式的剩餘部份,不過問題來了...
當 funcA, funcB, funcC 換成了
static bool funcA(int val)
{
    fprintf(stderr, "%s count %d\n", __FUNCTION__,  val);
    return true;
}

static bool funcB(int val)
{
    fprintf(stderr, "%s count %d\n", __FUNCTION__,  val);
    return true;
}

static bool funcC(int val)
{
    fprintf(stderr, "%s count %d\n", __FUNCTION__,  val);
    return true;
}
或者是換成了
static bool funcA(const char* msg, int val)
{
    fprintf(stderr, "%s says '%s' count: %d\n", __FUNCTION__, msg, val);
    return true;
}

static bool funcB(const char* msg, int val)
{
    fprintf(stderr, "%s says '%s' count: %d\n", __FUNCTION__, msg, val);
    return true;
}  

static bool funcC(const char* msg, int val)
{
    fprintf(stderr, "%s says '%s' count: %d\n", __FUNCTION__, msg, val);
    return true;

可是 event bubbling 的方法還是一模一樣的,這時候我們應該怎麼辦呢?
有一種最不用花腦袋最快速可以解決問題的辦法就是去複製貼上程式碼,然後將參數換一換,有幾種不同的函式就重複幾次同樣的工作

待續...

2009年12月6日 星期日

在 Linux 系統上面合併多個 PDF 檔案成為一個 PDF 檔案

今天臨時興起想要去複習一下 Advanced Linux Programming 這本相當不錯的電子書
於是就把網頁上面的 PDF 檔案都下載回電腦裡面
這時候突然覺得要這樣分別開啟多個 PDF 檔案實在是麻煩
於是在網路上面找了一些方法
第一個方法是利用 ImageMagick 這個套件裡面的 convert 指令
$ convert alp-toc.pdf alp-ch*.pdf alp-ap*.pdf alp-index.pdf alp.pdf
不過製作出來的 alp.pdf 品質感覺不是很好
第二個方法是用 ghostscript 的 gs 指令
$ gs -dNOPAUSE -sDEVICE=pdfwrite -sOUTPUTFILE=alp.pdf -dBATCH alp-toc.pdf alp-ch*.pdf alp-ap*.pdf alp-index.pdf
製作出來的 alp.pdf 品質不錯,不過花了不少時間
第三個方法是用 pdftk 這個套件當中的指令 pdftk
$ pdftk alp-toc.pdf alp-ch*.pdf alp-ap*.pdf alp-index.pdf cat output alp.pdf
這個方法比之前的方法都要來的快速~
另外有網友提到可以使用看看 pdfsam 因為有太多相依上的套件要額外安裝就沒有想要繼續試下去啦~ :P

2009年12月2日 星期三

GNU Build System 執行檔案的分類 {bin,sbin,libexec,pkglib,noinst,check,EXTRA}_PROGRAMS

關於 {bin,noinst,check,EXTRA}_PROGRAMS 在之前的文章已經介紹過了
剩下來的 {sbin,libexec,pkglib}_PROGRAMS 跟 bin_PROGRAMS 其實都是在 $make install 會安裝到預設的目錄底下
差別只在於安裝到哪個目錄底下,例如以下的 Makefile.am
bin_PROGRAMS = main1
sbin_PROGRAMS = main2
libexec_PROGRAMS = main3
pkglib_PROGRAMS = main4
noinst_PROGRAMS = main5
check_PROGRAMS = main6
EXTRA_PROGRAMS = main7

main1_SOURCES = main.c
main2_SOURCES = main.c
main3_SOURCES = main.c
main4_SOURCES = main.c
main5_SOURCES = main.c
main6_SOURCES = main.c
main7_SOURCES = main.c
如果沒有特別指定或改變 prefix 最後安裝的情況會像下面這樣
usr
`-- local
    |-- bin
    |   `-- main1
    |-- lib
    |   `-- hello
    |       `-- main4
    |-- libexec
    |   `-- main3
    `-- sbin
        `-- main2
main1 這個執行檔如預期會安裝到 bin 底下
main2 類似 main1 會安裝到 sbin 底下
另外比較不常見的 libexec_PROGRAMS 則會讓 main3 安裝在 libexec 底下
還有 pkglib_PROGRAMS 會讓 main4 安裝在 lib/hello 底下,其中的 hello 就是 configure.ac 裡面的 package name
configure.ac 如下:
AC_PREREQ([2.63])
AC_INIT([hello], [0.0], [fourdollars@gmail.com])
AC_CONFIG_SRCDIR([main.c])
AC_CONFIG_HEADERS([config.h])
AM_INIT_AUTOMAKE([-Wall -Werror foreign])

# Checks for programs.
AC_PROG_CC

# Checks for libraries.

# Checks for header files.

# Checks for typedefs, structures, and compiler characteristics.

# Checks for library functions.

AC_CONFIG_FILES([Makefile])
AC_OUTPUT
當然有點經驗的 Autotools 使用者都會知道這個 configure.ac 是用 $ autoscan 產生的 configure.scan 修改而來的
剩下的 main5, main6, main7 如前一篇文章所介紹的一樣,是不會安裝進系統之中的