TAG

首都機能移轉 (2) 歌詞 (2) 靠北文 (40) 戲言 (30) 糟糕 (7) ACG (23) Assembly (2) Boost (2) C (31) C++ (69) CMake (4) CSIE (67) Debian (34) Design_Pattern (2) Django (1) Eclipse (1) en_US (13) FFmpeg (3) FoolproofProject (26) FreeBSD (2) Git (4) GNU_Linux (65) IDE (5) Java (11) JavaScript (19) KDE (15) Khopper (16) KomiX (3) Kubuntu (18) Life (1) Lighttpd (2) Mac_OS_X (2) Opera (1) PHP (2) PicKing (2) Programing (21) Prolog (1) Python (7) QSnapshot (2) Qt (30) Qt_Jambi (1) Regular_Expression (1) Shell_Script (7) Talk (98) VirtualBox (7) Visual_Studio (13) Windows (18) zh_TW (36)

2009年11月29日 星期日

Array IS NOT pointer

呣,其實我大一的時候也有這種觀念,畢竟那時 pointer 還是個陌生的怪物,那還是個「如果你不會傳 array 就宣告成 global」的時代。
基本上 C 的 array 實在不怎麼討喜,包括它的「陣列大小一定要跟在陣列名稱後面」這種鳥語法,如果再加上 function pointer 那可真是醜斃了。之所以 array 經常和 pointer 混為一談,最大的原因應是 array 會被隱式轉型[?]為 pointer,且 array 變數本身的值就是 array 第一個元素的位址。這讓你可以把陣列丟入如下的函式裡:
void f1( const int * array );
呃,那二維陣列呢?
void f2( const int * * const array );
因為並不存在多維陣列對 pointer 的隱式轉換,因此你無法直接丟進去。
接下來要談談 array 和 pointer 有什麼不同。
最顯著的性質是,array 變數本身是唯讀的,它無法再被賦值,其代表的空間也無法被手動釋放,而 pointer 值則可以變動,指向的內容可能可以被手動釋放;這也是 pointer 令新手感到易混淆的地方,因為它同時有資源的持有者以及迭代性質。
此外,pointer 可以被取址,但是 array 的址和它自己一樣,也就是陣列開始的第一個位址:
int cla[1];
printf( "%p %p\n", cla, &cla );    // the same
另一個較為有用的不同點是,array 可利用 sizeof 求大小,而 pointer 就只是個 pointer。
int8_t a[10];
sizeof( a ); // 10
int8_t * b = a;
sizeof( b ); // 4 or 8, depends on your compiler
那如果你很希望陣列在傳入函式後還是個陣列,C++ 的 template 可以幫得上忙:
template< typename T, std::size_t N >
std::size_t getByteLength( const T ( & array )[N] ) {
    return sizeof( array );
}

// how to use:
int32_t tmp[4];
getByteLength( tmp );  // will return 16
坦白說,我不是很清楚為什麼一定要用 reference to array,不過你的確可以用這個 template function 傳 array。再多維也可以:
template< typename T, std::size_t N, std::size_t M >
std::size_t getByteLength2( const T ( & array )[N][M] ) {
    return sizeof( array );
}
以此類推。

2009年11月18日 星期三

我不喜歡的問題

