COdE fr3@K

4/19/2006

New Blog Hosting

COdE fr3@K已移到新的service provider上,所有post以及comment都成功轉移.

4/13/2006

CUJ merged with DDJ

這是感傷的一天,因為今晚剛知道,今年三月CUJ已被併入DDJ原本CUJ的首頁已被redirect到DDJ的頁面,顯示這樣的announcement:

    C/C++ Users Journal

    Effective with the March 2006 issue, the C/C++ Users Journal has merged with Dr. Dobb's Journal, which will now feature expanded C and C++ coverage. Past issues of CUJ—including source code—are available.
CUJ停刊了,而DDJ會增加C/C++的篇幅。雖然我也看DDJ,但與CUJ相比,喜愛的程度是無法相提並論的。即使改版,也不會變成另一本CUJ。

這種感覺就像是某天猛然發現最喜歡的coffee shop關門大吉,再也不能在週末把三五好友約在那裡談天說地。

4/12/2006

Free Software vs Open Source

Free SoftwareRichard Stallman於二十多年前離開MIT開始推廣的運動)常被許多人與Open Source(於1998年由Free Software分支出來的運動)混為一談。

Free Software強調的是自由。指的是言論自由(free speech)的自由,而非免費喝到飽(free beer)的自由。這運動主張所有Free Software的使用者有四種自由/權力

  • 不論目的使用Free Software。
  • 研究Free Software如何運作,並可因應使用者需求修改。使用者必須能取得源碼,為達成這自由的一個前提。
  • 重新發布Free Software。讓使用者也能幫助他人。
  • 改進Free Software,對公眾發布修改過的版本,讓整個社群都能受益。使用者必須能取得源碼,為達成這自由的一個前提。
Free Software運動主張的是使用者的自由/權力,而不是使用者行使該自由/權力所需付出代價(或是$$$)。Free Software的作者大可對一個使用者收取高額的源碼發布費用,但不能阻止這使用者免費對大眾發布原始或修改後的源碼。或許跟許多人對Free Software的刻板印象有不小的出入,但這就是Free Software的哲學。至於是否強制使用者將修改過的源碼對外發布,則因不同Free Software license而異。部份Free Software license允許使用者無條件或滿足特定條件下,不對外發布修改後的源碼。

GPL是最為人知的Free Software license之一。概略的說,除了Free Software主張的使用者自由/權利之外,GPL還加上了保護社群及回饋公眾的機制:

  • 一個軟體必須是GPL才能使用GPL的源碼或link GPL的library。這主要是為了限制non-free software不能站在GPL軟體的肩膀上與GPL軟體競爭,並鼓勵其他與GPL相容的Free Software加入GPL社群。
  • 任何人以任何形式發布GPL軟體(包含源碼),就必須將該軟體以GPL授權給所有的人。被授權並不代表可以向發布者要求一份免費的複本,發布者依然可以向你收費。若沒對外發布過,則不需要滿足任何發布源碼的要求。
LGPL可說是GPL的子集。把上列GPL機制的第一項拿掉,再把第二項的GPL替換成LGPL。

不論對Free Software的哲學認同與否,Free Software代表的是一種價值觀,一種信仰。相對的,Open Source運動的訴求則簡單許多 - 就是開放源碼,與使用者的自由/權力無關,有時源碼開放的程度也相對的比Free Software少許多,少到近乎好笑的地步。有個真實故事大概是這樣子的。1998年,有個以所謂Linux為主題的商展,一位知名公司的高級主管被邀請為主講者,會中他說:

我們不可能把我們的產品開放源碼,但我們有可能會對"公司內部"開放源碼,例如對客服部開放.....

聽起來幾近不可思議,對公司內部能算開放源碼!?但其實上述情況卻是符合Open Source運動精神的。也就是要讓除了(原始)開發人員外,能有更多人能看到軟體的源碼,進而找出更多源碼中的問題,提高軟體品質。

Open Source只是一種方法,一種軟體開發的方法。而Free Software是自由的追求,是個社會運動。

4/02/2006

C/C++ Tips - The Marco Assert

Introduction

assert()是個用來debug的macro, 接受一個expression. 如果expression被evalute的結果為整數零, 或能被convert成零(如null pointer), 則會引發assertion failure. Assertion failure會ouput被evluate為零的expression以及其位置, 包括file name, line number(有些compiler也會output function name)至standard error. 緊接著, 發生assertion failure的process會呼叫abort(), 導致process結束(有些compiler會產生core dump). 用以驗證下列precondition:

  • Client code對function的操作, 例如給予的參數合不合規範, 或呼叫相關functions的順序是否正確
  • Function作者的假設

