The Lost Art of C Structure Packing

找 C/C++ padding 資料時找到的文章:The Lost Art of C Structure Packing

講解 C/C++ struct padding 講的很仔細。C/C++ 編譯器會為了記憶體對齊而把變數放到偶數位址上,放到偶數位址上的好處是存取快速,而且編譯出來的指令也較少。但像 char 型態的變數佔用的空間不一定會是偶數,所以為了對齊而空出來的空間就是所謂的 padding。例如:

裏面的 pad 就是浪費掉的空間。

除了 padding 還有提到 pack,我以前對 pack 也不太了解。看完這篇才知道 pack 的意思,pack 就是告訴編譯器說,不要把變數對齊到偶數的位址上,當然,這樣做之後,編譯器就不會作 padding,但代價就是效能。在 gcc 裡,用 #pragma pack 就可以。

最後作者的建議就是:

  1. 除非你知道自己在做什麼,否則沒必要使用 pack。
  2. 安排 struct 成員時,先放指標類成員,再依照型態所佔用的空間大小來依序擺放,這樣可以避免不必要的 padding。

Feednix on Ubuntu 12.04

在 OMB! Ubuntu! 上看到 Feednix : Feednix is a Command Line RSS Reader for Feedly,這是用 C++ 開發的 terminal feedly client。目前還沒看到 PPA,就只能自行編譯了。

我的環境是 Ubuntu 12.04。

原始碼可以從 Jarkore/Feednix 這裡取得,裏面沒有提供 ./configure,所以得先輸入 ./autogen.sh,這裡會先告知你需要 autoconf 2.69,但 Ubuntu 12.04 只有 autoconf 2.68。要解決這個問題,得修改 configure.ac 的第一行,把 2.69 改為 2.68,並且重新執行 autoconf。

接著要安裝相依的函式庫標頭檔:libjsoncpp-dev, libcurl4-openssl-dev, libncurses5-dev,這些用 apt-get install 安裝即可。執行 ./configure 可以產生 Makefile,但編譯時會有 unrecognized command line option 「-std=c++11」的錯誤而無法編譯,這是由於 12.04 配的 gcc 版本過舊的關係。針對這問題,我改用 clang 來編譯:./configure CC=clang CXX=clang++  (用 sudo apt-get install clang)。

編譯的過程裡,還是有 PostData({….}) 無法初使化的編譯錯誤,這應該是 C++11 的新寫法,但 clang 不認可,那就只能改寫為比較不酷的寫法。

這樣就可以編譯通過,並產生出執行檔了。但最後執行,輸入完帳號跟密碼,仍會有 Segmentation fault 的問題。

PokerTH 0.4 編譯問題

環境:Ubuntu 12.04

下載 PokerTH 0.4 版來編譯,QT 的系統都是要先打 qmake 來產生 Makefile:

qmake
make

結果會出現錯誤

‘class boost::detail::try_lock_wrapper<boost::timed_mutex>’ has no member named ‘locked’

12.04 有 3 個 libboost-thread 版本:1.46, 1.48, 1.49 ,試過以後都不行。

直接去 /usr/include/boost/thread 看,原來是沒有 locked() method,去 src/core/common/thread.cpp 裡,將使用 locked() 函式的地方修改為 owns_lock() 就可以編譯了。

libchewing 與 mingw

試著要編譯 libchewing,首先下載 libchewing 0.3.4,參考裡面的 README 去搭建編譯環境。

libchewing 是用 mingw 來編譯,所以就請下載 mingw installer 來安裝,安裝時,記得選 C Compiler 與 MSYS Basic System。安裝完成以後,打開命令提示字元,輸入 C:\MinGW\msys\1.0\msys.bat 進入 msys 開發環境,然後輸入 mingw-get install automake autoconf libtool 以安裝 automake, autoconf, libtool

安裝完了,切到 libchewing 解開以後的目錄下,輸入 ./autogen.sh 產生 configure,好了以後,再輸入 ./configure。我在這邊踢到鐵板,./configure 會出現問題,參考 config.log,我認為是 mingw 問題,就試著用一個簡單的 hello.c 來看能不能編譯,結果不行,說 stdio.h 找不到,stdio.h 找不到耶,這實在是太扯了。

試著用 mingw-get 裝上 g++, m4, w32api,mingw32-make …等等的套件,都不行。試著去找 stdio.h,c:\mingw 下能找到的幾個 stdio.h 也都不在 gcc 標頭檔的搜尋路徑裡。百無聊賴之下,就用了 mingw-get update 去更新套件資訊,再用 mingw-get upgrade 更新,很神奇的事發生了,hello.c 編譯通過,也可以找到 stdio.h 了。

回頭再用 ./configure ,就順利產生了 Makefile,再用 make ,libchewing 就編譯成功了!

ANativeWindow::query = query

在FramebufferNativeWindow.cpp裡看到這樣的用法:ANativeWindow::query = query; 回頭看ANativeWindow的定義,query是一個函式指標,那照理來說應該不能這樣指定,應該要在ANativeWindow實體化以後才可以。好吧,我搞迷糊了。

重新追蹤代碼:

  • query 是一個在 FramebufferNativeWindow 裡的 static function
  • FramebufferNativeWindow其實繼承了ANativeWindow,所以在FramebufferNativeWindow裡使用ANativeWindow::query,就是指定父類別的query。