由於本篇是純粹的靠北文,如果以下內容你看到覺得我很靠北,可以跟我靠北沒關係。 如果你還沒看過提問的智慧,強烈推薦先看過再往下看。
第一個要說的是,在標題打上「急」之類的字眼:
[問題] XXX 不能用 急!!!!
內文如果打「如題」或「餓死抬頭」的更慘,基本上不會有人理你。你的時間是時間,別人的時間也是時間,用這種倉促的標題會讓人感覺你在催促答案,知道答案的人很可能不想淌混水。講白一點,你急不急其實對其他人來說一點關係也沒有,反而會讓人覺得你「沒有誠意,就只是個要答案的」。
然後是那種會在內文最後寫上「請詳細說明,有附圖文最好」之類的問題。大部分的人看到這個附註都會停下腳步,回頭看看有沒有其他可以回答的問題。基本上這就是要人做白工幫你抓圖幫你上傳還要打個幾百字讓你看到懂。換做是你,你要嗎?
上面還有一種變形,就是加上「我是新手」或是「我很笨」之類的附註。其實客觀來說也沒什麼問題啦,只是我個人會看得很不爽。
套上 duck typing 的邏輯:「如果你走起來像新手,知識像新手,問的問題也像新手,那麼我們就會判定你是新手」[?]。是不是菜鳥,老鳥當然一看就知道,還用得著你來講嗎?也許你講這句話是為了謙虛,或是為了怕大家指責你問了鳥問題,但是某些 GY 人(像我)會覺得你想拿這個當擋箭牌,掩飾你不想查資料,不想思考,因為你新你笨所以大家要原諒你要教你教到會。不過很遺憾,並不是新手做什麼都可以被原諒的。
還有一種,就是回文說「上面的太高級了,看不懂,能不能再講詳細一點」。
最糟糕的部分是你沒解釋哪裡不懂,不僅很難回答,還間接讓人認為你可能沒在思考;第二糟的是你無意間甩了前面熱心回答的人一巴掌,至少也回個「謝謝,但是上面提到的 XXX 我不是很懂意思」,意思一樣,但是絕對比原來的說法更能吸引解答出現。
如果整段回覆都是鴨子聽雷呢?那就要列出所有你看不懂的重點,搜尋一下維基百科或 google,找不到或是看不懂才問下一個問題。很多人都會忽略的一個重點是,你必須先知道你的問題才能找到答案。如果你無法凝塑出一個關鍵問題,不了解你到底哪裡不了解,通常代表你的背景知識嚴重不足,你應該先從更基本的地方著眼。
最後一個也是單純我個人很不爽的,客觀上完全沒錯,就是:
都英文,看不懂,能不能換個中文的
基本上我第一個想到的是「怪~我~囉~~」,然後就左轉離開了。
除了客觀上犯了伸手牌的問題以外,言下之意也是希望回答的人可以英翻中一下。現在翻譯用的資源實在很多,就算不是百分之百完全翻譯,靠字典也可以猜出一二,看不懂也猜一下,而不是怪別人只給你英文資料;畢竟很多資料都是英文,不是每個人時間都很多可以打個中文詳解。
我曾看過有人說:那是你們學生才有時間搞這個,上班的人哪有時間去找資料,知道答案直接講就好了啊?
噢,其實我不太相信你們可以用這種態度跟前輩問問題,如果有的話那前輩還真是佛心來的。如果找不到人問你不就擺爛?你的時間是一秒鐘幾十萬上下,別人的時間就不值錢?
你可以想像一下,在數十萬筆的資料中找出有用的,英翻中,抓圖附文,解釋給一個陌生人到懂並解決問題為止,但是不拿任何報酬;你願意嗎?
套某人說的話:「得之,你幸;不得,你命」。如果有那還真是佛心來的,可惜大部分的人都是普通人。
互相減少佔用對方的時間,不也是一種尊重嗎?

2009年11月16日 星期一

Khopper v0.2.3 released

  • 修正 flac 在檔案結尾處可能引發無窮迴圈的問題
  • 修正 flac 沒有 seektable 的問題
  • 修正 ogg 的寫入問題
  • 修正 mp3 vbr 的編碼問題,現在可以正確抓到長度了
  • 修正部分資訊讀取錯誤的問題

2009年11月12日 星期四

X input device setting

原本在找 Kubuntu 9.10 上如何控制觸控版的解法,意外找到這個好像很好用的設定工具。
首先 xinput list 會列出所有己和 X server 連接上的裝置,找出你要設定的裝置,看它的 id,之後用 xinput list-props id 來列出所有可設定的屬性。注意每個屬性的格式都像是:
簡短說明 (屬性 id): 屬性值
找到你想設定的屬性 id,就可以開始改:
xinput set-int-prop 裝置id 屬性id 8 屬性值(空白分隔)
其中的 8 指的是整數長度。

2009年11月11日 星期三

Casting function pointers from void * in C++