Assertion Failure Output

假設有個在str_util.c的library function:

    size_t string_length (const char* str)
    {
      // Precondition assertion, client input buffer
      // must be a valid string and must not be null!
      assert(str != NULL);
      // ...
    }
      
當assertion failure發生時, user會於standard error(通常就是terminal)看到類似這樣的訊息:
    AppName: str_util.c:245: size_t string_length():
      Assertion `str != NULL' failed.
      
這訊息對user(一個programmer)來說通常沒有多大助益, 除非user剛好熟悉這段發生assertion failure的code, 或是能取得發生assertion failure的source code.
假設user能取得這段source code, 有時就算沒有作者的comment, 也應該能合理堆測作者對這function設下了precondition, 此例的precondition為client code所給用以讀取的buffer(str)不能為NULL(當然, 所有precondition最好是寫在說明文件, 若沒文件最起碼也要有comment).
如果你是這library function的作者, 除了做好說明文件以及在source code裡寫足comment, 還有什麼是你能為使用者做的? 讓這使用者在發生assertion failure時更容易修正client code.

More Verbose Output

Imperfect C++(希望沒記錯)提出了一個小技巧, 用在這個地方特別適用. 把上例做點小小修改:

    size_t string_length (const char* str)
    {
      assert("Precondition failed, client supplied "
        "input buffer must be a valid string and must "
        "not be null" && str != NULL);
      // ...
    }
      
幾乎只是把原來的comment搬到assert的expression中, 就能在assertion failure發生時產生更有用的output:
    AppName: str_util.c:245: size_t string_length():
      Assertion `"Precondition failed, client
      supplied input buffer must be a valid string
      and must not be null" && str != NULL' failed.
      
這樣的訊息對user來說明顯的有用多了, user可以清楚知道client code給了string_length()一個null pointer, 而這違反了該function的precondition. User不需要取得該function的source code(甚至不用參考說明文件)也能知道如何修正這個錯誤.

Macro Side-Effect

Assert是個macro, 即使是經驗老到的人, 有時也會忽略掉若是以-DNDEBUG或是M$VC所謂的Relase Mode編譯, assert不但不會產生任何效果, 給assert的expression更是不會被evaluate. 例:

    int foo ();
    void bar ()
    {
      assert(foo());
    }
      
以-DNDEBUG編譯, foo()不會被呼叫.

Macro Side-Effect, C++ Specific

C++ Standard Library繼承了絕大部份(也許是全部, 但我不確定)C Standard Library. 只是在include standard header時, 由原來的:
    #include <assert.h>
      
變成:
    #include <cassert>
      
而且會把在該header中屬於global namespace的declartion, 移到std這個namespace當中. 但由於macro(errno也是macro)是preprocessor直接替換的, 與namespace無關(compiler才懂namespace). 因此使用起來像是存在於所有namespace當中(這也是盡量別用macro的原因之一).

Scenarios Not to Use Assert

有些情況, 寫library的人會錯誤地使用assert, 如:

    class SomeClass;
    void faux (SomeClass* ptr)
    {
      assert(ptr != NULL);
      // ...
    }
    void foobar (int n)
    {
      assert(n >= 0);
      // ...
    }
      
如果你的code看起來像上例的話, 請像下面這樣, 把interface改一改吧:
    void faux (SomeClass& instance)
    {
      // ...
    }
    void foobar (unsigned int n)
    {
      // ...
    }
      

1/25/2006

Feeding Frenzy

上週, 不來爾 (剛取了老婆, 很幸福喔~) msn jserv的blog給我, 問我有沒有沒有在blog. 過去, 有時因為工作需要寫教材有時是自己興趣, 斷斷續續的寫了不少東西. 有threading, networking等等, 但最多還是C++. 而以我這有時會掉東掉西的個性, 不令人意外地, 現在還找得到的 - 幾乎是零. 一聽到不來爾的提議, 馬上就覺得這是個好主意. 雖沒blog過, 以看過的blog形式而言 (沒吃過豬肉的人還是看過豬走路D), 應該會適合我這主題寫一點那主題寫一點, 更省得寫過又找不到.

