2007年6月27日 星期三

2007陶笛與塤的形音色美

陳鏡老師將於苗栗陶瓷博物館舉辦為期一個月的陶笛與塤的創作展, 同時也邀請多位國內陶笛製作者共同參加.

展覽期間也有多場精彩的陶笛音樂演出及陶笛相關的研討會. 這場夏日盛會, 您非去不可.

地點: 苗栗陶瓷博物館
時間: 2007 年 8/01 ~ 8/31
官網: 2007陶笛與塤的形音色美

白鷺鷥獲邀在 8/4 開幕音樂茶會上演出. 預計曲目為

莫札特小夜曲
玩具兵進行曲
四月望雨
白鷺鷥

敬請期待 .....



---------------------
◆指導單位:行政院文化建設委員會.國立臺灣工藝研究所
◆主辦單位:苗栗縣政府.苗栗陶瓷博物館
◆協辦單位:陶藝雜誌、中華民國陶藝協會、苗栗縣陶藝協會、苗栗縣陶笛協會、白鷺鷥陶笛樂團、台中市陶笛樂團、彰化縣陶笛樂團、換日線陶笛隊、嘉義市樂耕陶笛隊、Rubato陶笛音樂藝術樂團

◆展期:2007年08月01日起至08月31日止
◆會場:苗栗陶瓷博物館(苗栗縣旅客服務映像園區)
◆地址:苗栗縣公館鄉館南村館南352號
◆電話:037-236-818 傳真:037-238-288
◆陶笛參展作家:林孟德、李生鴻、李焜剛、何財銘、吳寶安、吳淑櫻、官鋒忠、范欽章、許浩倫、郭慶榮、陳國祥、陳鏡、張志名、楊文涵、劉鼎魁、潘國隆、蔡宗翰、謝永清、藍鈺雯(以上順序依姓氏筆劃排列)

◆記者會:2007年08月02日(四)上午10點整
◆出席名單:參與展覽及演出的朋友 等自由參加

◆開幕音樂茶會:2007年08月04日(六)上午10點整
◆演奏名單:許浩倫、蘇義堡、黃文琪、陳鏡、白鷺鷥陶笛樂團、台中市陶笛樂團、彰化縣陶笛樂團、 Rubato陶笛音樂藝術樂團 等

◆複管陶笛表演&陶笛製作示範:2007年08月12日(日)上午10點整
◆演出名單:許浩倫、陳鏡

◆陶笛吹奏&製作研討會:2007年08月19日(日)上午10點整
◆演出及主講名單:苗栗縣陶笛協會、換日線陶笛隊、潘旭建

◆陶笛吹奏表演:2007年08月26日(日)上午10點整
◆演出名單:嘉義市樂耕陶笛隊、王明中


◆策展人:陳鏡(桼采匋)
◆相關展覽圖片請至 http://photo.xuite.net/ceramicist/1743318



◆【苗栗縣旅客服務映像園區】占地2.1公頃餘
位置-位於苗栗縣公館鄉南方約兩公里,省六號公路左側;距離中山高速公路僅三公里餘。
特色-本為陸軍基地,已廢棄多時,苗栗縣政府以閒置空間再利用方式,改造為:
1、觀光旅遊資訊中心
2、苗栗縣陶瓷博物館
3、成為苗栗地區農業特產展現中心點
4、地區景觀公園
5、客家文化特色園區
http://www1.mlc.gov.tw/culture/01.php?id=24

2007年6月26日 星期二

檢查 library dependency 的小工具

在開發"手工打造"的 Embedded System 時,可能會遇到library depency 的問題
而我們不見得能一下子就知道這個 library 的 dependency (請使用 ldd or objdump)

愛偷懶的小弟因為懶得算 dependency,就寫了一支小 script 幫我算

它可以把我所指定的程式所依賴的 library 找出來,如果在我的 target 之中沒有它就把它從 library source copy 過來 (這樣就可以找出 library 的最小集合)

它可以搭配其它 script 使用,做到完全自動化計算 library dependency