在拿 DLL 裡的 symbol 時,它會回傳一個 void *,你必須要自己轉型成對應的 function pointer。不過問題是 C++98/03 都把 void * 視為指向某個物件的指標,而不會是一個 function pointer,所以使用 C++ style 轉型會失敗;既然標準沒有定義,就代表這種行為基本上是依賴實作的,為此我們需要一些怪怪的 reinterpret_cast
作法一,先轉成指標長度的整數,再轉成函式指標。
void * gptr = dlsym( ... );
typedef void ( * fptr )();
fptr my_fptr = reinterpret_cast< fptr >( reinterpret_cast< intptr_t >( gptr ) );
intptr_t 是 C99/C++0x 的新型別,一定是一個指標的大小,如果沒有辦法的話,也可以用 long 代替,大部分的平台上 long 都和指標同長。
作法二,把函式指標的位址轉型成 void * * 再指定。
fptr my_ptr = 0;
*reinterpret_cast< void * * >( &my_ptr ) = gptr;
...

2009年11月9日 星期一

不 try 一下怎麼會知道呢

最近用自己定出的編碼器介面寫外掛時, 才發現原本定的介面怪怪的。
首先是 hook 的呼叫順序。由於 libav* 是先開完檔再呼叫寫入 header 的函式,因此我也讓 openResource 在 writeHeader 之前先呼叫。但是其他的編碼器並不一定是這個順序,其實有的根本就沒有分 setupEncoder 和 setupMuxer 的步驟。反正我也沒有檢查每個 hook 要做什麼事,也不可能檢查 ... 所以這倒是沒什麼關係。
第二個問題是我在抽象類別上有定義 buffer 機制,原本是希望可以減輕具象類別的負擔,沒想到現在用起來覺得有點累贅,多了不必要的介面操作。這部分需要在 0.3 修正。
最嚴重的問題是,編碼器似乎不太需要動態載入外掛,應該說不需要和操作它的圖形介面分開發佈。原本的用意是希望降低耦合度,並且讓其他的外掛也有機會使用它,但是操作用的外掛不可能不知道它要呼叫哪個編碼器,而且分開發佈導致無法使用編碼器特殊的設定,像是壓縮率等。目前看來唯一的好處是其他人也可以産生這個實體 ... so what? 難道 mp3 設定會呼叫 ogg vorbis 的編碼器嗎?總之這個外掛的介面真的有點多餘。

2009年11月5日 星期四

A way to use C++ object in C callback

前幾天在用 libFLAC 的時候[?],它要求 client 註冊一個 callback 給它,prototype 長這樣:
typedef FLAC__StreamDecoderWriteStatus
(* FLAC__StreamDecoderWriteCallback)
(const FLAC__StreamDecoder *decoder,
const FLAC__Frame *frame,
const FLAC__int32 *const buffer[],
void *client_data);
看起來很複雜,其實不會,就是個 function pointer 的 typedef 而己[?]
可以看到最後一個 void * client_data 是用來接受 client 的資料用的,先接 void * 再轉成自己要的型別,算是 C 的老手法了。
Well, 在 C++ 上使用時就沒有那麼簡單。第一個問題是 member function 需要實體才能使用。
簡單一個想法是宣告一個 struct,把實體和 pointer to member function 包起來,再傳給這個 callback function。不過一來這有點麻煩,二來你不能呼叫用 protected 或 private 修飾的 members。
沒關係,我們有 friend!
嗯,不好意思,這的確是可以動,不過真的很爛。
還好我們還有另一顆子彈,就是 static member function。static member 不需要實體,且 member function 可以存取 class 內所有 member,這樣就一次解決了兩個問題。
所以我們的 callback 會長這樣:
static FLAC__StreamDecoderWriteStatus
FlacReader::writeCallback_
(const FLAC__StreamDecoder *decoder,
const FLAC__Frame *frame,
const FLAC__int32 *const buffer[],
void *client_data) {
    FlacReader * self = static_cast< FlacReader * >( client_data );
    // blah blah
}
client_data 那裡傳 this 就好。
不要譴責我沒用 dynamic_cast,我百分之百確定 client_data 是 FlacReader 好嗎?

2009年11月3日 星期二

Khopper v0.2.2 released

Changes:
  • 修正 flac 的解碼問題
  • 新增 mp3 cbr 192 bps 的選項
  • 修正部分環境的編譯問題
看起來很少,可是花了我不少時間在 trace 別人的 code ....