由於母親健康因素小弟決定陪伴在她身旁, 因此今天無法赴 OSDC.tw 會場分享聊天. 造成主辦單位以及與會朋友困擾, 小弟深感抱歉.
“Hadoop Hardware and Network Best Practices” Slidedeck
早上在噗浪上被 jeffhung 抱怨去年十月在 Hadoop in Taiwan 分享時使用的投影片沒放出來.
So, here it is – Hadoop Hardware and Network Best Practices.
[工商服務] Chief Engineer Wanted, Muzik Online
朋友在找一位 web backend 的 Chief Engineer. Package 頗有誠意, 具相關背景與經驗的朋友可以參考一下.
有興趣可以直接投履歷, 需要小弟轉介也別客氣.
In Code Documentation
在 G+ 河道上看到朋友貼了 cldoc 的連結, 也在 G+ 寫下自己對這類從 source code 以及其中的 annotation 產生文件之工具的看法. 本篇文字基本上為該 post 的中文版.
許多年以前, 我曾經大量使用從 source code 導出使用說明文件的工具, 特別是 Doxygen. 正如其首頁所說的 – “Doxygen is the de facto standard tool for generating documentation from annotated C++ sources”.
一段時間後我放棄了繼續使用這些工具. 我發現這類看似讓作者方便邊寫 code 就順便寫寫文件的工具, 實務上並沒有這麼美好.
就我有限經驗裡的觀察, 寫 code 與寫文件的不見得是同一 (批) 人. 由於做這兩種工作需要腦袋處於不同思考模式以及這兩種工作的產出多為軟體開發流程中不同階段所需, 即便有時作者是同一 (批) 人, 也不會在同一段時間一起進行.
有幾年工作經驗的 programmer 常會有這樣的認知; 只要不傷及可讀性與正確性, 手滑時在 code 或 comment 裏面留下的拼字/文法錯誤是無傷大雅的. 這類無礙的錯誤不見得會被修正, 特別是在 release 之後. 而給使用者看的說明文件可視為產品門面之一, 不太可能也照我們 programmer 的習慣得過且過 (誤). 因此, 我認為邊寫 code 邊寫 (給使用者看的) 文件不是一件 practical 的事情.
最糟糕的是把這類工具被用在 library. 當你為了修正拼字錯誤而的動到用來產生文件的 annotation, 它就是一個 revision. 理論上該出新 build 版號也要往下跳. 可是你要選擇把跳版號出新 build 這件事吃掉? 還是選擇對使用者解釋為何你把文件中的 teh 改成 the, 他們就要重新 compile…
IMHO, 使用 wiki 等專注於內容產生與協同作業 (而非排版美編) 的工具來編寫文件, 比用 editor 與 IDE 更有效也輕鬆多了.
Namespace std and Genericity
從 isocpp 看到一則 stackoverflow 上討論 std::initializer_list 的問題, 讓我想起曾打算提筆但一直沒動手的主題.
Swap and ADL
STL 定義的 user-defined type 大都提供了 std::swap(), 一個許多 algorithm 都需要的小 helper. 以 class template std::vector 來說, 長得像這樣:
namespace std
{
template <typanem T, typename Alloc = allocator<T> >
class vector
{
public:
void swap(vector& other);
// ...
};
template <typename T, typename Alloc>
void swap(vector<T, Alloc>& lhs, vector<T, Alloc>& rhs)
{
lhs.swap(rhs); // Based on vector's swap member
}
} // namespace std
C++ 把整個 standard library 都放在 namespace std, 這最大的好處是降低了 naming collision 發生的可能.
namespace LibX
{
struct vector { ... };
void swap(vector&, vector&);
}
#include <vector>
LibX::vector v1, v2;
LibX::swap(v1, v2);
using namespace LibX;
swap(v1, v2); // calls Libx::swap()
同時, 為了避免煩瑣的 fully qualified name (如 line 10) 以及 using directive (如 line 12), C++ 的 Argument Dependent Lookup 讓 compiler 利用我們傳給 function 的參數 幫你挑出對應的 candidate set. 因此, 即便把 line 12 的 using directive 拿掉, 上例依然是無誤的 code. 事實上, 甚至把 line 12 換成 using namespace std; ADL 還是會找到正確的 swap() overload, i.e. LibX::swap(). 只要你 follow Namespaces and the Interface Principle.
So far so good. Continue reading
Global Variables are Safer in C++11
正確的動態起始 global-variable/singleton 是一件苦差事. 直到 C++11 把 thread 及其配套納入標準. 以下是 n3337 (C++1y 第一個版本, 也就是說跟 C++11 官方標準文件之間只有微小差異) 是這麼說的:
An implementation is permitted to perform early initialization of other block-scope variables with static or thread storage duration under the same conditions that an implementation is permitted to statically initialize a variable with static or thread storage duration in namespace scope (3.6.2). Otherwise such a variable is initialized the first time control passes through its declaration; such a variable is considered initialized upon the completion of its initialization. If the initialization exits by throwing an exception, the initialization is not complete, so it will be tried again the next time control enters the declaration. If control enters the declaration concurrently while the variable is being initialized, the concurrent execution shall wait for completion of the initialization
用白話來說就是動態起始的 function static object 在第一次參考到時會被安全的起始, 並且會 block 其他在起始過程中也參考該 object 的 thread. 我的理解是這與使用 pthread_once 保護 function static object 的起始有相同的效果以及語意. 這下子程式簡單多了.
Re: Your Java programs won’t scale: A clear and concise explanation as to why
Just finished reading this two-part article +Nicholas Nezis shared and I am not buying.
If you haven’t read it, it would only take you a few minutes. If you are feeling lazy, it basically made the argument why Java doesn’t scale well by saying threading and locking is how Java scales with multi-processors, and:
“The more processors your throw at a Java program, the more often these data collisions happen, and you eventually hit a point where the whole JVM is simply bogged down in the process of managing and manipulating locks.”
奇碼共賞
昨天晃到一個有趣的網站. 這網站之所以有趣倒不是因為它的內容錯誤百出, 而是在於這網站是個程式教學網站/部落格. 有感在噗浪上寫下:
我對半吊子, 不知所云甚至是自嗨的技術性部落格完全沒有意見. 但是這樣的東西如果打上教學的招牌, 我就會起杜爛. 自己學藝不精真的沒關係, 自己高興就好. 搞到不懂的人也跟著誤入歧途才是罪過.
這網站上面的 example code 很難單獨用人類語言來形容它糟糕的程度, 今天就來個原碼照 po 奇碼共賞. 為了避免對當事人造成過度的傷害, 程式碼註解中的作者資訊已被刪除. 接下來我會展示原封不動的三個屬於同一個範例的 C++ code snap 並在每個 code snap 後接著我的 code review 短評.
首先, 以我有限的理解能力來看, 這程式的重點就兩個:
- 起始一個
Encryptinstance 時, 該 instance 會在內部產生一個由'a'到'z'所組成, 值不重複, 位置隨機 (其實我看不懂這隨機的算法…), 長度為N(i.e. 26) 的字串. 叫作Encrypt::cArray. - 如果要被 “encrypt” 的字串中有
'a'到'z'的 character, 就用Encrypt::cArray[ascii_code(character) - 'a']的值替換. 否則沿用輸入的值. 輸出結果就是一個長度與 input 一樣的字串, 只是其中的'a'到'z'會被用前述方法置換.
先不論這樣的設計與算法有無意義, 裏面的 code 可說是精彩萬分不容錯過, 保證搏君一笑. 讓我們繼續看下去…
Continue reading
A Technique to Avoid Value/Name Mismatch (or One Good Use of Preprocessor)
前一陣子斷斷續續花了好些時間解了一個很沒營養的 bug. 在我的 project 裏面有個 finite-state-machine, 其中有段 code 長得像這樣:
// listing-1
struct state
{
typedef int value_t;
enum {
new_client_wait,
client_handshake_wait,
abc_query_wait,
xyz_connection_wait,
session_established,
session_ended,
nack_wait,
};
};
總之這是某個 server 的 code, 這個 server 在 client 連進來後會先等 client 啟動 handshake 然後再根據內部其他系統的提示與狀態來決定是否 (以及如何) 與 client 建立 session. (其實以上完全不是重點 XD)
為了能在 log 裏面紀錄 state 的轉換, 稍後追加了一個 state 轉字串的 function:
// listing-2
struct state
{
// Same as listing-1
// ...
static std::string str(value_t st)
{
static std::vector<std::string> s =
{
"new_client_wait",
"client_handshake_wait",
"xyz_connection_wait",
"abc_query_wait",
"session_established",
"session_ended",
"nack_wait"
};
return s[st];
}
};
在 log file 裏面就可以看到這樣的訊息:
[timestamp] Transitioning from new_client_wait to client_handshake_wait.
這個方法雖然寫起來麻煩, 倒也似乎堪用. 直到在追某個不相干的問題的時候, 注意到 log 裡面出現異常的 state transition.
Continue reading
Tough Luck Mixing Lambdas with Async I/O using GCC-4.7
Note: This post was originally posted on my G+ account, which is where I read from others and share short posts in English nowadays. Per Andy‘s advise, I decided to crosspost it here.
This is disastrous to some. With gcc-4.7, you can’t pass stateless (i.e. no capture) lambdas to pointers-to-functions.
This mechanism is particularly handy for people working with C-style callbacks, which happens to be the case with most (if not all) asynchronous I/O frameworks written in C. Continue reading