個人覺得還蠻方便的,分享給大家

File: checklib.sh


#!/bin/bash
# This software may be used and distributed according to the terms
# of the GNU General Public License (GPL), incorporated herein by reference.

# Drivers based on this skeleton fall under the GPL and must retain
# the authorship (implicit copyright) notice.
#
# This program is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU

# General Public License for more details.

#composer Tick


LIB_SOURCE_PATH="/Project/tank/lib/ /usr/toolchain/arm-linux/arm-linux/lib"
REFERENCE_LIB_PATH="/path to my project rootfs/disk/lib/"
TARGET_LIB_PATH="/path to my project rootfs/disk/lib/"

LIBS=""
for file_tmp in $@;do
FILE=`file $file_tmp`
if (( `echo $FILE | grep "dynamically linked" | wc -l` == 0 )) ;then
continue;
fi
if [ "`echo $FILE | awk '{print $7}'`" = "80386," ];then
OBJDUMP="objdump"
else
OBJDUMP="arm-linux-objdump"
fi
LIBS+=`${OBJDUMP} -p $file_tmp | awk '/NEEDED/ {printf("%s ",$2); }'`
done

for lib_tmp in $LIBS;do
IS_exist=0
for SS in ${REFERENCE_LIB_PATH};do
if (( `find $SS -name ${lib_tmp} | wc -l` > 0));then
IS_exist=1;
break;
fi
done
if (( ${IS_exist} > 0 )); then
continue;
fi
SRC_LIB=""
for SS in ${LIB_SOURCE_PATH};do
SRC_LIB="${SRC_LIB} `find ${SS} -name ${lib_tmp}`"
done
if [ $SRC_LIB ];then
SRC_BASE=`echo $SRC_LIB | awk '{stop=index($0,".so");ans=substr($0,0,stop-1);print ans}'`
echo "You are going to copy the following libraries to ${TARGET_LIB_PATH}:"
echo "cp -av ${SRC_BASE}[0-9.]*[!a] to ${TARGET_LIB_PATH}"
echo -n "continue? "
read ans;
if [ "$ans" = "y" ] || [ "$ans" = "Y" ];then
cp -av ${SRC_BASE}[\.0-9\-]*[!a] ${TARGET_LIB_PATH}
fi
else
echo "Warning: I cannot find ${lib_tmp} from the LIB_SOURCE_PATH you set!!"
echo "It might be missed not in somewhere else."
echo "Please check if it is not in '${LIB_SOURCE_PATH}'"
fi
echo
echo
done

2007年6月23日 星期六

[舊文]list_head 的用法

三年前寫的文章
有機會再來討論一下這個好玩的東西


引言回覆
發表 list_head 的用法
在 linux kernel 之中做double link list 時有個東西實在是又好用,又強大,速度又快。
那就是 list head
其實這個東西和我們在資料結構中學的沒兩樣。
只是這個玩意運用了 struct 的結構性,及 C 語言本身的低階能力,使得速度變得很快。

struct list_head {
 struct list_head *prev,*next;
};

他的內容只有 list_head 的 兩個 pointer.也只能指 list_head
在運用時,有個 macro 叫
list_entry

他可以以 list_head 的位址,及這個 list_head 在物件中的位址,算出物件的位址。
也就是說,假設有一個物件叫 ABC 以下是內容:

struct ABC {
 int index;
 char *name;
 int score;
 struct list_head lh;
}


如果你今天拿到一個 ABC物件中的 lh 的位址(lhpt)的話,你就可以從這裏拿到該物件的位址。
e.q. struct ABC *abc = list_entry(lhpt, struct ABC,lh);
如此,list_head 就會依 lh 在 ABC 的相對位置,及 lhpt 的位址算出 這個 ABC 的位址。

當有 list_head 的物件被 allocate 出來時,要記得初始化 prev 和 next
e.q. INIT_LIST_HEAD(&abc->lh);

加入串列中的用法是
list_add(&abc->lh,head);
or
list_add_tail(&abc->lh,head);

