程式設計師

Document

出來跑的,總是要還~

Design Review

Architect:加油,好嗎?

Coding

寫 Code 是一時的,maintain 是一世的…

Bug

We are extremely fortunate not to know precisely that kind of bug we live in.

Code Review

曾經有一段複製來的 code 擺在我的面前,
但是我沒有 refactor ,
直到新 feature 來的時候才後悔莫及,
如果上天能再給我一次機會的話,
我還是會 copy-and-paste,
如果非要在這分 code 上修改的話,
我希望是…下一版…

Performance

客戶:出來面對~

逛書店

趁著寒流發威,跑去重慶南路逛書店,心想這種冷天氣,應該比較少人出門,沒想到如意算盤打錯,書店滿滿都是人,還看到很多媽媽特地把小孩帶來書店消磨時間,好吧,我以我們是個書香社會為榮(那出版社怎麼一直倒?!)

貨幣崛起 Computer Architecture

一本娛樂用,一本自強不息~

Weekly Report (20091123 ~ 20091127)

每到星期五或星期一,我都會用 outlook 寫下跟這篇文章有著極類似標題的信給主管和同事們,不過同樣內容寫在這邊就會 considered harmful~(這是工程師的冷笑話吧!)


星期二 regular meeting 後,一直感到頭暈,龜毛地我在位子與廁所來回走了四次,爾後才決定要請假回家,心中一直懸在一份即將要截止的 design 上,這是某種程度的任重道遠嗎?還是不見棺材不掉淚?

往診所的路上,寒風颼颼,腦袋倒是熱轉不停,想起有人曾說程式設計師都很樂觀,不確定我是不是這樣,但上禮拜著手一份 POC 程式,打從星期一下手之際,就覺得快做完,當晚下班時審視一下,雖不中亦不遠矣!樂觀持續到星期四,連帶延誤 design 的進度…

咳咳,帶著深深地罪惡感走進問診室,醫生診斷後,說:快感冒了,但是還沒感冒,喉嚨有發炎,趕快吃個藥回去睡覺,對了,這不是流感!走出聽診室,總覺得話中有話,我看起來很像是會主動要求施打流感疫苗的人嗎?


POC 的驗證是大陸的同事負責,這禮拜有很多跟他們講電話的機會。原先覺得都是中文應該很好溝通,沒想到電話那頭,講得又快又有口音,加上一些英文單字,發現不太能習慣,倒是他們似乎很快就習慣我們!official build 聽成 official way、checked build 聽成七個 build … 這一定是 VoIP 掉封包了…


星期五晚上的公司總有一股特別的氣氛,彷彿阿宅都不阿宅了,每個人都有行程,好像再忙,都不能禮拜五加班啊!不過逆向操作的好處就是有股安定感,沒有嘈雜聲、步調也慢了下來,有種喝販賣機咖啡都像是在左岸咖啡館、design doc 也變文學鉅作了…

公車站牌前,過了好幾部新店客運的公車,我都選擇等下一班,明明就十點了,公車上的人卻異常的多,從他們的表情看來,不太像 happy 完後要回家,原來我剛剛並不孤單!等公車總是會胡思亂想:可以的話,希望可以永遠保持清醒!可是為了什麼,我們要保持清醒呢?為了做更多的工作?為了有更多的時間做讓自己快樂的事?什麼是讓自己快樂的事?什麼是人生的意義?渾渾噩噩為什麼就不好?因為渾渾噩噩後,我們會後悔?可是能接受自己大半輩子渾渾噩噩的人,最後又怎麼會後悔呢?


上個禮拜是到部門一年,老闆很 high 地跟我說:開口,你破冬了唷!

我茫茫然然的,總覺得最近有點沒勁~

Invalid Parameter Handler and Security Enhancements in the CRT

在 VC++ 的 CRT 中,除了標準的 CRT 之外,Microsoft 也加入安全性的檢查和報錯機制,許多原有的 functions 都被加上 _s 的後贅字。舉例來說:strcpy_s(), wcscpy_s(), _mbscpy_s() 各別是 strcpy(), wcscpy(), mbscpy() 的 security enhanced 版。這些 Security enhancement 的重點在於:

  1. Parameter validation
  2. Sized buffers
  3. Null termination
  4. Enhanced error reporting
  5. Filesystem security
  6. Windows security
  7. Format string syntax checking

針對上述項目的檢查與 enforcement ,security-enhanced CRT 一旦發現問題,是不會嘗試去修正或繞過的,錯了就是錯了,舉例來說:wcscpy_s() 在 copy 記憶體時,一旦發現 destination buffer 比 source buffer 小,它並不會自動進行 truncation ,參考下面這個從 MSDN 來的例子:

errno_t wcscpy_s(
   wchar_t *strDestination,
   size_t sizeInWords,
   const wchar_t *strSource 
);
strDestination sizeInBytes, sizeInWords strSource Return value Contents of strDestination
NULL any any EINVAL not modified
any any NULL EINVAL not modified
any 0, or too small any ERANGE not modified

從表中可以看到,一旦 destination 和 source 有不正確的配對,那麼就不會對 destination 進行動,自然也就沒有把字串 truncate 後複製過去的結果發生。因此如果 truncation 是期望的結局,那就必須改用 strncpy_s(), _strncpy_s_l(), wcsncpy_s(), _wcsncpy_s_l(), _mbsncpy_s(), _mbsncpy_s_l() 這類原本就有 truncate 語意的函式。所以了,引用一下 MSDN 總結:

... the secure functions do not prevent or correct security errors; rather, they catch errors when they occur. They perform additional checks for error conditions, and in the case of an error, they invoke an error handler (see Parameter Validation).

沒錯,security enhancement 強調的是參數使用前的檢查以及可錯誤發生後的處理,不是幫忙修正錯誤。

Parameter Validation

Parameter validation 機制會檢查傳入 _s functions 的參數,檢查包括:

  1. Checking pointers for NULL
  2. Checking that integers fall into a valid range
  3. Checking that enumeration values are valid
  4. Validate that the buffer is large enough before writing to it

當 CRT 發現 invalid parameter 時,它會呼叫 invalid parameter handler 來處理。 CRT 有預設的 invalid parameter handler ,它會呼叫 Dr. Watson 產生 dump ,然後跳出一個 Error report 程式詢問使用者要不要上傳這個錯誤報告。而程式若是以 debug mode 編譯,則是會跳出 assertion。

drwtsn32

Fig 1. Dr. Watson (不是這個華生?也不是裘德洛?)

WER

Fig 2. Windows Error Reporting 對話方塊

AssertionFig 3. Debug Mode 下跳出的 assertion failed 視窗

Where have all the Exceptions gone?

const wchar_t*  src = L"Hello";
wchar_t         dest[ 5 ];

__try {
    wcscpy_s( dest, _countof( dest ), src );
}
__except ( EXCEPTION_EXECUTE_HANDLER ) {
    // Error handle...
}

在某些程式裡頭會看到像上面那樣的片段程式碼,看起來似乎四平八穩:我們指定了 dest 和其大小,若是 src 長度超過了 dest , wcscpy_s() 透過 exception 報錯,我們就可以透過 SEH 來處理。但程式一執行,ㄜ,好像 crash 了… 不過現在我們已經可以可能的問題是什麼:若是程式沒有置換過 invalid parameter handler ,那麼是丟不出 exception 的,程式的 crash 便是可預期的結果。

Install my own Invalid Parameter Handler

/*!
 * \param[in] expression argument expression
 * \param[in] function   the name of the CRT function that received the invalid argument
 * \param[in] file       file name in the CRT source
 * \param[in] line       line in file
 * \param[in] pReserved  reserved
 */
void _invalid_parameter(
    const wchar_t* expression,
    const wchar_t* function, 
    const wchar_t* file, 
    unsigned int   line,
    uintptr_t      pReserved
);

上述是 invalid parameter handler 的 prototype。我們很容易仿造一個自己的,延續之前 wcscpy_s() 的例子,我們就可以實作一個會丟出 exception 的 handler:

void myInvalidParameterHandler( const wchar_t* expression,
                                const wchar_t* function, 
                                const wchar_t* file, 
                                unsigned int line,
                                uintptr_t reserved )
{
    wcout << L"expr:" << expression << endl
          << L"func: " << function << endl
          << L"file:" << file << endl
          << L"line:" << line << endl;

    RaiseException( 0,          // exception code
                    0,          // continuable exception
                    0, NULL );  // no arguments
}

然後透過 _set_invalid_parameter_handler() 來註冊:

wchar_t   dest[ 5 ];
const wchar_t*  src = L"Hello";

_invalid_parameter_handler oldHandler = _set_invalid_parameter_handler( myInvalidParameterHandler );

_CrtSetReportMode( _CRT_ASSERT, 0 );

__try {
    wcscpy_s( dest, _countof( dest ), src );
}
__except ( EXCEPTION_EXECUTE_HANDLER ) {
    cerr << "exception ... " << GetExceptionCode() << endl;
}

_CrtSetReportMode() 在這裡可以幫助 debug mode 下取消 assertion failed 視窗的跳出。如果對於 wcscpy_s() 的 errno_t 的 error code 念念不忘,則可以修改 SEH 的 filter expression 從 EXCEPTION_EXECUTE_HANDLE 到 EXCEPTION_CONTINUE_EXECUTION。

__try {
    errno_t e = wcscpy_s( dest, _countof( dest ), src );

    if ( EINVAL == e ) {
        // ...
    }
    else if ( ERANGE == e ){
        // ...
    }
}
__except ( EXCEPTION_CONTINUE_EXECUTION ) {
}

PS: EINVAL, ERANGE 這類 pre-defined error code 定義於 ERRNO.H 裡頭。

Reference

  1. MSDN on Security Enhancements in the CRT: http://msdn.microsoft.com/en-us/library/8ef0s5kh.aspx
  2. MSDN on Parameter Validation: http://msdn.microsoft.com/en-us/library/ksazx244.aspx
  3. MSDN on _set_invalid_parameter_handler: http://msdn.microsoft.com/en-us/library/a9yf33zb.aspx
  4. MSDN on errno, _doserrno, _sys_errlist, and _sys_nerr: http://msdn.microsoft.com/en-us/library/t3ayayh1.aspx

Qt 為什麼要有 QScopedPointer 呢?

不久前在 Qt Labs Blogs 上的一篇文章引起了我的注意,文章標題是:Introducing QScopedPointer ,介紹了即將在 Qt 4.6 現身的新成員 — QScopedPointer , Qt 4.6 的 preview 也已經放進了相關的文件。在 C++ 中使用 RAII 的手法是相當常見的,而且也納入標準之中的,或是 boost 也提供了 Smart Pointers library

為什麼 Qt 需要提供自己一套 Smart pointers 呢?

Qt 背後的動機令我感到好奇,一如我在 KAutoPointer: a new autoptr class for QPointer 討論串上看到鄉民 Ingo Klöcker 說的:

