2009年8月15日 星期六

為台灣致哀


看著電視媒體傳送著台灣50年來最大的災難,心中的難過總是一湧而上,淚水總是在自己的強忍下,停留在眼眶中打轉,望著那些長官們說的鬼話,心中的氣憤更是難平,平常這些辛勞的百姓為生活在大太陽底下工作,而這些高官卻只是在冷氣房裡決定政策,即便是錯誤的政策,而又或者是為了貪汙而制定了哪些莫名其妙的政策,百姓們也都只能苦笑買單。 但是今天,面對多少破碎的家庭,多少妻離子散的畫面,那怕是官員們演個戲也好,為這些苦難的百姓哀悼一下吧,不是他們不願撤離家園,而是誰都不知道有這麼大的災難。再者,如果今天人人都能在冷氣房裡辦公,誰還願意在大太陽底下,踩著滾燙的柏油路,辛勤的工作,再辛苦也是為了那口飯,還有那年幼的小孩,年邁的父母親。我也是一位農家子弟出身,小時候不管颳風下雨或者再大的太陽,總是要把田裡的工作完成,無奈靠天吃飯的農民,可能一個颱風就毀了這段時間的汗水,如果幸運逃過老天的考驗,到了收成的那刻,在田裡所受的大小傷都值得了,但是,這一切也都只能換取全家的溫飽,要多的是不可能的了。即便蔬菜水果再貴,永遠都是批發商在賺大錢,而農夫能感受到的只有曬在那刺痛的身體上的大太陽,還有寒風刺骨的寒流,這一切都夠可憐了,遠在市中心的長官們能感受到嗎?苦民所苦是騙人的。 看著電視,望著一幕一幕的救災畫面,面目可憎的名嘴們、政客們哪一個在第一時間到達災區?哪一個敢深入災區?騙人民的感情與信任,說再多都沒有用,因為災民們需要的是具體的行動,他們在挨餓的時候不會因為你們遠在電視機裡的一句話而飽了,但是他們的親人卻可能因為你晚來了一步而死了,每當時間經過一秒,他們內心的惶恐就多了一分,因為他們想像著親人被壓在土石堆裡,還有那一口氣等待救援,但是卻手無寸鐵,無法將親人在最後一刻救出。如果是你,你能承受嗎? 七天過了,即便人沒有被壓死,也該被活活的餓死渴死了,是政策殺人,卻沒有一個人為此負責,名嘴與政客也沒有在第一時間發揮他們的影響力前去救災,只有小兵們、小老百姓們還在為災民冒著生命危險持續搶救,這才是真正愛這片土地,這塊土地上的偉大英雄,每當畫面看到名嘴與政客的相互批判,讓我著實感到悲哀,為什麼還有人願意相信這群人,他們分不清誰才是真正愛他們的人嗎? 雨過就會天晴,但是人死不能復生,未來的路還相當的漫長,面對可能產生的孤兒,未來的路更顯的崎嶇,想到這心中又不免難過了起來。一夕之間,山河變色,希望的是,未來政府能多照顧這群災民,能讓她們面對自己未來的人生,更要想辦法照顧這些孤兒,因為他們真的是無辜的,他們的生命現在還很脆弱,很可能因為沒了父母,失了照顧,成了社會的邊緣人。

GCC (4.4.1)之C Preprocessor part I


C Preprocessor系列,其實就是CPP manual的讀書心得。

initial processing 在初始化的處理上,CPP主要有四個任務:
  1. 讀取檔案到記憶體中。
  2. 如果有啟動trigraphs,那麼會做trigraph轉換,如??/轉換成\,??-轉成~等等。
  3. 合併連續行成一行,就是每行後面有\當結尾的,會將該行和下一行合併成一行。
  4. 將註解的部分用一個空白取代。
Tokenization CPP使用空白區分token,如果沒有token之間沒有空白,那麼CPP會使用greedy法則,也就是盡可能的找最長字串的token,比如a++++b會被解釋成a++ ++ +b,而不是a++ + ++b,而token之間預設也是一個空白,比如: #define foo() bar foo()baz 會被解釋成bar baz,不管#define中的foo()和bar有幾個空白。
brook@debian:~$ echo -e "#define foo() bar\nfoo()baz"|gcc -E -
# 1 "<stdin>"
# 1 "<built-in>"
# 1 "<command-line>"
# 1 "<stdin>"

bar baz