刪除是
list_del(&abc->lh);
但是,有一點一定要記得 list_del 不會清掉 abc->lh 的內容
但是會清掉 abc->lh->prev 的 next 和 abc->lh->next 的 prev
如果要清得乾淨一點,就要用 list_del_init(&abc->lh);
那就會 abc->lh->prev == abc->lh->next==&abc->lh

--
會不會寫得太難懂啦 ~_~

星期五 二月 20, 2004 8:18 am

[舊文] bash shell script 程式設計

這是三年前寫的舊文嘍~ 放在這懷舊一下



bash shell script 程式設計(序言,1,2,3,4,5,6,完)

發表 bash shell script 程式設計(序言,1,2,3,4,5,6,完)
相信所有玩過 Unix的人都會同意 shell script 是一個十分強大的工具,可以用來幫助自已在短時間內解決許多枯\躁而乏味的工作,而且讓工作變得有趣。
對於 shell script 要怎麼寫,為什麼會想到這麼寫?對於這個問題,我個人的答案是:用多了,就知道了。有時很聰明的想法,往往是靈機一動,就想到了。我會建議大家,如果寫了好用的 script 就把他留下來,給個好名字。有時候用得上時,就會很高興有這個自製的工具。
當然,要寫 script 之前,要先了解一些東西。
1. 資料流
>, >> , < , | , #&># .....
2. 一些好用的工具
cat
grep
awk
sed
find
more
........ everything you can use.
3. 變數
4. 指令代換
5. 判斷式
if else fi
6. 流程控制
while for
....
在之後的幾篇文章中,我會慢慢把一個個 topic 寫完...
希望不會寫得太糟 Razz



tick 在 星期五 二月 06, 2004 12:11 am 作了第 8 次修改
星期六 一月 17, 2004 12:42 am




發表 資料流
UNIX是第一個支援資料流的作業系統。
什麼是資料流呢?
就是把程式的輸入和輸出想像成一個串流,
在 Unix 之下,任和一個一般的程式都會直接開三個檔案。
0. stdin
1. stdout
2. stderr
這三個檔案分別是 資料流的 輸入和輸出,及錯誤輸出

stdin ====> 程式 =====> stdout
        ∟==> stderr

在 default 之下,stdin 是接著 keyboard
stdout 是接著 tty (你想成 monitor 就好了)
stderr 也是接著 tty


資料流既然是"流",那當然可以轉向。
"<" 這個指令呢就是把 stdin 接上後面的檔案 如 cat <>" 是把 stdout 寫入後面的 file
如檔案內有東西,會把檔案內的東西清空才寫入。

">>" 和 ">" 相同,但不會把檔案內的東西清空,而是寫在檔尾


cat test.txt >> foo

"|" (pipe)這個呢則是把前面的stdout 接到後面程式的 stdin

cat test.txt | more
這樣, cat 的輸出就可以被 more 接下來。

> >> 還可以把 stderr 轉到 stdout

2>&1
也可把 stdout 轉到 stderr

1>>&2

在 shell script 中資料流轉向,和pipe 是十分常用的技巧
(應該說是基本工夫)
在一般下指令的時候也是很常用上

cat pbsd.c | grep CONFIG_PBS

有時候如果要做一些比較複雜的動作。接個四五個 pipe 再加上轉向,也不為過

--
過年期間我不能上網,所以我會在自己的 notebook 寫,等回台北再 post 上來

星期日 一月 18, 2004 1:45 am



如果有好的能力,沒有好的工具,那也是沒有用,但很幸運的是,
之前的 Unix Programer 寫了許多方便自己的工具,相同的那些工具\r
也能方便我們。
下面是一些工具的簡單介紹。(都可用 pipe 和轉向)
cat :
  把檔案打開,印到 stdout 上
grep :
   grep 是一個在輸入資料中找出指明要找的字串的那一行文字的工具。
  如
   grep "I Love Shell Script" test.txt
   就是把 test.txt 這個檔案中,有 "I Love Shell Script" 這個字串的那一行印>到
  stdout 中。
  cat test.txt | grep "I Love Shell Script" #也是一樣的