找個免費blog hosting自然是再也簡單不過的事了 (just google). 不想特別花時間比較 (花時間寫東西實在些), 直接上了google本家的blogger註冊. 沒寫過html更別說blog的敝人在下我, 本以為用blogger網頁內嵌的What You See is What You Get排版編輯器打打字就了事了, 沒想到麻煩就此開始...

也許因為blog的是技術性主題, 需要標題, 分段, 條列, 內縮, 顯示源碼等功能. 也許是blogger的排版編輯器與firefox相容性不是特別好, 反正怎麼用就是無法排成我想要的效果, 只好動手hard code寫html. 學習需要的排版功能 (說穿了不過是少數的幾個tag), 並不困難. 麻煩的是既然是syndicated content (其實我那時候還不知道這個詞), 當然不能只用web browser看看就沒事了, 不訂閱試試看那怎行!?

馬上向我心中的新玩意達人, 大衛劉 (反正想到nano, 新手機, 新科技之類的新東西馬上就想到他) 請教. 他條理分明地對我開示 (阿彌陀佛~~), 說明什麼是syndicated content, feed, Atom, RSS, feed aggregator. 一開始我用thunderbird當aggregator訂閱COdE fr3@K, 結果是不行. 因為blogger捨RSS採用Atom (blogger當然用自家力推的規格), 但雷鳥似乎不喜歡Atom (至少1.0.7不支援). 在blogger的support (還是FAQ?) 提到如果使用者就是要用RSS feed, 建議使用FeedBuner. 用FeedBuner把COdE fr3@K轉成RSS格式以被訂閱似乎不是問題, 可是有幾篇blog用雷鳥會有不同問題狀況. 在經過以Feed Validator數小時debug/修正後, 剩下一個blog依然有嚴重的問題, 但我已經沒氣力繼續與thunderbird奮戰了. 接下來在mozilla找到個firefox的extension, Wizz RSS News Reader. 不確定是否為設定問題 (沒力fine tune), Wizz顯示的blog entry不是news feed的樣式, 而是blog entry URL指向網頁的內容, 也就是跟直接看網站的內容沒兩樣 (包括有iframe, side bar, 背景等等).

正打算棄訂閱者看feed的權利於不顧時 (事實上也只有幾隻小貓訂閱, 根本就是自己愛玩), 我沒報太大希望地試著用了兩個給news (nntp, Atom, RSS) 專用的aggregator (Liferea以及aKregator). 意外地, 不論是訂閱Atom feed或是RSS feed, 兩者都能完全正常顯示每一篇blog! 現在雖無法以我對news feeding的粗淺認識來論斷到底問題出在那個環節, 但至少有兩個news aggregator能正常訂閱以及顯示COdE fr3@K. 呼~ 讓我能先鬆一口氣, 暫時逃避這令人頭疼的feeding frenzy.

1/23/2006

C++ Terminology - 2. Instance and Instanciation

第一次聽到instance, 是在某個template的論述上, 用來表示template的具現. Template prgramming, 是具有上位性 (相對於傳統programming) 的programming, 也就是所謂的meta programming. 而instance指的就是該上位性的實現. 假設有一個function template:

    template <class T>
    T max (T lhs, T rhs);
    

如果程式中呼叫了兩次max<>, 一次給的arguemnt型別為char, 另一次為int. 則兩次呼叫的function為同一個function template (i.e. max<>) 所具現出來的兩個template function, 一個為max<char>, 另一個為max<int>.

一個常被template programming初學者誤解的是, function template本身並不是實體, 當然也不能被呼叫, 被呼叫的是它的instance (實體, 具現化的結果), 也就是 template function. 同樣地, class template並不能用以生成物件, 用來生成物件的是class template的instance, 也就是template class. 只是這些instance, 不存在code裡, 而是在compile-time自動被產生.

不論是template class, template function或物件被具現化的動作, 都被稱為instanciation (動詞為instanciate), 而具現化的結果都稱為instance. 若要確切說明其為template的具現, 則用template instanciation. 相對於instance, object則較為狹義, 只代傳統物件.

C++ Terminology - 1. Variety of Types

Fundamental Type

    Aka. built-in type, primitive type, native type and etc.
    C++ core language原生支援型別. 包含pointer, reference以及用const或volatile修飾過的型別 (不包含C++ Standard Library定義的型別!), e.g.:

    • float
    • volatile unsigned int
    • const char*
    • double&

