前幾天有網友回覆 四則運算解析器 那篇,回頭瞄了一下舊程式碼剛好讓我得到了一個靈感,所以寫了這篇「函數解析器」。
我過去曾用 C++03 實作過一些小型語言的編譯器、直譯器,使用都是比較傳統的方法,也就是設計一個 AST 節點的基礎類別,再特化出各種不同類型的 AST 節點。這個寫法非常的囉唆,許多程式碼都是為了滿足靜態型別語言的規範,而不是實現真正的功能,相較之下 python、javascript 之類的語言可以用精簡許多的程式碼完成同樣的事情。
novus 發表在 痞客邦 留言(1) 人氣(339)
在排序固定長度的小陣列時,那些 big-O 優異的演算法往往討不到便宜,而且還很容易因為多餘的操作而拖慢速度。Sorting Network 就是為了排序固定長度的小陣列而發明的。Sorting Network 是一組事先規劃好的比較、交換操作,只要按照固定步驟操作就能將資料排序。
若一個 Sorting Network 滿足某些條件,就可以將操作步驟平行化或者實作成平行排序硬體,這是這類演算法最大的優勢,不過這不是本文的重點。即使在沒有平行化的情況下,Sorting Network 作為循序執行的排序法效能通常也不錯,至少可以狂電 Bubble、Insertion sort,而且所有的動作都是固定的,可以輕易寫成一連串無迴圈的 if-swap 串,這在「big-O不代表一切」的小資料世界裡具有實作優勢。
novus 發表在 痞客邦 留言(0) 人氣(402)
今天和同事聊到「真搞不懂為什麼某些人那麼容易中毒?」這個話題時,剛好心血來潮,順手示範了一個從前曾拿來整人用的老把戲。
這個把戲就是用 linker 把某個二進制資料嵌入到執行檔中,變成其中一個區段。當然,其中沒什麼高深的學問,只是因為很多人沒有仔細玩過 binutils,不曉得 ld 有這樣的功能。
懶得打字,直接貼程式碼。首先是 Makefile,為了和 MinGW 行為相容,以下所有的執行檔都刻意加上 exe 副檔名,對 Linux 沒影響。(提醒一下,如果要複製去玩的話,別忘了把空白改 tab)
all: wrapper.exe
wrapper.exe: payload.exe wrapper.c
gcc -c -o wrapper.o wrapper.c
ld -Ur -o payload.exe.o -b binary payload.exe
gcc -o wrapper.exe wrapper.o payload.exe.o
payload.exe: payload.c
gcc -o payload.exe -O2 -s payload.c
clean:
rm *.o *.exe
novus 發表在 痞客邦 留言(0) 人氣(310)
今天有位幫一位朋友釐清觀念的時候,發現他對物件的記憶體分配有些誤解。這位朋友告訴我他的資訊來源是這個網頁:
http://ot-note.logdown.com/posts/173174/note-cpp-named-type-convertion
本來我應該直接在原頁面回應,但那個頁面好像必須要弄個啥鬼帳號才能回,所以我還是在這裡說明好了。
主要問題來自 DOWNCAST 標題下的這一段文字
novus 發表在 痞客邦 留言(7) 人氣(3,110)
C++14 都出來了,C++11 仍然有很多東西我沒有好好吸收,主要是因為在現實中沒有什麼使用機會,只是在閒暇時囫圇吞棗般看了一堆文章而已。舉例來說,inline namespace 就是一個我最近才仔細研究使用情境的機制。
C++11 的 inline namespace 主要功能在於,讓撰寫者可以在 namespace 建立抽象層,在底層切換不同版本。
理想化的使用情境是這樣的,假如有一個程式庫叫做 mylib,最初的實作如下:
namespace mylib {
inline namespace v1 {
void foo();
}
}
novus 發表在 痞客邦 留言(0) 人氣(1,433)
之所以會有這篇文章,源於前天發生的一件笨事。我正在為手邊新專案撰寫 CMakeLists,結果在編譯某個 DLL 的時候出現錯誤,主要的訊息是一堆 "Undefined reference to..."。
我對這類的玩意還算蠻有經驗的,快速確定了該連結的東西都有寫到,剩下比較有可能的大概就是連結順序的問題。但這實在不太可能發生,我一向非常留意這些細節。
可是我再三確認 CMakeLists 當中撰寫的順序,卻找不出所以然。不由得讓我有點驚恐,莫非我對 ld 的知識一直有誤?又或者 CMake 會亂調連結順序?
於是我花了大半天的時間用 nm 檢查有關的目的檔內符號名稱是否正確、在 CMakeLists 中亂印 message,最後終於找到禍首。原因是我有一個 CMake function 有名字的引數多了一項,這十之八九是複製貼上造成的,導致於某些情況下 ARGN 會少一項,其他的就不用多說了。簡而言之就是兩個字:手殘,和連結順序完全無關。
novus 發表在 痞客邦 留言(2) 人氣(4,396)
有一個很簡單的需求,一開始我以為對 Boost.Log 這樣功能強大的程式庫應該輕而易舉,結果花了我一點時間才摸出門徑。條件是這樣子的:
必須支援命令列和檔案輸出。
在支援 ANSI color code 的環境中,允許使用者啟用彩色輸出,程式會依照 log record 的 severity level 改變輸出顏色。
若環境不支援 ANSI color code,程式不應該輸出色彩,否則可讀性會慘遭 ANSI color code 破壞。
無論如何檔案都不應該輸出 ANSI color code,理由同上,更何況各家的 log viewer 都已經有自動上色的功能了。
novus 發表在 痞客邦 留言(0) 人氣(648)
大概兩週前有位網友在調查有多少人了解下面這個宣告式:
unsigned int n = -1;
我看到這個問題的時候,已經有不少人實驗得出結果,並且也開始討論這段程式碼的風格問題。以上程式碼的作用在於將 n 所有 bit 設為 1,這是一個非常可靠的行為 -- C 和 C++ 語言標準中對由 signed 到 unsigned 的轉型有段謎語般的詳盡敘述,事實上在採用 2 補數的環境中,所謂轉型的實質行為就只是把位元照搬過去而已(至於採用其他數字系統的平台太罕見,以下不討論)。
功能正確是一回事,關於程式碼的優劣有很多考量。首先,上面這個定義式會引發由 int 至 unsigned int 的自動轉型,有些編譯器在比較高的警告層級下會發出警告。這是一個舉手之勞就可以避掉的警告,任由這類訊息湮沒真正重要的警告是完全沒道理的。
novus 發表在 痞客邦 留言(2) 人氣(3,339)
假設程式需要用到三個演算法,分別是 Foo、Bar、Qwerty,每個演算法可能會有 64-bit 整數、32-bit 整數、SSE2、SSE3 等四種實作方式。
在 32-bit 編譯環境上,通常 32-bit 版本會比 64-bit 版還快,且 SSE 版還會比原生整數更快;反過來說,在 64-bit 編譯環境應當優先使用 64-bit 整數版。基於懶人因素以及現實考量,大部分的演算法在一開始並不會有 SSE 版,只有當現有演算法還有顯著改善空間時,才有足夠的誘因去實作 SSE 版。最後,並非所有平台都支援 SSE,因此必須適時關閉這部份實作。
這就是我遇到的問題。有一個很顯而易見的方案,就是運用條件編譯式插入不同的實作,我之前也是這樣做的。應該很容易想像,當程式碼漸漸擴充,條件編譯式很快就變得難以閱讀。
於是我冒出一個想法,首先用 struct 將演算法實作包裹起來:
novus 發表在 痞客邦 留言(0) 人氣(231)
不熟悉浮點數的人最容易犯的錯誤之一,就是直接用 == 或 != 比較兩個浮點數。以最常見的 IEEE 754 浮點數來說,下面這樣的判斷式竟然不成立:
if (0.1 + 0.2 == 0.3)
原因在於以二進位表示的浮點數並沒有辦法精確儲存 0.1、0.2、0.3 這些十進位實數,只能以最接近的浮點數表示,和原本的數值有微小的誤差。三個各自帶有誤差的數字要碰巧讓整個等式成立,實在是相當困難的一件事。基於同樣的理由,在採用 IEEE 754 的環境下,以下程式片段陷入無窮迴圈也就沒什麼好奇怪的了:
novus 發表在 痞客邦 留言(1) 人氣(14,049)