就這兩點看下來,可以使用ANativeWindow::query=query這點,就不會覺得奇怪了。

gcin筆記(2)

看了 gcin 的程式,覺得頗亂,命名跟檔名是有其規則,例如 pp 是指詞音、pho 是注音、gtab 是亂倉打鳥、倉頡…等,不過,對於要新增輸入法,並沒有 interface讓人知道說該實作些什麼。看到這裡有點想放棄了說~
連線的部份是 socket,但 event 的處理則是用 glib 的 io_channel (g_io_add_watch)。
當 client 第一次跟 server 建立連線,是在 cb_new_gcin_clients(),gcin_clients 在 cb_new_gcin_client() 被配置出來,這邊是在超過預先配置好的 gcin_clients 的時候,才以 trealloc() 去重配置。在一開始如果有太頻繁的client過來(仔細想想,好像不太可能),每次都重新配置,是不太有利的。但是如果改用 g_list 或 g_array 來做,感覺上又不是很划算。
server 收到 client request 的點是在 im-srv.cpp 裡的 cb_read_gcin_client(),裏面會去執行 process_client_req()。process_client_req() 在 im-dispatch.cpp 裡,拿到 fd,直接用之前在 cb_new_gcin_client() 預配置的陣列 gcin_clients 去做判斷、處理。裏面會根據 req_no 再去看要呼叫 ProcessKeyPress()/ProcessKeyRelease()…等函數 (在 eve.cpp 裡)。
參考 anthy 日文輸入法,好像比較好,因為同樣也是外部的library
下面是掃過 eve.cpp 以後,得到的一些東西:

  • feedkey_anthy()
  • feedkey_anthy_release()
  • hide_win_anthy()
  • show_win_anthy() => init_in_method() 會用到
  • move_win_anthy()
  • get_win_anthy_geom()
  • anthy_visible() => 給 win_is_visible() 用
  • init_win_anthy() => init_in_method() 會用到
  • anthy_get_preedit()
  • gcin_anthy_reset() => 給 gcin_reset() 用
  • flush_anthy_input => 給 flush_edit_buffer 用

看起來是要實作這些部份。
init_win_anthy(): anthy 是用 dlopen 的方式載入,這樣也對,可以省掉一開始不必要的配置。

gcin筆記

gcin輸入法以 INMD 定義,在 gtab-list.cpp 裡載入,從 gtab.list 載入,裏面決定了檔名、方法…
如果要加新酷音,

  • 在 method_codes[] 裡增加一個項目, !CHEWING
  • gtab.list 裡增加一個新的項目,新酷音 ] !CHEWING chewing.png
  • MAX_GTAB_NUM_KEY 也要再加1
  • load_gtab_list 一開頭把 method_type 寫死了,所以這邊也要加,inmd[16].method_type=method_type_CHEWING
  • 加 method_type_CHEWING

以上從 gcin-setup.cpp 的 cb_default_input_method() 開始追
callback 裡呼叫了 create_gtablist_window(),然後再呼叫 create_model()。從裏面的 add_items() 再看到 load_gtab_list() 而推導的。
從 GtkIMContextClass 的 filter_keypress 開始看: gtk_im_context_gcin_filter_keypress
大致上的架構是這樣子:
gtk_im_context -> gcin client <-> gcin server
eve.cpp 裡的 ProcessKeyPress 是處理 Key 的部份,所以這邊可以看到輸入法的切換、暫時切換為英文、選phrase…等等的處理。

libchewing小記

libchewing的用法挺簡單的,我是先試 python 目錄下的範例,可是即便我把 Init() 裡的目錄改成對的了,還是不行。這邊要傳進去的目錄,要用 libchewing3-data 的安裝路徑,否則Init會出錯。
對照 test/testchewing.c 裡的程式以後,發現沒什麼太大的不同,可是就是不行,反正以後沒有要用 python 來寫,所以就算了。
最後是對照 test/testchewing.c 去寫一個小的程式,就可以運作了,test/testchewing.c 是一個很好的進入點。

Lex 練習

看 lex & yacc 第一章的練習,然後改用 glib 的 GList 來做:

Lex 是用來 tokenize 輸入用的,也就是用來辨識說輸入裡有哪些東西是你要的。這個例子書上沒針對怎麼使用做解說,乍看之下,不容易懂。其實使用上就是打 verb is,表明說 is 是 verb,你可以一直輸入 verb read、verb write、noun book、noun dog….程式就會把你輸入的這些內容分類、放到list裏面去,之後你輸入 read、write、book 時,程式就會告訴你 read、write 是 verb,book 是 noun 。

改用 glib,是因為想練習 glib。g_list_find_custom令人意外地沒範例,不過用法挺簡單,主要變化在第二個參數,第二個參數是一個 function。該 function 第一個參數是 list 裡的元素,第二個參數就是 g_list_find_custom 裡的第二個參數,你可以參考 compare_word,應該是不難懂。
參考資料:

C++ ctor 不能呼叫 virtual function

原本以為可以用上 template pattern 的,可是卻碰上釘子,怎麼編譯就是不能通過,實驗了好一會,才發現 constructor 裡是不能呼叫 virtual function 的,如果不是 constructor 的話,就可以。真的是學到教訓了,果然自己寫的程式太少。