... Unfortunately, there appears to be quite some aversion against using boost's pointer template classes (a trivial compile time dependency) in KDE …

而的確,這也引起了一陣討論,下面收集了一些討論來瞭解一下:

  1. QT Labs Blogs:
    1. Introducing QScopedPointer
    2. Count with me: how many smart pointer classes does Qt have
    3. Some more pointing
  2. Mailing list or news
    1. KAutoPointer: a new autoptr class for QPointer
    2. OS news: Introducing QScopedPointer

題外話一下:有沒有興趣猜猜 Qt 提供了幾種 smart pointers 呢?在 Count with me: how many smart pointer classes does Qt have? 一文中列出了八種:

  1. QPointer (4.0)
  2. QSharedDataPointer (4.0)
  3. QExplicitlySharedDataPointer (4.3/4.4)
  4. QtPatternist::AutoPtr (internal class, 4.4)
  5. QSharedPointer (4.5)
  6. QWeakPointer (4.5)
  7. QGuard (internal class, 4.6)
  8. QScopedPointer (4.6)

呵呵(或,科科),是不是很有趣呢?回到正題, Count with me: how many smart pointer classes does Qt have? 一文解釋了為什麼不採用既有的 scoped pointers:

  1. C++0x 尚未通過,其實作也只有在 GCC 4.3 之後以及 MSVC 2010 beta 中才有;
  2. TR1 的實作品不夠;
  3. Boost 呢,ㄜ,Qt 認為它是“un-Qt-ish” API,或者該說是“horrible API”,這句話其實是沒有惡意的,只是想說兩者的命名哲學不同。此外,Qt 與 KDE 對於 binary compatibility 有著嚴格、複雜的要求,若是採用 boost 實作會破壞這相容性。(如果有興趣,可以參考一下 KDE TechBase 上的一篇文章:Policies/Binary Compatibility Issues With C++ ,最近的貢獻者就是 Count with me … 一文的作者: Thiago Macieira。)
  4. Qt 還是希望握有 scoped pointer 的 knowledge。

在我看來, Qt 期許自己成為一個成熟易用又跨平台的函式庫,它的支援環境可能沒有完整的標準 C++ 函式庫,又因為 C++ 缺乏統一的 ABI 支援,在某些應用上若沒有一些事先規範會讓事情發展到某種程度後窒礙難行,至於有多窒礙難行呢?我還不能說個所以然,因為自己沒有真的痛過,無法把那種罄竹難書講得感同身受 Orz …

延伸閱讀:

一些關於 ABI 、binary compatibility、modular mechanism 的討論:

  1. Qt Labs Blogs: Some thoughts on calling convention
  2. Qt Labs Blogs: Some thoughts on binary compatibility
  3. Imperfect C++: Part 2 - Surviving The Real World
  4. Essential COM: COM as a Better C++
  5. C++0x 有過相關的討論:
    1. Modules in C++
    2. Draft Proposal for Dynamic Libraries in C++
    3. 不過被打槍了!

Qt 4.5.2

自從 Qt 被 Nokia 併購後,似乎越來越活耀(?!),網站也改版了,不過以前的都會把 source code 版本直接放一個 link 出來,現在好像得去 ftp 下載,

Qt_FTP

編譯

官方的 binary 好像(不確定,我只試過 Qt SDK: Complete Development Environment 這包而已)只有給 mingw32 用的,如果想在 Windows 搭配 Visual C++ 使用,可以自己 compile ,請下載 qt-win-opensource-src-4.5.2.zip 這個檔案,不要下載 qt-all-opensource-src-4.5.2.tar.gz,編譯時可能會有下面的錯誤:

qscriptdebuggercodefinderwidgetinterface.cpp
debugging\qscriptdebuggercodefinderwidgetinterface.cpp(43) : fatal error C1083:
Cannot open include file: 'qscriptdebuggercodefinderwidgetinterface_p_p.h': No s
uch file or directory
qscriptdebuggercodefinderwidget.cpp
debugging\qscriptdebuggercodefinderwidget.cpp(43) : fatal error C1083: Cannot op
en include file: 'qscriptdebuggercodefinderwidgetinterface_p_p.h': No such file
or directory
qscriptdebugoutputwidgetinterface.cpp
qscriptdebugoutputwidget.cpp
qscriptbreakpointswidgetinterface.cpp
qscriptbreakpointswidget.cpp
qscriptbreakpointsmodel.cpp
qscripterrorlogwidgetinterface.cpp
qscripterrorlogwidget.cpp
qscriptenginedebugger.cpp
Generating Code...
NMAKE : fatal error U1077: '"C:\Program Files\Microsoft Visual Studio 8\VC\BIN\c
l.EXE"' : return code '0x2'
Stop.
NMAKE : fatal error U1077: '"C:\Program Files\Microsoft Visual Studio 8\VC\BIN\n
make.exe"' : return code '0x2'
Stop.
NMAKE : fatal error U1077: 'cd' : return code '0x2'
Stop.

而且編一次都要很久,禁不起這種錯誤打擊的!編譯可以使用這個指令,是我試過 ok 的:

configure -debug-and-release -no-sql-sqlite -no-qt3support -no-opengl -platform win32-msvc2005 -no-libtiff -no-dbus -no-phonon -no-phonon-backend -no-webkit 

其中最重要的是要指定 -debug-and-release ,因為預設是不會產生 debug 版本,有時會有 debug 或是 trace 上的困擾。如果電腦上安裝多個開發環境,請透過 -platform win32-msvc2005 指定 toolchain