Integral Type

    為fundamental type的subset. 代表整數的fundamental type, e.g.:

    • char
    • unsigned short
    • long

User-defined Type

    Aka. class type.
    使用者自訂型別, 也稱為類或類別. 泛指所有的struct與class, 包含C++ Standard Library定義的型別, e.g.:

      class SomeClass;
      struct SomeStruct;
      template <class, class> class basic_string;
    對C++來說, 在定義user-defined type時, class與struct幾乎是沒有差別且能互相替換 (interchangable). 唯一的差別是預設(默認)的存取權限:
      class DerivedClass : /*private*/ BaseClass
      {
      /*private:*/
          int data;
      };
      struct DerivedStruct : /*public*/ BaseStruct
      {
      /*public:*/
          int data;
      };

POD

    Acronym for Plain Old Data.
    User-defined C-style structure. 為user-defined type的subset, 需符合下列條件:

    1. 沒有user-defined constructor, destructor以及assignment operator
    2. 沒有base class type, 換句話說非derive自user-defined type
    3. 如果有data member, 其所有data member必須為fundamental type或POD
    下兩例皆為POD:
      class SomePodType
      {
      public:
          unsigned int data0;
      };
      struct AnotherPodTypeWithDataMember
      {
          SomePodType data1;
          char data2;
      }; 

Template Class

    也是user-defined type的subset. 為Class template的instance (具現), e.g.:

      typedef list<char> IntList;
    上例中list<>為class template, IntList為template class.

Type

    泛指任何fundamental type或user-defined type.

1/22/2006

Naming Notation - 1. the Hungarian Notation

在open source的世界也好, 在公司內部也好, 任何像樣的project, 大都免不了採用或自訂一套coding convention. 不同coding convention所要達成的目標並不盡相同, 但都有個共同點 - 讓你寫的code看起來像是別人寫的. 主要原因是 - 這樣做應該可以讓他人容易閱讀/理解/修改你寫的code.

Coding convention主要分為兩個部份:

  • Naming notation (命名法)
  • Formatting (程式格式/排版)

以我個人經驗以及從朋友處得到的消息來看, 公司內部最常採用的naming notation是Hungarian Notation (匈牙利命名法). Hungarian notation的歷史我不清楚, 但把它傳播到世界各地, 甚至錯誤地使用在例如C++之類的strong-typed (或是接近strong-typed) 語言上, M$ (危軟) 功不可沒, Win32 SDK與MFC是其中經典之作. 時至今日, 就連M$內部也已展開了反撲, 最好的例子就是.NET丟開M$過去採用hungarian notation的包袱.

痛恨hungarian notation, 但這通常不會造成我在工作上的困擾. 我總是能幸運地說服同事與上司放棄它, 或至少不強迫我採用它. 不過還是常會有朋友跟我抱怨公司強迫他們採用hungarian notation, 他們多是無奈接受. 這帖blog, 就是為了這些朋友寫的, 如果你剛好在這些公司工作, 不妨把這帖blog的link轉給你的同事或上司.

Annoyances of the Hungarian Notation:

  • 視你為弱智. 認為你替變數取的名字大都不好, 很難從名字辨認大約的型別
  • 視你為白痴. 認為你有可能會 - 把一個叫做size的變數 (不是integral type還能是什麼?) 誤判為一個ifstream的instance
  • 視strong-typed compiler (如C++編譯器) 為無能. 認為compiler有可能會 - 在你把一個叫name的變數 (不是某種字串還能是什麼?) 當作ComboBox來操作時, 不產生compile error

我常用的命名法則是盡量口語化, 並試著恰如其分地命名. 舉例說明較快:

  • Instance of integral type
    • unsigned long user_count;
      int offset;
      size_t index, received_bytes;
  • User-defined type
    • class Contact;
      class FastString;
      typedef list<Contact> Contacts; // or ContactList
  • Instance of user-defined type
    • string english_name;
      Contacts kens_contacts; // or contacts_of_ken
      Window main_window;
誤判以上所舉例子型別的人, 或許該考慮換個工作:(.

當然, 上例都是淺顯的例子, 從我個人經驗來說, 應用這原則倒也沒遇過特別困難的狀況. 我想blog的並不是我用的方法多好, 而是希望別再有更多人強迫他人或被他人強迫用什麼彆腳的 dwData, wndMain, ctrlBrowser, nCount, szName來寫C++.