awk :
  一個用來分析內文的工具,它可以掃描文章內容,找出你要的文字,且
  依你的要求做輸出,你可以把它當做 grep 的加強版。
  awk 的語法很像 C 語言,所以對會寫 C 語言的人而言,這是一個很好用的工具
  如
  awk '{print NR,$0}' text.txt
  就會把 text.txt 的內容印出來,且加上行數
sed :
  這個工具可以幫你把你想換掉的字串一次換掉

awk 和 sed 這兩個工具是十分強大的工具,它們可以做到的事十分的多
遠比它一開始被定義時所想到的功能還多,多到可以寫一本書來談。\r
O'Reilly 就有出一本 awk & sed 的書,有興趣的話可以找一本來看看。

find:
  這個工具可以依你的要求,找出你想要的檔案,在系統管理上很好用。
  可是依時間,大小,檔名,修改時間等等的選項來找檔案。

more (less) :
  把 stdin or 檔案內容分成一頁一頁的來看,一般來說, less 比較強一些。
  可以支援 vi 的語法,但目前 more 也一一支援了。

以上每個工具都是十分好用的工具。
但我沒有詳細的談,詳細的東西呢,你可以用 man 這個工具來看。

man find
然後你就會了解為什麼我只說一下它們的功能了 Smile

星期二 一月 27, 2004 11:34 am




發表 變數 參數
在 bash 中設定變數很容易
不用指定形態
直接指定就可以了

myvar="I love linux."
就是指定 myvar 這個變數為 "I love linux." 這個字串
使用這個變數呢,則是用 $myvar 來使用

echo $myvar

如想對 變數做運算,可用 let
let "expression"

如 let i=i+1

在 shell script 中,可以在 script 加上參數
而參數會依你所打入的指令依序定義
如有一個script 叫 showme
#!/bin/bash
# filename showme
echo $0
echo $1
echo $2

執行結果如下
>./showme abc def
./showme
abc
def

如果要 show出所有參數,可以 $* 表之。
要知道參數數量,可以 $# 表之
$$ 為 script 執行時的 pid


--
寫著寫著,有一些東西不清楚,找了一下 man
發現 man 寫得真是清楚極了。

上 google 查了一下,有一個網頁整理的很好
http://www.fanqiang.com/a4/b1/index_b.html
看來我可以不用寫了 QooQ

星期二 二月 03, 2004 12:47 am



發表 指令代換
在 bash 之中 我們可以用 ` command` 來執行指令,且將其視為一個整體,最後的結果為全部的結果
例如
ls -alF `find -size 0`
就是找出所有 size == 0 的檔案
將之結果當做是 ls -alF 的參數
這個技巧在 shell 之中很常見如
NumberOfFiles=`find | wc -l`
就是把 find | wc -l 這結果指定為 NumberOfFiles 的值

星期三 二月 04, 2004 10:27 pm



發表 判斷式
shell script 是一個完整的語言,當然會有 if else 等等的指令
就讓我們來看看如何使用 bash shell 的 判斷式 吧

在這裏我是假設大家會使用某種程式語言。

格式是如下的

if expression; then
  list
elif list; then
   list
else
  list
fi

這裏的 list 代表是一個或一堆指令的集合。
expression 代表的是一個會有return 值的指令。