nmake 後就是看 CPU 運動了!不過 vc 似乎無法吃光四核心 + 4G Ram 的效能 :(

好多 nmake

Qt_nmake

一個 cl

Qt_cl

吃不了什麼 CPU 和 RAM

Qt_CPU_Usage

The C++0x "Remove Concepts" Decision

正如 COdEfr3@K 所說:大件事!花了點時間 survey 一些網站~

  1. 大師說話了
    1. Bjarne Stroustrup: The C++0x “Remove Concepts” Decision
    2. Doug Gregor: What Happened in Frankfurt? (由 COdEfr3@K 補充)
    3. Herb Sutter: Trip Report: Exit Concepts, Final ISO C++ Draft C++ Draft in ~18 Months
    4. Bjarne Stroustrup Expounds on Concepts and the Future of C++ 
    5. Danny Kalev:
      1. The Removal of Concepts From C++0x
      2. The Rise and Fall of C++0x Concepts
      3. The Future of C++ 0x, Part 1
    6. The View (or trip report) from the July 2009 C++ Standard meeting
    7. The View (or trip report) from the July 2009 C++ Standard meeting (Part 2)
    8. Concepts Removed from C++0x
  2. 鄉民的討論
    1. Concepts Get Voted Off The C++ 0x Island
    2. The Removal of Concepts From C++0x
    3. TopLanguage: {C++} concept给毙了:(
  3. 翻譯~ 
    1. Concept 的道路
    2. Concept 失敗的原因

下面是從 DDJ 翻譯的,讓 Bjarne 說說看整個事件吧 : )

The C++0x "Remove Concepts" Decision

在 2009 七月的德國法蘭克福會議上, C++ Standards Committee 決定將 concepts 移出 C++0x。雖然這對那些致力於 concepts 多年以及熟知 Concepts 潛力的人們來說是個沈重的打擊,但幸運地,移除 concepts 對大多數的程式設計師並不太有直接的影響。對於現實世界的軟體開發, C++0x 和 C++98 相比,仍舊是個更極具表達力(expressive)和更有效用的(effective)語言。 Committee 對於 remove concepts 的用意:在於希望能限制它帶能的風險與確保整個時程(The committee acted with the intent to limit risk and preserve schedule),或許更好的 concepts 會在未來五年後出現。這篇文章將簡述移除 concepts 的理由以及 committee 做出此決定背後的的考量,此外也讓大家知道 C++ 的天還沒有塌下來啊~(儘管大把大把的謠言剛好說得相反!)

No "Concepts" in C++0x

2009 七月 ISO C++ Standards Committee (WG21) 的法蘭克福會議上,在討論 concepts 應用在 specifying requirements for template arguments 時被"脫鉤(decoupled)"了,對於我來說,一個比較不那麼外交的用語是被"猛烈扯開(yanked out)"了,這代表 concepts 不會出現 C++0x 或是它的 library 裡頭,在我看來,這是 C++ 的一大挫敗,但不是災難一場(disaster);一些替代方法都只會更糟。(That -- in my opinion -- is a major setback for C++, but not a disaster; and some alternatives were even worse)

我在 concepts 已經努力超過七年了,而花了更甚於此的時間在解決這類問題。許多對於 concepts 的工作已經非常久了,舉例來說(按年代排序):

唉,這不用說,我跟其他人對於這件事感到相當失望,而事實上,一些替代方案都只是更糟、於事無補的,而我也無法提供快速、便利的解法。

請注意到 C++0x 對於 C++ features 的改善與進步對大多數程式設計師看到和使用到的是沒有影響的。C++0x 與 C++98 相比,將仍舊是更具表達力的(expressive),它支援了:

  • Concurrent programming
  • A better standard library
  • 許多的改善使得寫出好程式碼更加容易、簡單(像是更有效率、更好維護)

尤其,對於那些我給的 C++0x 範例,要是沒用到 concepts 或是 requires 的更是不受影響(像是在 Evolving a language in and for the real world: C++ 1991-2006 at ACM HOPL-III )。請參考我寫的 C++0x FAQ,一些人們甚至對於 C++0x 比他們想像中的還簡單而感到歡欣。

Concepts 曾經是 C++0x 對於 template 有更好的理論基礎的重要核心 feature,像是說:

  • 提供更加穩固的 standard library specification
  • 扮演讓泛型程式設計(generic programming)成為主流使用的核心角色

現在,人們將在沒有語言的直接支援下使用 concepts。我對於未來最好的想像(scenario)是:我們在五年後的 C++ 裡頭得到了比現有 concepts 更好的 concepts。要能得到這樣的成果將會需要許多人非常專注的研究(但可不是"design by committee"喔!)。

What Happened?

Concepts 在過去幾年被發展出來,並在 2008 年,經過某些技術上的妥協後,進入到了 C++0x working paper 裡頭(妥協是自然而且必須的)。在實驗性的實作中已經足以應付"conceptualized" standard library,但還不到產品品質(but was not production quality),但我個人認為已經足夠稱為 proof of concept (POC)了!

(此段怕誤導、失真,段落後附有原文節錄)

我跟幾位 members 對於 concepts 有著同樣的考量:

Concepts 的設計,尤其是可用性(usability)這點,是否可為一般普通的程式設計師所掌握

會這麼說,是因為這決定了 concepts 設計的目標:

使 generic progarmming 可被大多數的程式設計師接受[BS&GDR2003]

但這目標在我現在看來已經做了極大的妥協:

Concepts 已經變成了(只存在)專家手上的工具,而不是一個讓 generic programming 更加可被接受的工具了。

過去一年半裡,我試著從使用者的角度去檢驗 C++0x,我擔心即用了 concepts 加持的 libraries 仍然對於程式設計師設下了一道新門檻。我覺得 concepts 的設計和它在 standard library 的使用並沒有充分反應我們過去幾年對於 concepts 的經驗。

「原文節錄」:My concern was with the design of "concepts" and in particular with the usability of "concepts" in the hands of "average programmers." That concern was shared by several members. The stated aim of "concepts" was to make generic programming more accessible to most programmers [BS&GDR2003], but that aim seemed to me to have been seriously compromised: Rather than making generic programming more accessible, "concepts" were becoming yet another tool in the hands of experts (only). Over the last half year or so, I had been examining C++0x from a user's point of view, and I worried that even use of libraries implemented using "concepts" would put new burdens on programmers. I felt that the design of "concepts" and its use in the standard library did not adequately reflect our experience with "concepts" over the last few years.

接著,幾個月以前,Alisdair Meredith (一位極具洞察力的 UK committee)和 Howard Hinnant (the head of the standard library working group)問了幾個很好的問題,問題是關於:

  • 誰應該直接使用到 concepts、
  • 使用 concepts 哪個部份、
  • 如何使用

「原文節錄」:who should directly use which parts of the "concepts" facilities and how

這問題導引出各方考量以及不同角度出發對於 usability 的討論。最後,在經過多次令人混淆的討論後,我把我的結論放在 [BS2009]。

幾個可能有點過度簡化的結論:

  • 現行定義的 concepts 難以使用,並將導致 concepts 不被使用(disuse),這可能是 template 不被使用或是 C++0x 不被公開採納。
  • 一些簡化方法[BS2009]仍舊可以描述 concepts ,使得 C++0x 可以出貨或是僅須稍稍的修改。

請注意到:這只是個非常簡略、囫圇吞棗的結論,standards committee 在討論時是非常有 polite 的,我們有著同樣的目標,總是避免直接的針鋒相對、對質詢問。不幸的是,當時的(內部)討論實在過於龐雜(許許多多牽涉到細節或沒有牽涉到細節的訊息)、令人困惑,在對於問題的定位及如何處理沒有浮現共識下,我在法蘭克福會議裡頭報告了幾項替代方案:

  • "fix and ship"
    • Remaining work: remove explicit "concepts," add explicit refinement, add "concept"/type matching, handle "concept" map scope problems
    • Risks: no implementation, complexity of description
    • Schedule: no change or one meeting
  • "Yank and ship"
    • Remaining work: yank (core and standard library)
    • Risks: old template problems remain, disappointment in "progressive" community ("seven year's work down the drain")
    • Schedule: five years to "concepts" (complete redesign needed) or never
  • "Status quo"
    • Remaining work: details
    • Risks: unacceptable programming model, complexity of description (alternative view: none)
    • Schedule: no change

我和一些其他的人贊同第一個方案("fix and ship")並認為這是可行的。然而,大多數的 committee 不是這麼認為,他們選擇了第二個方案("yank and ship," renaming it "decoupling")。在我看來兩者都比第三個方案("status quo")好,我對於投票結果的解讀是這樣的:(這段怕有情緒性字眼,也保留原文)

  • 對於 concepts 的支持者而言,他們沒有一個共識,整個想法看起來太過爭議
  • 一些人則是擔心 C++0x 雄心勃勃的時程表開始(ㄜ,在我不公正地看來,開始指責 concepts)
  • 一些人則是從頭到尾對於 concepts 就是興致缺缺

「原文節錄」:My interpretation of that vote is that given the disagreement among proponents of "concepts," the whole idea seemed controversial to some, some were already worried about the ambitious schedule for C++0x (and, unfairly IMO, blamed "concepts"), and some were never enthusiastic about "concepts."

有鑑於此,"fixing concepts"不再是一個迫於現實的選則了,本質來說,所有對於 concepts 的支持都將是更久之後的事("later" and "eventually")。我對於現在把 concepts 移除這件事也提出了會有無可避免的延遲的警告, 因為在沒有時程的壓力下,所有的設計抉擇都將被重新評估(譯注:我想是對於 concepts 的設計抉擇)。

「原文節錄」:I warned that a long delay was inevitable if we removed "concepts" now because in the absence of schedule pressures, essentially all design decisions will be re-evaluated.

令人驚訝的(或許吧),在法蘭克福的會議上沒有關於 concepts 的技術報告和討論。整個討論都只著重在時間問題上,我想這投票結果主要反應時間考量吧~

請不要譴責 committee 的謹慎,這不是 "Bjarne vs. the committee fight", 而是試著在大多數人的考量中取得平衡點。我跟其他人會失望,是因為我們沒有抓住"fix and ship"這個機會,而且 C++ 不是一個學院派、實驗性質的語言,除非成員們相信這麼做對於程式碼撰寫上帶來的風險非常低,否則他們就得、也必須拒絕接受它。整體來說,committee 代表的是數十億行程式碼,舉例來說,如果 C++0x 無法被公開採納或是無法在有 concepts 的情況下讓 unconstrained templates 得到長期的使用,C++ 社群將會分崩離析。因此,一個很虛(poor)的 concepts 設計可能會比沒有 concepts 帶來更糟的後果。兩難之間,我也選擇了移除 concepts ,我寧願選擇挫敗也不要一個災難來臨。

Technical Issues

懸而未解的 concepts,其問題在於,當把焦點放到 distinction between explicit and implicit "concept" maps 時(參考 [BS2009]):

  1. Should a type that meets the requirements of a "concept" automatically be accepted where the "concept" is required (e.g. should a type X that provides +, -, *, and / with suitable parameters automatically match a "concept" C that requires the usual arithmetic operations with suitable parameters) or should an additional explicit statement (a "concept" map from X to C) that a match is intentional be required? (My answer: Use automatic match in almost all cases).
  2. Should there be a choice between automatic and explicit "concepts" and should a designer of a "concept" be able to force every user to follow his choice? (My answer: All "concepts" should be automatic).
  3. Should a type X that provides a member operation X::begin() be considered a match for a "concept" C<T> that requires a function begin(T) or should a user supply a "concept" map from T to C? An example is std::vector and std::Range. (My answer: It should match).

我的建議和在"status quo before Frankfurt" 提到的解法(answer)是完全不同的。很明顯地,在這裡我必須忽略大部份的細節和設計理念、簡化一下我的解釋。

我無法在這裡重現整個技術討論,但是下面是我的結論:在 "status quo" 的設計裡頭, "concept" maps 被用來做到兩件事:

  • To map types to "concepts" by adding/mapping attributes and
  • To assert that a type matches a "concept."

不知何故(Somehow),後者未被視作不幸的、少見的必要性,反而被視為基本的功能。當兩個 concepts 有著語意上的差別時,我們需要的不是一個用來說明這個 type 符合這個 concept 但不符合那個 concept 的 assertion (這充其量,只是種 workaround,一種不直接接、用說明回擊問題本身的手法,原文:this is, at best, a workaround -- an indirect and elaborate attack on the fundamental problem),而是一個 assertion 可以說明這 type 帶有與這 concept 相同的語意,但沒有另外一個  concept 的語意。(fulfills the axiom(s) of the one and not the other "concept")

「原文節錄」:Somehow, the latter came to be seen an essential function by some people, rather than an unfortunate rare necessity. When two "concepts" differ semantically, what is needed is not an assertion that a type meets one and not the other "concept" (this is, at best, a workaround -- an indirect and elaborate attack on the fundamental problem), but an assertion that a type has the semantics of the one and not the other "concepts" (fulfills the axiom(s) of the one and not the other "concept").

舉例來說, STL 的 input iterator 和 forward iterator 有著關鍵性的語意差別:

You can traverse a sequence defined by forward iterators twice, but not a sequence defined by input iterators;

像是,在 input stream 上施行 multi-pass algorithm 不是個好主意!在 "status quo" 解法中,會強迫每個使用者說明:

What types match a forward iterator and what types match an input iterator

而我的建議的解決方式是:

若且唯若(If (and only if))

  1. 你想使用的語意並不是兩個 concepts 的共同部份,
  2. 而且 compiler 無法幫你推導出哪個 concept 才是較好的 metch 時,

你必須說明 type 的語意,像是說:

"my type supports multi-pass semantics."

其他人則可能是這麼說:

"When all you have is a 'concept' map, everything looks like needing a type/'concept' assertion."

在法蘭克福的會議上,我是這樣做結論的:

  • Why do we want "concepts"?
    • To make requirement on types used as template arguments explicit
      • Precise documentation
      • Better error messages
      • Overloading

不同的人們有不同的出發點以及排出不同的優先順序,然而,站在這高階的角度來看,可能會有些混淆,但應該是極少或不相衝突的。每個合理的、才走一半的 concept design 都提供了這些特性。

  • What concerns do people have?
    • Programmability
    • Complexity of formal specification
    • Compile time
    • Run time

我個人關注的是 programmability (ease of use, generality, teachability, scalability) ,其次是正式規格書的 complexity (40 頁的標準文件),再其他則關於 compiler time 和 run time 的考量。然而,我覺得實驗性質的實作(ConceptGCC [Gregor2006])中,constrained templates (使用 concepts)的 run time 可以做的跟 nonconstrained templates 一樣好、甚至更好。ConceptGCC 實際上非常慢,但我並未將它視為根本考量,當它用來驗證一個想法時,我們陷入了傳統上的兩難。這裡描述一下有點過度簡化的兩難問題:

  • "Don't standardize without commercial implementation"
  • "Major implementers do not implement without a standard"

各方面來說,細節設計和實驗實作構成了基本的妥協。

我對 concepts 的原則是:

  • Duck typing
    • The key to the success of templates for GP (compared to OO with interfaces and more)
  • Substitutability
    • Never call a function with a stronger precondition than is "guaranteed"
  • "Accidental match" is a minor problem
    • Not in the top 100 problems

我對於 concepts 的"minimal fixes"寫在 pre-Frankfurt working paper:

  • "Concepts" are implicit/auto
    • To make duck typing the rule
  • Explicit refinement
    • To handle substitutability problems
  • General scoping of "concept" maps
    • To minimize "implementation leakage"
  • Simple type/"concept" matching
    • To make vector a range without redundant "concept" map

細節請參考 [BS2009]。

No C++0x, Long Live C++1x

即使在 concepts 已經被移除後,下一版的 C++ standard 仍可能延遲推出。悲慘地說,將不會有 C++0x 了(除非你把 C++0x 這個小改版算進去)。我們一定得等到 C++1x,希望這個 x 是個很小的數字。希望還是在的,因為 C++1x 已經 feature complete 了(除非標準對於一些在 formal proposal for the standard 還有所堅持)。 "所有" 剩下、懸而未決的都只是龐雜的技術問題和意見了。

在我的 C++0x FAQ 中你可以看到一些 features 和討論,這裡僅是列出部份:

和 libraries:

  • Improvements to algorithms
  • containers
  • duration and time_point
  • function and bind
  • forward_list a singly-liked list
  • future and promise
  • garbage collection ABI
  • hash_tables; see unordered_map
  • metaprogramming and type traits
  • random number generators
  • regex a regular expression library
  • scoped allocators
  • smart pointers; see shared_ptr, weak_ptr, and unique_ptr
  • threads
  • atomic operations
  • tuple

    即使沒有了 concepts,C++1x 仍將會是個基於 C++98 大量改善的版本,尤其當你注意到這些 features 都讓你可以在最大的表達力(expressiveness)和彈性(flexibility)間的互動的。我希望我們會在新版的 C++ (或許五年後)中看到 concepts ,也或許我們應該稱呼 C++1y 或是 C++y!

    心得

    該看書了 >__<

  • How Lord of The Rings Should Have Ended

    讓我想到小時候寫作文應該要:

    1. 小題大做
    2. 大題小做
    3. 不要做作

    Boost.Program_options – Tutorial – Option Details and Multiples Sources

    Vocabulary

    1. Program options, options
    2. pair. (name, value)
    3. Token
    4. Position options
    5. Composing options

    Option Details

    沒錯,你得到它了!option value 不僅只侷限於 int 型別,此外還可以有其它下面的屬性(proerties)。完整程式可以在

    example/options_description.cpp

    找到。

    想像你在寫一個 compiler ,你可以設定它的 options:

    1. Otimization level
    2. Include paths
    3. Input files
    4. 其它做些有趣的事。

    下面的程式碼試著描述這些 options :

    int opt;
    po::options_description desc("Allowed options");
    desc.add_options()
        ("help", "produce help message")
        ("optimization", po::value<int>(&opt)->default_value(10), 
      "optimization level")
        ("include-path,I", po::value< vector<string> >(), 
      "include path")
        ("input-file", po::value< vector<string> >(), "input file")
    ;
    1. help:就像上個範例一樣。最好讓每個 case 都有自己的 help option。
    2. optimization:這裡的 optimization 有兩個特點:
      1. 我們在程式裡頭指定了變數的位址(&opt)。如此一來,當 values 被分析儲存後,我們就可以透過該變數取得 optimization 的 value。
      2. 我們指定了變數的 default value 為 10 。因此,當使用者沒有給 optimization 指定一個 value 時, value 會是 10。
    3. include-path:include-path 在這裡是一個特殊的例子,它只從 command line 讀入資料來使用 options_description。(原文是:The "include-path" option is an example of the only case where the interface of the options_description class serves only one source -- the command line.  看了上下文,總覺得這句有點詭異、跳 tone)。對於常用到的 options,使用者往往喜歡使用它們的 short option name 形式,而這也就是程式碼裡頭 "include-path,I" option name 想表達的意思:"I" 是 include-path 的 short name。
      另外,值得注意的是 "include-path" 的型別是 std::vector。Boost.Program_options 對於 vectors 有特別的支援,它可以多次出現在 option 輸入中,而所有的 values 都會被存放在一個 vector 裡頭。
    4. input-file:描述了一連串、將被處理的檔案。當然,一開始的時候像下面的方式指定 input file 是可以的:
      compiler --input-file=a.cpp
      但,如果和下面的方法相比,上面的方式似乎就有點不標準囉!
      compiler a.cpp

      我們將針對這問題來探討一下:

    像 a.cpp 這種在 parsing 的時候找不到 option name 的 token 的,在 Boost.Program_options 裡頭稱為 positional options。透過一些使用者的幫助,Boost.Program_options 有辦法可以知道 a.cpp 實際上就是 "--input-file=a.cpp" ,下面是妳/你需要的額外程式碼:

    po::positional_options_description p;
    p.add("input-file", -1);
    
    po::variables_map vm;
    po::store(po::command_line_parser(ac, av).
              options(desc).positional(p).run(), vm);
    po::notify(vm);

    開始兩行程式碼的意思是:所有的 positional options 需要被轉會成 "input-file" options。此外,這邊我們使用 command_line_parser 類別,而不是 parse_command_line function。parse_command_line 是一個只在簡單情況下會用到的 wrapper function 而已,但我們現在需要更多的資訊來做正確分析。

    現在,所有的 options 都已經被描述和分析了,我們就饒了自己吧,不要去管怎麼實作 compiler 的邏輯、細節,先用 print 的方式來表示各個 options。

    if (vm.count("include-path"))
    {
        cout << "Include paths are: " 
             << vm["include-path"].as< vector<string> >() << "\n";
    }
    
    if (vm.count("input-file"))
    {
        cout << "Input files are: " 
             << vm["input-file"].as< vector<string> >() << "\n";
    }
    
    cout << "Optimization level is " << opt << "\n";     

    下面是個編譯、執行這個範例的方法:

    $bin/gcc/debug/options_description --help
    Usage: options_description [options]
    Allowed options:
      --help                 : produce help message
      --optimization arg     : optimization level
      -I [ --include-path ] arg : include path
      --input-file arg       : input file
    $bin/gcc/debug/options_description
    Optimization level is 10
    $bin/gcc/debug/options_description --optimization 4 -I foo a.cpp
    Include paths are: foo
    Input files are: a.cpp
    Optimization level is 4

    歐喔,還有一個小問題!我們還是可以使用"input-file"來指定,而 Usage 也是這樣寫的,這可能會混淆了我們的使用者,如果能把這資訊隱藏應該會不錯,不過就留到下一個例子吧。

    Multiptles Sources

    很快地,妳/你就會發現都得把所有的 compiler options 填好是一件令人抓狂的事。想想看,難道妳/你希望使用者裝了一個新的 library 後每次使用妳/你的 compiler 都得一個一個把 options 填好嗎?如果能她/他決定了一些選項是每次都要使用到的呢?最好的情況是產生一個 common settings 然後搭配 command line 來使用。

    當然,把 config file 跟 command line 搭配使用是有必要的。舉例來說:config file 的 optimization level 會被 command line 的輸入蓋過;然而 include path 卻是需要有累加效果的。

    來看個 code 先,完整的程式可以在

    example/multiple_sources.cpp

    找到。option 定義有兩個有趣的點:

    1. 我們宣告了多個 options_descriptions 實體(instances)。為什麼呢?因為不是所有的 options 都是相近的,有些 options,像是 input-file 不應該出現在自動化 help message 裡頭;有些 options 只在 config files 裡頭才有意義。
    2. 最後,讓 help message 有點結構化、而不只是串列表是不錯的主意。

    下面讓我們宣告幾個 option groups 吧:

    // Declare a group of options that will be 
    // allowed only on command line
    po::options_description generic("Generic options");
    generic.add_options()
        ("version,v", "print version string")
        ("help", "produce help message")    
        ;
        
    // Declare a group of options that will be 
    // allowed both on command line and in
    // config file
    po::options_description config("Configuration");
    config.add_options()
        ("optimization", po::value<int>(&opt)->default_value(10), 
              "optimization level")
        ("include-path,I", 
             po::value< vector<string> >()->composing(), 
             "include path")
        ;
    
    // Hidden options, will be allowed both on command line and
    // in config file, but will not be shown to the user.
    po::options_description hidden("Hidden options");
    hidden.add_options()
        ("input-file", po::value< vector<string> >(), "input file")
        ;        

    請注意到宣告 include-path option 時呼叫的 composing method,這告訴了 library 不同來源的 values 應該被組合在一起(composed together)。

    我們可以用 options_description 的 add method 來進一步的組織 option groups 中:

    po::options_description cmdline_options;
    cmdline_options.add(generic).add(config).add(hidden);
    
    po::options_description config_file_options;
    config_file_options.add(config).add(hidden);
    
    po::options_description visible("Allowed options");
    visible.add(generic).add(config);

    分析和儲存 values 跟以往的作法相同,除了我們得額外呼叫了 parse_config_file 和呼叫 store 兩次之外。如果有些 value 在 command line 和 config file 中都出現了,那會發生什麼事?通常來說,先儲存下來的 value 會被視為優先,這也是 -optimization option 的設計,而對於 composing options 來說(像是 include-file ),這些 values 則是會被合併。

    下面是個編譯、執行這個範例的方法:

    $bin/gcc/debug/multiple_sources
    Include paths are: /opt
    Optimization level is 1
    $bin/gcc/debug/multiple_sources --help
    Allows options:
    
    Generic options:
      -v [ --version ]       : print version string
      --help                 : produce help message
    
    Configuration:
      --optimization n       : optimization level
      -I [ --include-path ] path : include path
    
    $bin/gcc/debug/multiple_sources --optimization=4 -I foo a.cpp b.cpp
    Include paths are: foo /opt
    Input files are: a.cpp b.cpp
    Optimization level is 4

    第一個呼叫是使用 config file。第二個則多使用了 command line 的輸入。正如我們所看到的,include path 的 value 是從 command line 和 config file 合併來的,而 optimization 則是從 command line 取得。

    Reference

    1. Boost 1.39.0 Doc: Chapter 13. Boost.Program_options

    See Also

    1. Boost.Program_options – Introduction and Tutorial 

    Boost.Program_options – Introduction and Tutorial

    最近因為需要一些自動化的程式看了 Boost.Program_options 這個 library ,這系列文章主要內容都是翻譯Boost 1.39.0 Doc 裡頭,希望還有點時間可以加上一些 trace 的心得和紀錄,希望沒有誤人子弟、教壞仔大小、對大家有幫助~

    Vocabulary

    1. Program options, options
    2. Config file
    3. Command line
    4. pair. (name, value)

    Introduction

    Boost.Program_options 是個可以讓程式設計師獲得 program options 的函式庫,什麼是 program options 呢?就是那種由使用者提供,可能是 command line 或是 config file 的 (name, value) 組合(pairs)

    那為什麼需要使用 Boost.Program_options 呢?為什麼說它比我們依靠直覺寫出來的程式碼還好呢?

    • 簡單。Boost.Program_options 很小、提供的新增 options 的語法也簡單。像是自動化的 option value 的型別轉換(type conversion)、變數存放。
    • 比較好的錯誤回報。 當分析(parsing)command line input 出錯時,Boost.Program_options 會回報所有的錯誤,但 hand-written code 可能會誤判。
    • Options 可以來自任何地方。當使用者可能對你的程式只能藉由 command line 輸入感到不滿,妳/你很快就會用到 config file 或是 environment variables 了。這些都可以幾乎無痛得透過 Boost.Program_options 來擴充。

    Tutorial

    在這個 section ,我們將從最簡單的地方開始,之後逐漸知道大部分 Boost.Program_options 的 usage scenarios 。這部份的文件都只有部份的、重要的程式碼,完整的範例可以在:

    BOOST_ROOT/libs/program_options/example

    資料夾找到。此外整個範例,都假設下面的 namesapce alias 是有效的!

    namespace po = boost::program_options

    Getting Started

    第一個也可能是最簡單的一個範例是:

    處理兩個 options

    下面是程式碼部份(完整程式可以在 example/first.cpp 找到)

    // Declare the supported options.
    po::options_description desc("Allowed options");
    desc.add_options()
        ("help", "produce help message")
        ("compression", po::value<int>(), "set compression level")
    ;

    一開始的地方宣告了一個 options_descriptions 來存放允許的 options。呼叫 options_description class 的 add_options method 會回傳一個提供了 operator() 操作的 proxy object 。呼叫 operator() 就是宣告了 options 。參數分別是:

    (option name, information about value, option description)

    這個範例裡頭,第一個 option 沒有 value,第二個 options 有一個型別是 int 的 value 。

    po::variables_map vm;
    po::store(po::parse_command_line(ac, av, desc), vm);
    po::notify(vm);    

    之後,宣告一個型別為 variables_map 的 object -- vm。variables_map 是用來存放任意型別的 value 。接著呼叫:

    1. store
    2. parse_command_line
    3. notify

    去讓 vm 獲得所有 command line 來的 value。

    if (vm.count("help")) {
        cout << desc << "\n";
        return 1;
    }
    
    if (vm.count("compression")) {
        cout << "Compression level was set to " 
     << vm["compression"].as<int>() << ".\n";
    } else {
        cout << "Compression level was not set.\n";
    }

    最後我們可以任意地使用 options 了,variable_map 提供了像是 std::map 的操作,除了取得 value 時,必須使用 as method外。(要是你不小心誤用了 as 中的型別參數,使得 as 參數想取出的型別和實際不合時,會出一個 exception)

    試著 compile 你的程式吧,下面是個範例:

    $bin/gcc/debug/first
    Compression level was not set.
    $bin/gcc/debug/first --help
    Allowed options:
      --help                 : produce help message
      --compression arg      : set compression level
    $bin/gcc/debug/first --compression 10
    Compression level was set to 10.

    Reference

    1. Boost 1.39.0 Doc: Chapter 13. Boost.Program_options

    See Also

    1. Boost.Program_options – Option Details and Multiple Sources

    浮動 IP 架站

    幾經波折,還是決定自己來架設一台 server ,用的是陪我走過大學研究所六年的電腦,七年了,七年之癢了,換了新電腦,就拿來當 server 吧~家裏用的是沒有固定 IP 的 ADSL,於是去 DynDNS 申請了個免費帳號。不過因為是免費的,domain name 的名字不是那麼自由,就像這篇教學文所說的:想要得到 *.tw 結尾的網址要付錢,而且只有 *.idv.tw 比較便宜,*.net.tw 或 *.org.tw 等都不是那麼容易,除非執念太深,不然使用推薦使用 DynDNS 這類免費的服務,有些名字還至於太糟。

    申請 DynDNS

    https://www.dyndns.com/ 後,按下網頁右上角的 Create Account 就可以申請帳號了。

    image

    有了帳號後,還需要一個小程式,讓它常駐在系統裡,當 ADSL 撥接上後就可以自動連接到 DynDNS 去更新 IP 與 domain 的對應。

    Windows

    在 Windows 上可以用DynDNS Updater: http://cdn.dyndns.com/windows/DynUpSetup.exe 提供的工具,相當不傷腦又好用。

    Linux

    稍稍麻煩了點,需要 ddclient 這程式,主流的 distribution 都可以用套件管理程式取得,或是到這邊來下載。ddclient 的設定檔放在 /etc/ddclient.conf 下,網路上有很多教學,指導怎麼寫這設定檔,但最方便莫過於使用 DynDNS 提供的 config file generator 了。登入 DynDNS 後,點選 https://www.dyndns.com/support/tools/clientconfig.html 這網址,就會看到下方的頁面,選擇你要產生的 domain 和工具類型即可。

    image

    下面就是產生的設定檔,比起網路上有得教學和既有設定簡潔許多。

    ## ddclient configuration file
    daemon=600                  # check every 600 seconds
    syslog=yes                  # log update msgs to syslog
    mail-failure=keiko.zhou@gmail.com # Mail failed updates to user
    pid=/var/run/ddclient.pid   # record PID in file.
    
    ## Detect IP with our CheckIP server
    use=web, web=checkip.dyndns.com/, web-skip='IP Address'
    
    ## DynDNS username and password here
    login=Keiko
    password=##YOUR PASSWORD##
    
    ## Default options
    protocol=dyndns2
    server=members.dyndns.org
    
    ## Dynamic DNS hosts
    keiko.homelinux.net

    根據好心人提示,為防萬一,第一次使用 ddclient 時,建議可以用 debug mode ,確定設定檔行不行得通。

    ddclient -daemon=0 -debug -verbose -noquiet

    成功啟動的話,會看到類似下圖的畫面。

    HostYourServer03

    學長,我錯了…

    星期一跟小路學長吃飯才聊到某某人好像到了某校畢業典禮講了不恰當的話,沒想到今天回家我弟興致高昂地跑來叫我去參加反奴隸銀行遊行,順便問我認不認識楊基寬,還說這人大學跟我同間…

    好吧,那就仔細來看一下,哇,ptt 好多懶人包,學校還發了新聞稿

    第一點,給選擇延畢學生的提醒,如果你是因為景氣風暴而決定在校園裡面多躲一年的話,但請你告訴自己,職場是你人生必經的路,你沒有任何閃躲的空間,如果你的生平第一次就當個躲起來的懦夫,那麼你還能指望有什麼擔當呢?成大的學生不應該如此。

    第二點,給選擇讀研究所同學的一個提醒,如果你讀研究所的出發點,是希望你將來的薪水會比學士要高一點,碩士的名聲聽起來會比學士要好一點,那麼整個社會,對於一個心中只是想著多賺幾塊錢及虛榮心作祟的碩士生,還能期望他什麼呢,社會指望你讀研究所是希望你論文的研究主題能為社會或產業解決某一個學士生無法解決的問題,而不是指望你拿一篇長篇大論甚至是廢話連篇的論文來滿足你個人狹隘的虛榮心和薪水的要求,因此請告訴自己,職場是你人生必經的路,如果你生平第一次就只能這麼狹隘的自我期許,那麼你能夠指望這個狹隘的主人,就是你,會有什麼擔當呢?成大的學生不應該如此。

    第三點,是給選擇就業的同學一個提醒,我有信心學弟妹們都能順利找到工作,但因為今年景氣比較特殊,如果不幸的在未來,你已經不眠不休努力找工作找了三個月,還沒有找到的話,那麼請試著採用學長要給各位的一個策略,叫做「勇者策略」,當我們剛畢業的時候,什麼都還不會的時候,我們需要爭取的不是薪資,我們需要爭取的不是公司的大小,我們需要爭取的不是公司離家近不近,我們需要爭取的不是公司有沒有某一項的福利,我們需要爭取的不是公司需不需要加班,我們唯一需要爭取的就是那最珍貴的磨練,還有證明自己的機會,因此所謂的勇者策略是,當你經過3個月都還不能找到工作的時候,請挑選一家你心目中的公司,仔細研究這家公司有什麼地方是你可以為他們帶來所需要的解決提案,然後西裝筆挺的走進那家公司,眼神正視老闆跟他講說:「老闆,我這裡有一個針對貴公司某產品的提案,我願意不拿薪水,為公司效勞,直到景氣恢復為止。」老闆問你為什麼,你就跟他講,因為我需要磨練,我需要向你證明我是有用的,半年後如果景氣恢復,那麼老闆肯定會先用你,因為企業看到了你的主動,因為企業看到了你的解決能力,因為企業看到了你當仁不讓的決心,屆時你的經驗超過那些空等待的同學。

    ㄜ…

    第一點好像太偏頗了、第二點好像太偏激、第三點…… 聽說前陣子 Google/MS 人事凍結,早知道我就 xxoo

    想起星期天跟老師聚餐,老師講的話:要是我們的教育,得讓孩子這麼辛苦,我們的教育是失敗的…

    ps. 打開 WLW 又寫不出東西了,五月都沒寫 blog ,忘了怎麼寫文章了,看來我也要找個作文老師了!

    啟動完整的記憶體傾印 Enable Complete Memory Dump

    一直困擾我的問題,沒想到今天終於得到解答,感謝不知名的強者我同事!

    為了讓系統 crash 後有較多的資訊可以幫助 debug ,我們可以設定系統將 dump 資訊盡可能的保留下來,方法很簡單,進到啟動及修復後,選擇撰寫偵錯資訊(write debugging information),不過有時會發現找不到完整的記憶體傾印(Complete Memory Dump)的選項,即使是將在將虛擬記憶體調大、實際環境的硬碟空間也足夠的情況下依然會有這種情形出現。

    CompleteMemoryDump

    此時需要修改一個 registry key 來啟動,\HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\CrashControl\CrashDumpEnabled2 改成 1

    CompleteMemoryDump06 再回到啟動及修復後,選擇撰寫偵錯資訊(write debugging information)就會出現完整的記憶體傾印(Complete Memory Dump)的選項。

    CompleteMemoryDump07

    騰雲駕霧程式競賽 Blog 開跑了~

    image

    趨勢科技 2009 騰雲駕霧程式競賽的 blog 開跑了,就在作者外出到台南取材的時候 -_-||

    http://www.wretch.cc/blog/trendnop09

    上面可以詢問一切有關於比賽的問題,像是比賽方式、評分標準、今年還有沒有妙齡小天使、 training course 可不可以翹課、獎金可不可以不要扣稅、決賽入闈可不可以帶幸運毛毯、頒獎典禮可不可以帶寵物等等。不過還是要 prompt 本站一下,所以要是大家在官方 blog 問不到滿意的答案,歡迎還是回(娘家?!)到這發問啊,代客詢問是我專業自婊、出一張嘴、毀人不倦之外的第四專長,我會秉持打破沙鍋問到底、一審不定讞、二審再上訴、三審就辭職的態度幫忙發問,下面開放十個回應免費匿名…

    騰雲駕霧程式競賽

    這當然是廣告文!

    我們公司辦的比賽,有興趣的學長弟妹可以參加唷!節錄一些重點:

    「騰雲駕霧程式競賽」由趨勢科技主辦,IBM生物資訊研發中心協辦,該單位將提供硬體設備贊助。競賽籌備小組自3月10日起將至台北科大、台大、清大、交大等9所大專院校進行巡迴說明…

    哈,漏掉成大了,不過 3/24(二) 還是會去成大資工 4263 宣傳,可能過一陣子,海報、文宣就會出來了,我也會去唷,大家再找時間吃個飯吧!  <--- 重點!

    • 報名資格
      大學生、研究生、每隊四人(不多不少唷),可跨校!
    • 報名
      3/20-5/15:將開放網路報名,6月份將安排參賽者接受第一階段的網路遠距教學!
    • 比賽方式
      • 初賽
        7/1:網路公佈題目。
        7/8:00:00 停止收件。
        7/22:公佈入圍名單。
      • 決賽訓練課程
        7/30 ~ 7/31:到趨勢上課!
      • 決賽
        8/18 ~ 8/19:決賽入圍。今年也有可能不會在台北士林劍潭青年活動中心,會改去 IBM 生物資訊研發中心吧?機器都在那邊,或是要很炫的自己遠端 deploy 到伺服器海中,那就不必到現場了!
    • 頒獎
      8/20:頒獎。往年頒獎典禮都蠻有心的,會找飯店,以前有去過晶華酒店,典禮有簡單的點心、可邀請家人朋友、找記者來採訪(不過記者好像都不會來-_-|)
    • 獎項(這好像是最重要的)
      1st:NT$500,000
      2nd:NT$300,000
      3rd:NT$200,000
      4th:NT$150,000
      5th:NT$100,000
      6th ~ 10th:NT$50,000
      預聘書現在只給前三名了 <囧>
    • 題目
      好像沒提到,但是跟雲端運算有關就是!
    • 競賽網站
      http://www.trend.org/fd/tabid/66/Default.aspx
    • 附註
      比賽中會需要各式各樣的人,寫 UI、寫程式、寫文件、上台 presentation、找 bug、做 testing 都會,所以記得要把團隊技能平均問題,不要只點某一、兩樣技能唷!

    我是工作人員,不管有什麼疑難雜症、難以啟齒需要匿名的問題,我都可以代為傳達唷!

    騰雲駕霧程式競賽

    沒想到真的有人來問問題,作一篇置底廣告文,歡迎大家提出問題!DM01

    我們公司辦的比賽,有興趣的人,尤其是學長弟妹可以參加唷!節錄一些重點:

    「騰雲駕霧程式競賽」由趨勢科技主辦,IBM生物資訊研發中心協辦,該單位將提供硬體設備贊助。競賽籌備小組自3月10日起將至台北科大、台大、清大、交大等9所大專院校進行巡迴說明…

    • 說明會時間地點
      時間 學校 地點
      3/11(二) 12:20-13:30 北科大 綜合科館第三演講廳
      3/11(三) 13:00-14:00 台大 資工系 102
      3/12(四) 12:10-13:20 台科大 IB510
      3/16(一) 12:20-13:30 中山 電資大樓 F1001
      3/17(二) 11:00-12:00 中央 工五館 207
      3/18(三) 15:00-16:00 清大 資電館 127
      3/19(四) 12:20-13:30 交大 工三 122
      3/23(一) 14:00-16:00 中正 地震館 215
      3/24(二) 12:20-13:30 成大 成大資工 4263
    • 報名資格
      大學生、研究生、每隊四人(不多不少唷),可跨校!
    • 報名
      3/20-5/15:將開放網路報名,6月份將安排參賽者接受第一階段的網路遠距教學!
    • 比賽方式
      • 初賽
        7/1:網路公佈題目。
        7/8:00:00 停止收件。
        7/22:公佈入圍名單。
      • 決賽訓練課程
        7/30 ~ 7/31:到趨勢上課!
      • 決賽
        8/18 ~ 8/19:決賽入圍。今年也有可能不會在台北士林劍潭青年活動中心,會改去 IBM 生物資訊研發中心吧?機器都在那邊,或是要很炫的自己遠端 deploy 到伺服器海中,那就不必到現場了!
    • 頒獎
      8/20:頒獎。往年頒獎典禮都蠻有心的,會找飯店,以前有去過晶華酒店,典禮有簡單的點心、可邀請家人朋友、找記者來採訪(不過記者好像都不會來-_-|)
    • 獎項(這好像是最重要的)
      1st:NT$500,000
      2nd:NT$300,000
      3rd:NT$200,000
      4th:NT$150,000
      5th:NT$100,000
      6th ~ 10th:NT$50,000
      預聘書現在只給前三名了
    • 題目
      好像沒提到,但是跟雲端運算有關就是!
    • 競賽網站
      http://www.trend.org/fd/tabid/66/Default.aspx
    • 附註
      比賽中會需要各式各樣的人,寫 UI、寫程式、寫文件、上台 presentation、找 bug、做 testing 都會,所以記得要把團隊技能平均問題,不要只點某一、兩樣技能唷!

    我是工作人員,不管有什麼疑難雜症、難以啟齒需要匿名的問題,我都可以代為傳達唷!

    How to Deploy my Visual C++ Applications, Part 1

    前言

    寫程式的人常常會寫了一些應用程式或小工具後,想把它拿到另外一台電腦上測試或是分享給親朋好友,此時若是對方的電腦沒有安裝 Visual Studio,往往會遇到一個問題:對方無法執行我們的程式!

    中文版錯誤訊息:ScreenHunter_02 Apr. 25 20.41

    無法啟動這個應用程式,因為應用程式的設定不正確。重新安裝應用程式應該可以修復這個問題。

    英文版錯誤訊息:(我沒有英文版 Windows ,只好上網偷

    ScreenHunter_01 Apr. 25 22.11

    This application has failed to start because the application configuration is incorrect. Reinstalling the application may fix this problem.

    每當我遇到這種情況的時候,我都會想起研究所的學弟 DNA !當然不是想他這個人,是想起他說的話:

    What The Fuck! (WTF)

    這當然有一些解決方式:

    1. static link、
    2. 找出所有的 dependency ,然後包一包丟過去、
    3. 做個 release build ,然後找 MS 出的轉散發套件(redistributable package)請對方安裝、
    4. 做個安裝程式,把所有東西包一包!

    解決方式

    這裡就介紹最簡單的解決方式:copy!Visual C++ 的散佈套件放在:

    安裝目錄\VC\redist

    若是散佈 release 版本,就在這資料夾下根據平台選擇,若是 debug 版本,就選擇 Debug_NonRedist 這個子目錄,進去後一樣根據平台選擇下一個目錄。

    image

    不論是 release 版或是 debug 版,目錄都會根據函式庫類型分類好,不過 debug 似乎比 release 少了些東西:

    • Microsoft.VC80.ATL
    • Microsoft.VC80.MFCLOC

    不確定是不是因為 debug 版沒有上面兩者或是包含在 DebugMFC 裡頭了

    ScreenHunter_03 Apr. 25 22.27 Microsoft Visual Studio 8\VC\redist\Debug_NonRedist\x86 的 layout,給 debug build 用

    ScreenHunter_04 Apr. 25 22.27Microsoft Visual Studio 8\VC\redist\x86 的 layout,給 release build 用

    剩下要做的就是把跟你程式相關的函式庫一同 copy 給對方就可以了,不過這只解決了與 Visual C++ 相關的相依性,其他的程式用到的函式庫,就得靠自己尋找一下。

    基於好奇,再往 CRT 的資料夾裡頭看一下,那麼你會發現裡頭有四個檔案:(節錄自C Run-Time Library (CRT)

    1. Microsoft.VC80.DebugCRT.manifest
      Side-by-side assembly 不可或缺的一環,是微軟在 .Net 提出的架構,用來管理程式、元件的相依性。竟然說是不可或缺了,記得一定要 copy 這個檔案過去。
    2. msvcr80d.dll
      Multithreaded, dynamic link (import library for MSVCR80.DLL). Be aware that if you use the Standard C++ Library, your program will need MSVCP80.DLL to run.
    3. msvcp80d.dll
      Standard C++ Library
    4. msvcm80d.dll
      C Runtime import library. Used for mixed managed/native code. 如果程式沒用到 managed code,這個就不需要!

    image

     

    Reference

    1. MSDN: How to: Deploy using XCopy
    2. This application has failed to start because the application configuration is incorrect
    3. MSDN: C Run-Time Library (CRT)

    又是廣告,公司徵才!

    中午接到以前打工認識的朋友的電話,他現在在一間新公司上班,想在台灣成立軟體團隊,產品是 DVR 相關的監控產品,工作地點在台北中和或是大陸廣州中山,工作會用到 ARM, Linux, C/C++,職缺是軟韌體工程師,有熟悉下面的有加分:

    1. 對 Video Codec (H.264)熟悉
    2. Video streaming
    3. TCP/UDP/RTP/RTSP protocol
    4. Linux kernel, driver
    5. Windows 遠端監控
    6. Mobile 遠端瀏覽程式

    有興趣的朋友,可以跟我說唷!

    Qt 4.5 Released

    自從 Trolltech 被 Nokia 買了之後,網頁風格就變了,感覺好像比以前好了!不過最重要的是, Qt 4.5 改成以 LGPL 作為 Free 版本的授權,Yes!不過這次的編譯好像變麻煩了,試了好幾天 : ( 原來前陣子,這麼 down ,Qt 也要負責啦!)下面是嘗試過後,可用的編譯選項:

    1. configure –platform win32-msvc –debug-and-release –no-qt3support –no-webkit
      • -platfrom win32-msvc: 這似乎是這個版本一定要加的選項,我有試過用 %QMAKESPEC% 這個環境變數,但似乎沒用,一定得加這個選項。
      • -debug-and-release: 嗚嗚,是人總要 debug 吧,預設的編譯是不產生 debug 的,記得加上!
      • -no-qt3support: opensource 最常被訐詨的就是不向下相容, Qt 佛心來的!
      • -no-webkit: 論壇上很多人說他 build webkit 就會 build failed ,如果沒有要用到這個功能,可以略掉!不過我自己測試的結果是 OK!
    2. nmake

    編譯環境:

    • Windows XP
    • Visual Studio 2005 SP1

    Enjoy it!

    PreCode Snippet 4.0.2 Released

    在之前的文章提到的 PreCode 出新版了,已經支援 SyntaxHighlighter 2.0 的語法。不必像之前文章那樣進到原始碼模式修改 tag 。

    喜形於色

    小學的時候大家都寫過國語習題的照樣造句,沒寫過的表示我們是不同年代的人了…

    今天我也要來照樣造句,

    第一題:喜形於色。
    例句:小明的論文上了,所以喜形於色。
    Keiko:Royce 今天喜形於色地丟著 msn 訊息!早上老闆看了都大叫:Royce 你在笑唷!晚上 Aawen 同事見了也大叫:Royce 你怎麼了,怎麼一直笑!

    第二題是成語造句,要先查成語字典,知道成語的意思後,再造句!
    題目:春風得意。
    解釋:舊時稱登進士第後的志得意滿為「春風得意」。後用以形容人做事順利,志得意滿的神情。唐˙孟郊˙登科後詩:「春風得意馬蹄疾,一日看盡長安花。」元˙喬吉˙金錢記˙第四折:「他見我春風得意長安道,因此上迎頭兒將女婿招。」
    Keiko:今天台北冷冷的,飄著小雨,可是身邊暖暖地,原來旁邊站著是 Royce ,看他春風得意的,啊,原來台北沒有黃鶯,有的是路大師!原來人間四月天,春都到了小路身旁,怪不得外頭冷颼颼的,惟有這處暖!哇,春風滿面樂醄醄,一聲長笑海山高 : )

    第三題是自由發揮
    題目:百年好合、永結同心
    Keiko:老師,這題我要留到 5/17 ,口以嗎?
    老師:可以啊,科科!

    當兵

    今天比平常早了一點到公司,就跟 Arther 老大閒聊了一下,聊到了成功嶺大專兵可能要又恢復了!夭壽啊,當兵耶,一個可以讓平常剛毅木訥、口才遲鈍的男人瞬間長舌的話題,就像瑪莉兄弟吃到無敵星星,講話、回憶技能都滿了!果不其然,坐我後頭的兩位 J 先生馬上就打開話夾子了,耶,Architect 的穩重怎麼都不見了!一個是步校、一個是帶預官的,還好不用一梯退三步,不然我就要站去 101 了!

    不過話題的結束台詞跟我想的一模一樣:
    Jason 1:現在想想,當兵那段時間還蠻值得回憶的!
    Jason 2:當兵是件可以回憶卻不想在經歷的事!
    Keiko:Yes, bingo! (心理話)

    忘了自己哪時候退伍…

    啊,還有 866 天,這就是版面新工具的由來!

    出差出差!

    wang0zc5

    生平第一次出差,目的地是台南成大,南國的母校啊,真是令人懷念!雖然科技新貴已經是上個世紀的事了,但很多還是會把資訊系跟有錢畫上等號,殊不知成大資工系其實是個年輕到了不行的科系,沒有畢業校友的金錢挹注,換來的是古色古香的系館,儘管對這棟系館發出讚嘆的永遠外系的人多過在裡頭唸過的人…

    有種圍城的感覺:裡頭的人拼了命想外跑,外頭的人擠破頭想進來!好像選公司也是一樣的,永遠是還沒去過的公司最好!想起了跟 D.N.A 學弟講的話。

    Keiko:人家都學歷顯赫,個個都留美學歷!
    D.N.A:可是你留台啊!開口粥,留台多年,菁英份子!

    哈哈,或許人就是這樣,對未知的東西不是充滿恐懼就是抱有美好幻想!忘了是哪位實驗室學長說過的話,還是我說得呢?

    外國人絕對不會比較厲害,狗屁倒灶的事絕對比我們多又扯,只是我們只看到好的一面!

    現在想想,我真應該在「工商服務時間」把這點提出來講一下!這就是所謂:門的另一端,真實的世界。突然想起上個禮拜請假到台南玩,星期一去了七股鹽田,在鹽山之前看到一對情侶朝我走來,男生是背對著我,手裡拿著相機,鏡頭當然對著心愛的女友,天空藍藍的,海風不強,輕輕拂過女孩的長髮,三千髮絲搖曳著,雖然只看到男生的背影,不過已經可以想像他是多麼小心翼翼想會這針對的一刻留下永恆的紀念,他一定小心翼翼地想捕捉女生最美的瞬間,傳說中甜蜜的青春也不過如此吧!

    可是呢?這當然是我的幻想啊,從來沒有女生讓我這樣拍過,我也沒有有錢到請 model 來讓我幻想一下,更何況我手中只有 Sony W7 這種傻到不行的相機!那現實可能是怎樣呢?

    1. 可能是男孩倒退著走,然後一個重心不穩,跌個狗吃屎。
    2. 也可能男生其實跟我一樣,得了一種「相機帕金森式症」,這是一種拿到相機才會發作,症狀是手晃不止的病。結果就是把女朋友拍得很醜,晚上回到飯店可能遭到一頓痛毆。

    image

    天啊,我話好多,讓我想起這幾天看的獵人漫畫 280 話的拿酷戮,短短擦身而過,原來我也想了這麼多字!

    its_Cold

    出差很累,比上班還累,早上六點半就起床,因為家住台北知名的塞車之地,天空下著小雨,冷到我都想偷我弟的羽戎衣來穿!為什麼公司發的宣傳衣是短袖,為什麼我明明是興高采烈的想去說明會,卻是這樣的天氣歡迎我,知道嗎?這就回到剛剛講的:門的另一端,真實的世界…

    儘管如此,這世間還是有不變的美好!要我舉例來說嗎?

    1. 嗯嗯,那就像我印象中的台南永遠都是有陽光的,高鐵只要一過曾文溪就會看到太陽。
    2. 李強老師永遠不記得我的名字,卻永遠記得我這個學生,記得我修過他什麼課、幹過什麼壞事,這次到是不忘跟我要名片!
    3. 黃宗立老師跟我打招呼的速度一定比我跟他打招呼快!
    4. 黃崇明老師一直記得我在蘇小鈺手下打雜!
    5. 每次要找朱治平老師,他永遠不在辦公室!!
    6. 永遠以為楊中平老師跟我一樣菜 -_-||
    7. 老師的女兒德恩不管看過我們幾次,每次的印象卻都像是要砍掉重練,從零開始、再出發!
    8. 老師辦公室的唱片還是很多,不時還會有陣陣木頭香!
    9. showmin, 文森, D.N.A 永遠很捧場,buffett 一定要坐在角落,aaa 一定要遲到。
    10. 冠廷一定要提早會消失(哪怕我們有革命情感),bbb 一定要耍龜毛!
    11. 老師一定會問我:我的 P2P player 呢?我的黑膠呢?
    12. 最後,在回台北的高鐵上,計畫著下次什麼時候再來?(這樣會不會被發現我是敗家子,都坐高鐵來回 -_-)

    回到正題,出差是為了宣傳騰雲駕霧程式競賽,雖然來的人不多,不過也讓我體驗到什麼是一字之差的 mindset:

    Royce:會來的就是會來!
    Keiko:該來的就是會來!

    不過還是有感動的事,遇到三個電通所的學弟,雖然不知道他們是從哪看來的網宣,可能是系學會幫的忙,不過從他們堅定的眼神看得出來他們會參加!

    最後,也是最重要的一點,說明會前遇到了系主任,在他大力支持下, 4/17 (五)下午 2:00 開始的 seminar 我們又可以再去一次了!天啊,意義非凡,因為星期五下去,表示可以星期 X 才回台北!

    Domain-Specific Language (DSL)

    前言

    過去幾年蠻熱門的話題(?),原本還想來學院派式的追本溯源一下,不過凡事起頭難,手邊資料不多,很多論文又不能下載,就作罷了,當閒聊了,又不是要寫論文 XD

    我想是因為自己很晚才接觸資訊這個領域,加上上個世紀,網路、社群並非如此盛行,總有種錯覺:這幾年許多新語言的出現不再宣稱自己能解決所有的問題、可以被應用在所有領域,而是將焦點專注於特定問題、領域(domain)的,當然,這不是個新概念,但 Domain-specific language 卻是這幾年才流行起來的。

    Generative Programming

    Generative Programming 一書是這麼描述 DSL 的:

    A domain-specific language (DSL) is a specialized, problem-oriented language. …(略) ... Domain-specific languages can be textural (e.g. SQL) or graphical (e.g. the graphical specification of a GUI in a GUI builder).

    哈,基本上這是個很寬鬆的描述,會被 reviewer 打槍的那種,不過不難想像到 DSL 概念的成品已經充斥著我們生活周遭,像是:

    1. lex & yacc:大學 compiler 課用會到!
    2. SQL
    3. 曾經 SLIM 想用的 XUL ,到後來自己設計的 component script
    4. SMP 的 SREAM script
    5. ChucK (那有沒有 SCREAM 呢!)
    6. 微軟 .Net 新一代的 GUI 平台用到的 XAML(很多 open source GUI framework 也有類似的概念)
    7. UML 中的 OCL

    這幾年 VM 這種結合 compiler, interpreter 特性的平台語言很是盛行,透過重複利用 VM 的中間碼可以讓原本設計一個 programming langauge 需要的繁雜後端工作得到部份減輕。所以許多研究、實驗性質的 DSL 會考慮建構在 VM 之上。舉例來說,就有許多語言建構在 JVM 之上,參考這邊

    優點

    那到底 DSL 的好處是什麼呢?wikiDomain-Specific Language: An Annotated Bibliography 做了很好的條列和解釋,不過我還是想再說一遍:( -_-||唉,我想我個性不太好)

    1. Expressive:我想這是最重要一點,人類的思考會受到語言、符號、圖像的限制。因此 DSL 在描述問題、解決問題時都會比 general purpose language (暫用 GPL 代表)更能貼近核心。最顯而易見的例子就是 GUI 的設計,用 WYSIWYG 的方式刻繪介面會比純文字好、BNF  form 比一堆 if, else, loop 湊出來的 FSM 更能表達語言結構、SQL 語法比用 procedural 清晰。此外,好的表達力可以讓維護程式碼時,一目了然,不需太多註解、文件相輔,一行 DSL 程式碼表達的運算可能需要數行 GPL 才能完成,這種精簡效果是很可觀的!
    2. 語法支持:這個部份我最想挑出來說的是 domain-specific error report/checking 和 constraint enforcement。好的語言不僅給予程式設計師自由的空間去發會,還要能適時給予程式設計師提醒與限制。 enforcement 是我進公司後才體驗到的一個 term ,這裡或許反映出一個個人喜好,我不喜歡把錯誤推遲到執行時期發現。不同的應用中常會帶來不同的限制或者說是規範,最糟糕的情況是無法透過 GPL 既有元素去表達的,或是僅能在執行時期偵測、或是以間接、隱晦的方式去表現。
    3. 其他,如可以做到更好的最佳化、程式碼內嵌 domain knowledge、易於測試等。(哈,我用其他來帶過,其實就代表我個人認為前兩項是最重要的好處)

    為了避免空口說白話,這邊以 C++ 實做一個矩陣為例子來說明 GPL 可能做不好的地方:

    class MyMatrix {
    public:
        MyMatrix();
        MyMatrix( int row, int col );
    
        int operator ()( int row, int col );
        vector<int>& operator []( int row );
        template <typename T>
        MyMatrix& operator =( const T& );
    
    private:
        int     row_;
        int     col_;
        vector< vector<int> >   data_;
    };

    使用時很簡單,就像下面一樣,精練、不失矩陣的意含(我想是這樣沒錯):

        MyMatrix m1( 10, 10 );

    第一步看起來不錯,但是很明顯的當我們想初始化矩陣時,無論我們怎麼努力,卻很難只靠 C++ 本身(先不討論 Preprocessor)做到像下面的效果:

        MyMatrix m2 = { { 0, 0 }, { 0, 0 } };

    最多我們只能靠著 operator overloading 做到:

        for ( int i = 0; i < 10; ++i ) {
            for ( int j = 0; j < 10; ++j ) {
                m1[ i ][ j ] = i + j;
            }
        }

    或是提供 MyMatrixRow, MyMatrixCol 之類的 Proxy class 去一次設定多個元素;但無論如何,當元素間沒有規則時,一個一個指定矩陣元素的步驟就跑不了,此時若是有個 Matrix 相關的 DSL 輔助,我們可以寫出更精練、具可讀性的程式。下一個我們會面臨的問題可能發生在矩陣運算上,以加法為例,兩個相加的矩陣必須具有同樣的維度,但這個限制(或說規範)在 MyMatrix 是無法直接表達的(請先不要考慮到 C++ template,首先是因為這不是每個語言都有的 feature,其次是之後會有個段論詳細解釋這部份),所以我們可能得在

    MyMatrix operator +( const MyMatrix& lhs, const MyMatrix& rhs );

    的實做時,去檢查矩陣的維度,當兩者不符合時丟出一個 excption。當然不是說 exception 不好,而是若是能越早發現錯誤會更好,尤其是這種維度的指定與推導是可以靜態完成的,實在沒有必要留待動態時期去偵測。此時若是有個 Matrix 相關的 DSL 可以在撰寫時給予這方面的限制,可以省去不少麻煩、也能提高程式效能。

    缺點

    說完 DSL 的優點,聽起來是如此好用,讓我也一度著迷,想做些 DSL 出來用,不過現實世界當然不會如此美好!DSL 最大的問題在於:

    1. 草創期
      實做一個 DSL 需要許多人力投入在設計、實做上,先期的 domain/problem 分析、研究更是花費人工。這階段最惱人的應該是穩定度吧?!
    2. 初期
      當語言實做完成後,下一個面臨的問題就是配套與使用者心理。即使語言再簡單,都還是有自己的中心哲學,對於使用者來說:他們是不是快速的學習、語言的 Learning curve 是不是適當呢?甚至使用者可能排斥再學一個新語言?接著實際開發時,是不是相關的 tool chain 都能搭配上,是不是能有好用的 editor、IDE 來輔助開發、吸引開發者目光。而其中最為麻煩的,我想是錯誤資訊(像是 compiler error、failed to code gen …)與 debug 的環境,該如何讓使用者快速 trouble shooting 是很重要的。即使今天我們提供的 DSL 只是做 transformation,把 MyMatrix DSL 轉成 C++ 而已,錯誤資訊的轉換是恨重要的,有用過 lex/yacc 的人可以回憶一下,在寫錯 lex/yacc 的程式時,lex/yacc 的 codegen 程式有時不會知道,它們只能根據預先的條件去轉換成 C 語言,當送給 C compiler 時才會 compile errors,此時使用者該如何從這樣的錯誤對應回 lex/yacc 的錯誤呢?當然最好的方式是,使用者不必以 codegen 後的角度去找尋錯誤,而是直接面對他們寫的 DSL 程式去找錯(像是 lex, yacc 程式碼、MyMatrix),同樣地,debug 也是面臨同樣問題,lex/yacc 雖然在code generation 時會把對應的行號、檔名寫到產生出來的 .c 檔中,但用 C debugger 回頭看到的卻是 FSM 和 LALR 的程式,個人認為使用者是很容易迷路的!
    3. 中期
      維護,像是文件、社群資源等都是需要長時間投入的。
    4. 長期
      想必該是面對改版相容性問題時候了!

    DSL 的形式

    從 DSL 的實做方式以及它怎麼跟其他語言合作,我們可以大致上分成下面幾種,並且從設計、實做與使用上來討論一下:

    1. Fixed, separate DSLs
      這是最常見的一種形式,DSL 擁有自行一套的 tool chain 甚至執行環境,例如 SQL 。這種形式的 DSL 最大的問題是很難跟其他語言進行良好的整合,可能是語言特性(static vs. dynamic)、執行方式(compiler vs. interpreter)、執行環境的差異、tool chain 等因素造成。即使是可以透過一定的手法把 separate DSLs 轉為 embedded DSLs,但仍不是很方便。而 DSL 實做上呢?從頭到尾打造一個 DSL 是很曠日費時,而且成本極高的一件事,更不要談說相關的開發環境。簡單的說, DSL 和他要合作的語言間太鬆綁了(loose coupling),loose coupling 有時不是件好事。
    2. Embedded DSLs
      另一種方式是我們可以在 GPL 中定義相關的類別、函式去包裝 DSL ,讓開發過程中,DSL 與 GPL 是緊緊地包在一起的,這聽起來比 separate DSLs 好,但是根據實做出的合作方式,卻可能比 separate DSLs 好不到哪裡去,舉個例子來說,很多 database 都會有不同語言的 interface library 供開發者使用,PostgreSQL 有一個 C++ 介面的函式,叫做 libpgxx ,它與 PostgreSQL 連接的方式是:
      int playWithDb() {
          connection Conn( "dbname=test" );
          {
              work Xaction( Conn, "DemoTransaction" );
              result r = Xaction.exec( "DELETE FROM " + Table + " WHERE ID=" + ID );
      
              for ( result::size_type i = 0; i != R.size(); ++i ) {
                  Process(R[i]["lastname"]);
              }
          }
      }
      或是看個 C++ 與 TCL 的整合:
      void hello()
      {
           cout << "Hello C++/Tcl!" << endl;
      }
      
      int main()
      {
           interpreter i;
           i.def("hello", hello);
      
           string script = "for {set i 0} {$i != 4} {incr i} { hello }";
      
           i.eval(script);
      }
      不難發現這種倚賴參數、字串來執行 DSL 的方式讓我們失去了機會去做一些事情:
      1. express domain-specific optimization
      2. domain-specific error report:  syntax error 和 debug 是我認為尤其重要的,像是 compile time 的 error/constraint enforcement !
      3. domain-specific syntax
      不過幸運的是,有另外一種方式可以讓我們從 GPL 中衍生 DSL ,那就是 metaprogramming ,雖然不是每種語言都有這種特性,也不是擁有這種特性後就能一切完美。以 C++ 來說,我們可以透過 template metaprogramming 做到 code generation 和 code optimization ,甚至透過 C++ 型別去做部份的 error report/checking。但大部分時候仍是補強而已,很多 domain-specific 的事還是得推延到 run time。這邊想示範一個簡單的 C++ metaprogramming 利用型別實做 constraint enforcement 的概念,那就再次請出 Matrix 來當例子,這次我想針對維度做出 constraint enforcement ,那我們可以把原本的實做改成像下面這樣:
      template <int RowT, int ColT>
      class MyMatrix {
      public:
          friend MyMatrix operator +( const MyMatrix& lhs, const MyMatrix& rhs );
      
          MyMatrix();
      
          int operator ()( int row, int col );
          vector<int>& operator []( int row );
          template <typename T>
          MyMatrix& operator =( const T& );
      
          static const int RowDimension = RowT;
          static const int ColDimension = ColT;
      
      private:
          vector< vector<int> >   data_;
      };
      注意到沒有,我們把維度的概念帶入型別之中,那麼下面的程式碼, 
       
          MyMatrix<10, 10> m1;
          MyMatrix<2, 2>   m2;
      
          cout << m1 + m2 << endl;
      便可以在 compile time 丟出錯誤,下面是 VC 2005 的錯誤訊息:

      error C2679: binary '+' : no operator found which takes a right-hand operand of type 'MyMatrix' (or there is no acceptable conversion)

      但是細心的你注意到了嗎?即使我們把錯誤拉前了,但這樣的錯誤訊息並不是跟矩陣有太大關係,如果能丟出

      incompatible dimensions

      是不是更好呢?在 GPL 中,我們有時是很難做到完整的 domain-specific error report/checking 的 。再者,並不是每種 constraint 都適合以型別的方式表達,一來可能造成 template parameter 數量爆增,這可能增加 compile 時間,二來也暴露太多細節在 client code 中,這會讓程式碼難以擴充維護。
      雖然用了 C++ template 當例子,不過值得提一下, template haskell 似乎也很有趣,許多 functional language 都可以一些語言擴充!
    3. Modularly composable DSLs
      腦子很小的我,只想出前面兩種,這第三種形式是 Generative Programming 一書提出的,它強調的是在語言的基礎建設(infrastructure)完善皆備的情況下,去開發語言的 plug-in ,說來神奇,其實不會, C/C++ 的 preprocessor 或是更古老的 Smalltalk 和 CLOS 都是這種概念的雛型。只是它們未能在 domain-specific 的需求上達到更好的精細度。或許看到這裡,你會有跟我一樣的一個疑問:那 Modularly composable DSLs 與 Embedded DSLs 有什麼不同呢?聽起來都是建構在既有語言之上,其實這中間有個隱微的差異,可以細細體驗一下:

      Embedded DSLs 的根基是現有語言的 syntax,而 Modularly composable DSLs 則是做 syntax extensions。

      這句話代表的是,Embedded DSLs 的使用是類似於使用 GPL 的 library 或是 framework 的,其 syntax/semantic 跟原本 GPL 是一樣的。但 Modularly composable DSLs 是構造在 GPL infrastructure 之上,除了既有的 syntax/semantic 外,我們還可以增加 domain-specific 的 syntax/semantic ,甚至是刪去、捨棄既有的。若是想更細部條列兩者的差異,或許可以參考 Generative Programming 所條列的,Modularly composable DSLs 可以比 Embedded DSLs 做到:
      1. Syntax extensions
      2. Semantic extensions or modifications of language constructs
      3. Domain-specific optimizations
      4. Domain-specific type systems
      5. Domain-specific error checking and reporting
      再回到 language infrastructure 上, 我們有什麼辦法可以操作 language infrastructure 呢?有幾種可能性:
      1. Preprocessor
      2. Metaprogramming
      3. Modularly Extendible Compilers and Modularly Extendible Programming Environments
      前兩者在許多語言上已經可以做到,正如前面提到的 C/C++, Smalltalk, Haskell 和 CLOS 都行,只是做的不夠好。因為這不只是語言本身支援的問題,還牽扯到 tool chain 與環境。至於第三種,則可以參考 Charles Simonyi (是的,就是微軟的那位大師,匈牙利命名法、WYSIWYG document 發明者、 Word 之父)的研究 – Intentional Programming 。大師在 Microsoft Research 以及後來開設的 Intentional Software 公司都可以找到這方面的發表與研究成果,研究開始得很早,約莫在上個世紀的 90 年代初,未來有時間,希望能玩玩看再來跟大家分享一下!

    結語

    這篇文章真是超長,內容又很空洞,可是我打了兩天,大概是我在低潮吧…不過還是想跟大家分享一下幾點:

    1. Compiler is your friend!
    2. Constraint enforcement is your friend!
    3. Compile-time error report/checking is your friend!
    4. The last but most important point, C++ is your friend!

    MiniFilter InstanceSetupCallback is not called?

    一般來說,MiniFilter 的 InstanceSetupCallback 會在 filter manager 把 minifilter attache 到 volume 後呼叫。如果沒有的話,可以檢查一下 minifilter 的 INF 是否把 instance fla...