The preprocessing language 所謂的preprocessing language,就是以#開頭的那些preprocessing directive,這些指令是固定的,無法讓使用者自訂新的。這些指令主要有四種用途:
  1. include of header file(比如#include)。
  2. Macro expansion(比如將#define展開)。
  3. Conditional Compilation(比如#if,決定哪些要被編譯,哪些不被編譯)。
  4. Line control(我不是很清楚,只知道#line num下一行就會由num當行數重新計算,也可以簡寫成# num)。
  5. Diagnostic(比如#error)。


Header files Header files主要分成兩類:
  1. System header files:提供OS部分的interface,<xx.h>。
  2. Your own header file:提供source files的interface,"xx.h"。
傳統上,C的header files都使用.h當結尾,當您在#include 時,其實就是將xx.h複製到該行上,並利用Line control,重新計算後面的行數,比如:
brook@desktop:~$ cat a.h
#ifndef A_H
#define A_H
int hello(char *str);
#endif
brook@desktop:~$ cat a.c
#include 
#include "a.h"

int main(int argc, char * argv[])
{
    printf("hello world\n", __LINE__);

    return 0;
}
brook@desktop:~$ cpp -I./ a.c
... 略 ...
# 2 "a.c" 2
# 1 "a.h" 1


int hello(char *str);
# 3 "a.c" 2

int main(int argc, char * argv[])
{
    printf("hello world\n", 6);

    return 0;
}
看到把a.h中的int hello(char *str)複製到source file中了嗎?隨後又重新計算行數# 3,而header files並沒有規定只能放啥東西,不過通常就是放置一些宣告。 include syntax #include有兩種變形:
  1. #include <file>:for system header files,就是會尋找系統路徑。
  2. #include "file":for your own header files,會先尋找目前目錄,找不到就依循的search path尋找。
注意:有些OS會使用'\'當pathname separator,比如M$,但是還是請使用'/',因為GCC一律都使用'/'當pathname separator。

search path 一般在UNIX底下,system header files的search path為 /usr/local/include libdir/gcc/target/version/include /usr/target/include /usr/include 透過GCC實際觀察一下吧
brook@desktop:~$ gcc -I./ -v a.c
... 略  ...
ignoring nonexistent directory "/usr/local/include/i486-linux-gnu"
ignoring nonexistent directory "/usr/lib/gcc/i486-linux-gnu/4.3.2/../../../../i486-linux-gnu/include"
ignoring nonexistent directory "/usr/include/i486-linux-gnu"
#include "..." search starts here:
#include <...> search starts here:
 ./
 /usr/local/include
 /usr/lib/gcc/i486-linux-gnu/4.3.2/include
 /usr/lib/gcc/i486-linux-gnu/4.3.2/include-fixed
 /usr/include
End of search list.

... 略  ...
您可以透過-I新增search path,新增的search path會優先被尋找,但是如果新增的search path已經在預設的(系統的)search path中,該search path會被忽略,避免影響預設的search path順序。有興趣可以再研究一下-I-和-iquote。

Once-Only headers 一個header files很可能被include兩次,進而造成重複define,引起compile error,標準做法應該是在header file中,使用conditional compilation防止被引入兩次,如:
#ifndef XX_H
#define XX_H
 the entire file
#endif /* !XX_H */
XX_H我們稱為controlling macro或guard macro。macro name不應該使用'_'開頭的命名方式,而在system header files中應該使用'__'開頭,避免與user program造成衝突。 computed include 有時候我們會需要根據不同的configure引入不同的header files,比如:
#if SYSTEM_1
#include "system1.h"
#elif SYSTEM_2
#include "system2.h"
#elif
... 略 ...
#endif
像這樣需要經過計算然後才引入的行為就稱為computed include,你可以發現很就會被許多的#elif淹沒,你可以使用簡單的方式替代,比如:
#define SYSTEM1_H <stdio.h>
#define SYSTEM2_H "system3.h"
... 略 ...
#include SYSTEM1_H
#include SYSTEM2_H
甚至SYSTEM_H可以由Makefile傳遞進來。

System Headers是用來宣告OS和runtime libraries的interface,system header所產生的warning除了#warning會顯示出來,其他的都會被抑制住。一般而言,當GCC在編譯時就已經設定哪些目錄會被當成system header存放的目錄了,不過我們還是可以透過-isystem#pragma GCC system_header兩種方式,將一般的header file當成system header file。
  • -isystem後面接的目錄會被當成system header,用法上和-I一樣。
  • #pragma GCC system_header這個指令告訴GCC把這個header file當成system header。

brook@ubuntu:~$ cat syshdr.c 
#include <stdio.h>
#include "syshdr1.h"
#include "syshdr2.h"
#include "syshdr3.h"

int main(int argc, char *argv[])
{
    syshdr1();
    syshdr2();
    syshdr3();
    return 0;
}
brook@ubuntu:~$ cat syshdr/syshdr1.h 
#ifndef SYSHDR1_H
#define SYSHDR1_H
int syshdr1(void) { printf("%s\n", __FUNCTION__); }
#endif
brook@ubuntu:~$ cat syshdr2.h 
#ifndef SYSHDR2_H
#define SYSHDR2_H
#pragma GCC system_header
int syshdr2(void) { printf("%s\n", __FUNCTION__); }
#endif
brook@ubuntu:~$ cat syshdr3.h 
#ifndef SYSHDR3_H
#define SYSHDR3_H
int syshdr3(void) { printf("%s\n", __FUNCTION__); }
#endif
brook@ubuntu:~$ gcc -Wall -isystem syshdr syshdr.c -o my_syshdr
syshdr.c: In function ‘syshdr3’:
syshdr3.h:3: warning: control reaches end of non-void function



2009年8月12日 星期三

extjs - Ext.PagingToolbar簡介


隨著records的增加,Browser會花更多的時間在畫grid,Ext.PagingToolbar主要用來處理這樣的問題,透過傳遞參數的方式(start:第幾筆開始,limit:共要幾筆),告知Server要傳送哪些資料。
注意,Ext.PagingToolbar只是告知Server要顯示的資料為何,而Ext.PagingToolbar預設會顯示所有的資料。

因為要讀取Server的資料,所以Store.proxy改用Ext.data.HttpProxy。而讀取的格式我們打算用json,所以,store.reader要用Ext.data.JsonReader。
我們的json file(phone_num.json)如下:
{
    "success": true,
    "results": 12, // how many entry will be sent.
    "rows": [ // *Note: this must be an Array
        { "city": "台北市", "num": "02"},
        { "city": "新竹市", "num": "03"},
        { "city": "苗栗縣", "num": "037"},
        { "city": "台中市", "num": "04"},
        { "city": "南投縣", "num": "049"},
        { "city": "嘉義市", "num": "05"},
        { "city": "台南市", "num": "06"},
        { "city": "高雄市", "num": "07"},
        { "city": "屏東縣", "num": "08"},
        { "city": "台東縣", "num": "089"},
        { "city": "馬祖", "num": "0836"},
        { "city": "烏坵", "num": "082"}
    ]
}

Ext.onReady(function() {
    var cm = new Ext.grid.ColumnModel([
        new Ext.grid.RowNumberer(),
        {
            header: 'City',
            dataIndex: 'city'
        }, {
            header: 'Num',
            dataIndex: 'num'
        }
    ]);

    var store = new Ext.data.Store({
        proxy: new Ext.data.HttpProxy({url: 'phone_num.json'}),
        reader: new Ext.data.JsonReader({
            totalProperty: 'results',
            root: 'rows',
            fields: [{name: 'city'}, {name: 'num'}]
        })
    });
    store.load();

    var grid = new Ext.grid.GridPanel({
        renderTo: Ext.getBody(),
        store: store,
        cm: cm,
        sm: new Ext.grid.RowSelectionModel(),
        viewConfig: {
            forceFit: true
        },
        title: "區碼",
        loadMask: true,
        height: 200,
        width: 330,
        bbar: new Ext.PagingToolbar({
            pageSize: 5,
            store: store,
            displayInfo: true
        })
    });
});


由於,我們在Server-Side都只是把整個資料丟給client,所以,grid即便有Ext.PagingToolbar,但是還是會顯示所有由Server丟過來的資料。

Paging with Local Data
在local paging官方網站上有提到兩種方式:
  1. Ext.ux.data.PagingStore
  2. Paging Memory Proxy(examples/ux/PagingMemoryProxy.js)

根據Ext.ux.data.PagingStore作者的說法,PagingMemoryProxy缺點如下:
  1. You have to write extra code to remote load the data for the proxy.
  2. query, filter and collect only work on the current page. You have to write extra code to use the PagingMemoryProxy filter support.
  3. Local sorting works, but you need to set remoteSort:true. There is no remote sorting support.
  4. Added and removed records are only remembered for the current page.
  5. Changing the page is relatively slow (PagingMemoryProxy reprocesses all data).
檔案下載與詳細討論:
PagingStore.js for ext-js v3
PagingStore.js for ext-js v2

以下是v2的code:
Ext.onReady(function() {
    var cm = new Ext.grid.ColumnModel([
        new Ext.grid.RowNumberer(),
        {header: 'City', dataIndex: 'city'},
        {header: 'Num', dataIndex: 'num'}
    ]);

    var store = new Ext.ux.data.PagingStore({
        proxy: new Ext.data.HttpProxy({url: 'phone_num.json'}),
        reader: new Ext.data.JsonReader({
            root: 'rows',
            totalRecords: 'results',
            fields: [{name: 'city'}, {name: 'num'}]
        })
    });
    store.load({params:{start:0, limit: 4}});

    var grid = new Ext.grid.GridPanel({
        renderTo: Ext.getBody(),
        store: store,
        cm: cm,
        sm: new Ext.grid.RowSelectionModel(),
        viewConfig: {
            forceFit: true
        },
        title: "區碼",
        loadMask: true,
        height: 200,
        width: 330,
        bbar: new Ext.PagingToolbar({
            pageSize: 4,
            store: store,
            displayInfo: true
        })
    });
});


可以看到,Server一樣丟全部的資料,但是Grid已經可以以paging的方式顯示了。


熱門文章