如expression 不為空,或 0 就表示為 true
fi 代表 這個 if 結束了 如同 C 語言的 }
then 就如同 C 語言的 {

elif 後面的 list 會依序執行,到了最後如不為空,或 0 就為真 也就是再執行後面的 list

對於 expression 如要寫判斷式 有兩種格式
1. (( expression ))
這個格式等同於 let "expression"
這就是把 expression 內的資料做計算
例如
(( $abc + 1 ))
2. [[ expression ]]
這個格式等是可以比較這個 expression 的兩邊

[[ $1 == "abc" ]]
如果相同,就為 1 否則為 0
比較的運算子有 == != ! && ||
== 是全等
!= 不等
! not 運算子
&& and 運算子
|| or 運算子

( (( $# > 3 )) && [[ $1=="abc" ]] && ( [[ $2!="def" ]] || ![[ $3=="ghi" ]]) )

一個例子
if (( $#==0 )) ; then
echo "usage: xxx [xxx | xxxx]"
else
 if ( [[ $1 != "xxx" ]] && [[ $1 != "xxxx" ]] ) ; then
  echo "prometer [xxx | xxxx]"
  exit
 fi
 if [ $1 = "xxx" ]; then
  ....
 else
  ........
 fi
fi

星期三 二月 04, 2004 11:00 pm



發表 流程控制
在程式之中,for 和 while 讓我們省下了很多的力氣

在 bash shell 之中 for 和 while 的用法如下
for:
在bash 中 for 的用法有兩種
for name [ in word ] ; do list ; done
for (( expr1 ; expr2 ; expr3 )) ; do list ; done
第一種是從 字串的array 中拿出一個,成為 name (name 為變數)
再進入迴圈中,直到array 中的字拿完為止 (list 為迴圈內容)

第二種就很像 C 語言了, 第一次跑時,一開始 run expr1
再 check expr2
跑完 list 後 跑 expr3 再跑 expr2
一旦expr2 的結果是 0 or 空 就跳離


while:
while 的用法
while list_a ; do list_b; done
當 list_a 的最後一個 command 結果不為 0 或 空之時,就進入迴圈做 list_b

until:
until list_a; do list_b; done
和 while 相反,當 list_a 的最後一個 command 的結果不為 0 或空時,離開迴圈。

還有一些東西 如 select case esac 等... 這些東西可以用 if for or while 組合而成,就不再說明了。

星期四 二月 05, 2004 11:40 pm




發表 put all together
說了那麼多,我們開始來寫寫 bash shell script 吧
就拿之前post 過的 chout 為例子好了

首先,一始開寫 shell script 的人很有可能不知道如何執行 shell script
假設我們有一個 shell script 叫 chout 已經寫好了
我們有兩個方法可以執行它
1. bash chout
這個方法是執行一個 bash ,且叫它讀入 chout 並執行
2. 在 chout 的第一行加入 #!/bin/bash
(告訴你的shell 這個 shell script 指定用 bash)
再 chmod a+x chout
(指定 chout 為一個可執行檔)
之後你就可以把 chout 當一個程式來使用了。

讓我們來看看這個例子
01 #!/bin/bash
02 if (( $#==1 )) ; then
03  ares=`find -name '*.c'`
04  ares="$ares `find -name '*.h'`"
05 else
06   ares=`find -name $2`
07 fi
08 for arrs in $ares
09 do
10  if [ -f $arrs ] && (( `cat $arrs | grep -i $1 | wc -l` > 0 )); then
11   echo "In file $arrs"
12   cat $arrs | awk '{print "#"NR"\t"$0}' | grep -i $1
13   echo " "
14  fi
15 done

01 行告訴我們,這是一個 bash 的程式
02 $# 是變數的數量,也就是 : 若 變數的數量為 1 則做 03 04 否則做 06
03 把 "find -name *.c" 這個指令的結果存在ares 中
04 ares 再加上 find -name *.h 的結果
06 找 名字為第二個參數的檔案,且存成 ares
08 在 ares 中依序抓出一個元素,命之為 arrs ,如抓完了,做 15 行的下一行(沒有了,就是結束了)
09 10~14 是迴圈範圍
10 當 $arrs 是檔案 ( [ ] 是 test 的簡寫 (有興趣請看 man test)) 且 $arrs 有大於一行包涵 第一個參數的話,做 11~13
11 應該不用解釋吧
12 為$arrs 中的內容加上行號,只 show 出有 $1 的行
13 印空白行

 
以上只是一個小小的 shell script
因為 shell script 不用 compile ,只要隨手改改,隨手寫寫就能用。
不用考慮太多形態的問題,所有程式都是指令。都是 可用的function
所以在使用上很方便,只要多加一點點巧思,就可以用一點點的時間,做出一些很炫的東西。
試著玩玩看吧,保証 linux 功力會大增喔!!

Self Unit Test Framework

自我測試架構
適用語言: OOP language

相信大家都知道軟體測試的重要,也明白軟體測試有助於開發者及早發現問題。
軟體測試之中有相當多的面向,而今天我要提的,是 Unit Test 這個部分。

在 OOP 的語言之中,每個元件應該有其一獨特且專一個功能 (如此才能彰顯 OOP 的優勢)
也就是說,在寫 OOP 的案子時,到最後應該會像是在玩樂高一般。把每個元件兜到它應該出現的位置。案子也就成了。

但,這只是個理想。

實際作做案子就會知道,程式一定有 bug ,而要如何 debug 就成了每個設計師的惡夢。而debug 中最最另人頭疼的,就是不知道 bug 在那。
Unit Test 就是一個很有效且快速的技術幫助設計師了解且找出問題的所在。
在表面上,做 Unit Test 看起來會多花許多工夫。不過,在案子越來越龐大時, Unit Test 可以快速的指出問題的癥結,也可以讓 PM 更明確的掌握案子的進度。
一般來說,使用 Unit Test 技術可以讓案子穩定的有進展。

在使用 Unit test 之前要了解幾件事。
1.你的物件是要做什麼的。
2.有那些 boundary 存在?
3.錯誤情況的處理為何?
4.任何你的物件會遇到的極端狀況

再依上述的認知做以下的 Test
1. 正常的 input 資料是否有正常的 output (要先知道正確的結果)
2.在 boundary 時資料是否也是正常運作 lower/upper bound 都要做
3.不正常的 input 時,是否能發現,且做出正確的回應?
4.大量被使用時,是否能成功的運作?

在做測試時,你要用最嚴苛的心去測你的程式。這才是正真對你自己仁慈。

每個物件都要有一個自己的測試程式,每做一次修改(不管多小的修改),就要跑一次,並得到一份報告。

如果物件被整合到其它物件,其它的物件也要做 Unit test
最懶也是最好的方法就是寫支小 script 一次跑完所有的 Tests
(好像很花時間?其實不然,因為它可能會在很短的時間內幫你找出本來可能要花一個月才找出來的 bug)


以下是小弟寫的 Unit Test FrameWork (可輕易的改寫成 Java 版的 :-) )
每個要被測試的物件,只要多重繼承 SelfTestUnit
然後實作下面兩個 functions
virtual bool doSelfTest() {return false;};
把你對這個物件的測試寫在裏面,並回報是否測試成功

virtual const char *myClassName(){return "Please Set ClassName Here";};
指定這個物件的名字 (for report)

而在測試程式之中寫:
Capataz cap;
cap.addSelfTester(new 被測試的物件(1));
cap.addSelfTester(new 被測試的物件(2));
cap.addSelfTester(new 被測試的物件(3));
...
cap.doTest();

就可以了...




File: selftest.h

#ifndef __TICK_CPP_SELF_TEST_FRAMWORK__
#define __TICK_CPP_SELF_TEST_FRAMWORK__

#include <vector>

class SelfTestUnit {
public :
virtual bool doSelfTest() {return false;};
virtual const char *myClassName(){return "Please Set ClassName Here";};
};

class Capataz {
public :
//Capataz(char *file);
//~Capataz();
bool addSelfTester(SelfTestUnit *);
bool addSelfTester(SelfTestUnit &);
bool delSelfTester(SelfTestUnit *);
bool delSelfTester(SelfTestUnit &);
bool doTest();
private:
std::vector < SelfTestUnit * > _tester;
void testReport(SelfTestUnit *,bool );
void allPassReport(bool);
};


#ifdef SELFPROFILE
#include <stdlib.h>
#include <stdlio.h>
extern "C" void __cyg_profile_func_enter(void *func,void *caller) __attribute__((__no_instrument_function__));
extern "C" void __cyg_profile_func_exit(void *func,void *caller) __attribute__((__no_instrument_function__));
#endif //SELFPROFILE

#endif // __TICK_CPP_SELF_TEST_FRAMWORK__




File: selftest.cpp

#include <iostream>
#include "selftest.h"


bool Capataz::addSelfTester (SelfTestUnit * tester) {
if (tester == NULL) {
std::cout << "Try to register a NULL pointer" << std::endl;
return false;
}
_tester.push_back (tester);
return true;
}

bool Capataz::addSelfTester (SelfTestUnit & tester) {
return addSelfTester (&tester);
}

bool Capataz::delSelfTester (SelfTestUnit * tester) {
std::vector < SelfTestUnit * >::iterator pos;
if (tester == NULL) {
std::cout << "Try to remove a NULL pointer" << std::endl;
return false;
}
for (pos = _tester.begin (); pos < _tester.end (); pos++) {
if (tester == *pos) {
_tester.erase (pos);
return true;
}
}
return false;
}

bool Capataz::delSelfTester (SelfTestUnit & tester) {
return delSelfTester (&tester);
}

bool Capataz::doTest () {
bool allpass = true;

std::vector < SelfTestUnit * >::iterator pos;
if (_tester.size () == 0) {
std::cout << "There are no tester yet!!" << std::endl;
return allpass;
}

bool btmp;

for (pos = _tester.begin (); pos < _tester.end (); pos++) {
std::cout << "Do Testing:" << (*pos)->myClassName () << std::endl;
btmp = (*pos)->doSelfTest ();
allpass = allpass && btmp;
testReport ((*pos), btmp);
}
allPassReport (allpass);
return allpass;
}


void Capataz::testReport (SelfTestUnit * stu, bool rep) {
if (rep) {
std::cout << "Test Report: " << stu->myClassName () << "--->OK!!" << std::endl;
} else {
std::cout << "Test Report: " << stu->myClassName () << "--->Failed!!" << std::endl;
}
}

void Capataz::allPassReport (bool rep) {
std::cout << std::endl;
if (rep) {
std::cout << "All Passed? Yes" << std::endl;
} else {
std::cout << "All Passed? No" << std::endl;
}
}

[舊文]陶笛 DIY 心得圖

因為陶韻的資料不見了,只好把以前的東西搬來這裏 @_@
慘~ 過去點點滴滴的回憶啊~

post at:
2005/08/29 17:29


[舊文]陶笛DIY 時的想法

舊文回 po ,因為陶韻的資料不見了,把它放到這來嘍

星期一 十月 17, 2005 7:56 pm 文章主題: DIY 時的一些....想法 引言回覆
也許應該放在閒聊板 0_O

剛剛才用榔頭敲掉了一隻花了很多心血、長得很美的笛子
原因無它,"音色不夠好",而且又被我修壞掉了

當決定要把它回收重作之時,內心其實是天人交戰的。
那隻笛子是我彷是誠的外型,做日式十二孔笛。
外型真的很美,可惜內空間沒挖好,又想用調音的調到我想要的調性
調到了,音色也毀了。

這陣子,作了好幾把笛子,在每把笛子中都灌注了多少的心血,感情。
每當一把笛子第一次發出聲音之時,總是有一種莫名的感動,就好像聽到新生兒第一次啼哭,任何人都會因此而感動…之後慢慢地從單音變成十幾個不全的音,再慢慢的一個音一個音的相對音準慢慢確立。如果幸運的話,它會從黑黑的笛子慢慢變成灰白色…此時它已經能吹奏出許多的曲子。它已經有強烈的個性,有它的優缺點。
出來的不見得是我所預期的個性,不見得是我要的音色,但每一支都讓我收獲不少。對於"陶笛"這樣東西又有更深入的體會,對於每種性質的平衡又有更深的體悟。

把笛子拿去燒,又是另一次的浴火重生,每當決定安排一次燒笛,總是會在腦海裡幻想燒出來會有什麼樣的花紋,啊~~這到目前我為是沒辦法有效的掌握…也會怕燒壞了…這時的每一隻笛子早就和我有深厚的感情,早就帶給我無窮的歡樂。往往在燒之前,都是以訣別的眼神看著它們,先做好最壞的打算。所以燒成後,總是有一種重生的感覺。

陶笛 DIY 入門真的不難,只要你有正確的工具,有點耐心,有點幸運,要一隻陶笛嗚嗚叫真的不難。
難,是難在於進階之後。要高音漂亮?低音漂亮?音色乾淨?共鳴好?聲音哄亮?有無氣聲?音是不是夠準?每個音的氣量是否平均?是否好演奏?外型好不好看?……

時至今日,學作陶笛大約半年的時日,回想起剛開始做陶笛之時,才做了幾把,就以為可以做出很好的笛子,深深覺得好笑

手下那把破碎不堪的土笛,在幾個月前,我可能會把它封成 "演奏級" 的笛子,而現在卻是期待它的轉生。

陶土就是這麼可愛,當你不滿意時,總是個可以再來一次 ^.^

2007年6月11日 星期一

Linux kernel 之 list_head

Linux Kernel 是世上的 Hacker 們留給世人一份極為珍貴的資產。
然而 Kernel 卻又是如此的龐大,要一下子了解整個 Kernel 實在不容易。

不過,有個有趣的進入點也是一個好的開始。

在大型的系統之中,必定會有大量的資量要處理,儲存...
處理資料的效率和可靠性會強烈的影響一個作業系統的表現。

在這裏 Linux 使用了一個很漂亮,而且相當強大的手法來實作 double link list

它放在 include/linux/list.h

這個東西,其實小弟在三年多前就寫了一篇文章談它
不過那時只是很簡略的談了一這個工具的用法。沒有深入的討論它背後的技術。


list_head 是一個很簡的 struct ,內容如下
struct list_head {
struct list_head *prev,*next;
};

沒錯,它就是這麼的簡單。 list_head 的內容就是兩個 list_head 的指標 (pointer 註一)
我們可以把它想像成一個鉤環,可以把它安裝在任何結構之中。
例如:

struct ANY_STRUCTURE {
int A;
int B;
struct list_head head_1;
char *C;
struct list_head head_2;
}
在這個例子之中 ANY_STRUCTURE 包涵了兩個 list_head 的實體
如下圖所示:


只要稍微想一下,不難發現 head_1 head_2 的 prev 和 next 可以輕易的指到任何 list_head 的位址上。

而當我們有一個結構的定義時,我們就能算出各個 field 在結構中的相對位址。
也就是說 head_1 相對於 struct ANY_STRUCTURE 的位址是 sizeof(int) * 2
而 head_2 的位址是 sizeof(int)*2 + sizeof(struct list_head) + sizeof(char *) 註二

也就是,如果我有一 head_1 的位址,我就可以算出包涵這個 head_1 的ANY_STRUCTURE 的位址了。
設計 head_1 的位址為 ptr,則 ANY_STRUCTURE 的位址是
==> ptr - ((structure ANY_STRUCTURE *)0)->head_1
在這裏,我們把位址 0 當做是 structure ANY_STRUCTURE 的指標,而它指相的 head_1 的 offset 就是 ANY_STRUCTURE 到 head_1 之間的 offset

也就是說,我可以間接從 head_1 或 head_2 的位址得到 ANY_STRUCTURE 的位址。

在 Linux kernel 之中,就利用 list_head 的 prev next 指向 前一個,及後一個 list_head 的物件的位址,和同一串 list 中的物件都為相同結構。就可以不用搬資料,只控制指標的快速對任何 type 的結構做操作。


註一:小弟習慣把 pointer 的中文翻成指標,有些人會翻成指針…有相當多的名字…在這知道指的是 pointer 就好了

註二:在 kernel 之中,因為各種機器的架構不同,我們不可以預設一個基本變數的長度。