<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>JeffHung.Blog &#187; Programming</title>
	<atom:link href="http://www.jeffhung.net/blog/categories/devel/programming/feed/" rel="self" type="application/rss+xml" />
	<link>http://www.jeffhung.net/blog</link>
	<description>(My smile insists of having nose. :-)</description>
	<lastBuildDate>Thu, 24 Nov 2011 07:25:31 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.1.2-alpha</generator>
		<item>
		<title>Unescape URL in Perl</title>
		<link>http://www.jeffhung.net/blog/articles/jeffhung/2424/</link>
		<comments>http://www.jeffhung.net/blog/articles/jeffhung/2424/#comments</comments>
		<pubDate>Sat, 24 Oct 2009 18:51:08 +0000</pubDate>
		<dc:creator>jeffhung</dc:creator>
				<category><![CDATA[Programming]]></category>
		<category><![CDATA[perl]]></category>

		<guid isPermaLink="false">http://www.jeffhung.net/blog/?p=2424</guid>
		<description><![CDATA[看 log 時快速解碼用的，一行搞定。]]></description>
			<content:encoded><![CDATA[<p>看 log 時快速解碼用的，一行搞定：</p>
<pre class="code">
SHELL&gt; echo &quot;%3A%22abc%22&quot; | perl -MURI::Escape -ne 'print uri_unescape($_)'
:&quot;abc&quot;
</pre>
<p>看來某程式碰到非 ASCII 字元就會爛掉。</p>
]]></content:encoded>
			<wfw:commentRss>http://www.jeffhung.net/blog/articles/jeffhung/2424/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Ubuntu 真難用</title>
		<link>http://www.jeffhung.net/blog/articles/jeffhung/1086/</link>
		<comments>http://www.jeffhung.net/blog/articles/jeffhung/1086/#comments</comments>
		<pubDate>Fri, 02 Oct 2009 12:23:09 +0000</pubDate>
		<dc:creator>jeffhung</dc:creator>
				<category><![CDATA[Programming]]></category>
		<category><![CDATA[cpp]]></category>
		<category><![CDATA[linux]]></category>

		<guid isPermaLink="false">http://www.jeffhung.net/blog/?p=1086</guid>
		<description><![CDATA[兩個工作資歷加起來超過 15 年，可跨 UNIX/Windows 開發軟體的工程師，為了要在 Ubuntu 上編譯 C 的 hello world 程式，竟然要花上半小時。]]></description>
			<content:encoded><![CDATA[<p>兩個工作資歷加起來超過 15 年，可跨 UNIX/Windows 開發軟體的工程師，為了要在 <a href="http://zh.wikipedia.org/wiki/Ubuntu">Ubuntu</a> 上編譯 C 的 hello world 程式，竟然要花上半小時。</p>
<p>工程師 A：「B 你能不能來幫我看看，為甚麼 configure 不過。」</p>
<p>工程師 B 仔細地看了一下螢幕上的 error messages，發現是因為找不到 <code class="inline_code">crt1.o</code>。</p>
<p>工程師 B：「看起來像是 libc 沒有裝，我們可以試著編一個 hello world 來確認。」</p>
<p>於是工程師 A 寫了一個 <code class="inline_code">hello.c</code> 如下：</p>
<pre class="code">
#include &lt;stdio.h&gt;

int main()
{
    printf(&quot;Hello World\n&quot;);
    return 0;
}
</pre>
<p>用 gcc 編譯，gcc 會動，但跑出錯誤訊息，說找不到 <code class="inline_code">stdio.h</code>。</p>
<p>工程師 B：「果然。」</p>
<p>工程師 A 順手打了一下指令：</p>
<pre class="screen">
SHELL&gt; locate stdio.h
Not found.
</pre>
<p>工程師 A：「Ubuntu 應該是用 apt-get 安裝軟體吧？」</p>
<p>工程師 A 試著下指令：</p>
<pre class="screen">
SHELL&gt; sudo apt-get install libc
Cannot find package libc
</pre>
<p>工程師 B：「可能是叫做 glibc。」</p>
<p>工程師 A 試著再下指令：</p>
<pre class="screen">
SHELL&gt; sudo apt-get install glibc
Cannot find package glibc
</pre>
<p>工程師 A：「見鬼了，誰知道 libc 應該要叫什麼啊！要怎麼知道套件的名字是什麼？」</p>
<p>工程師 A 試著打下面的指令：</p>
<pre class="screen">
SHELL&gt; apt-get search libc
Invalid command 'search'.
</pre>
<p>工程師 B：「還是用 GUI 工具好了，至少應該會有個搜尋框可以用。」</p>
<p>工程師 A 從系統選單裡找到 <a href="http://en.wikipedia.org/wiki/Synaptic_%28software%29">synaptic</a> 這支程式，查了幾次， 用 <code class="inline_code">glibc</code> 找不到任何東西，後來改用 <code class="inline_code">libc</code> 當關鍵字找，終於找到 <code class="inline_code">libc6-dev</code> 這個套件。</p>
<p>實在無法理解，為什麼 <code class="inline_code">apt-get</code> 沒有提供搜尋的功能，誰知道到底套件叫做 <code class="inline_code">libc</code>、<code class="inline_code">glibc</code> 還是 <code class="inline_code">libc6-dev</code> 啊。後來我知道，似乎搜尋的指令下法是 <code class="inline_code">apt-cache search</code>，可是 cache 這個字眼，跟 search 感覺上就是沾不到邊，真是莫名其妙的組合。其實我覺得，<code class="inline_code">apt-get</code> 的 <code class="inline_code">get</code> 也很莫名其妙，感覺上就是功能越做越多，沒有一開始就設計完善，最後只好通通塞在一起。</p>
<p>用 synaptic 的過程也是挫折感十足。利用其搜尋功能，確實地找到了 <code class="inline_code">libc6-dev</code> 這個套件，可是勾選安裝後，就不曉得接下來要怎麼辦，才能真的開始安裝。樸素的視窗，兩個人硬是 try 了十分鐘，才終於明瞭，要按工具列的「Apply」按鈕，才能開始安裝。誰知道這個 Apply 是在 apply 什麼啊，難道不能用個「Start」或「Next」的按鈕嗎？又不是在搞 version control，還要下個 commit 才會真正改變 repository。</p>
<p>最後，總算成功地編譯了 hello world，花了半小時。XD</p>
<p>然後我還是不懂，為什麼預設裝好有 <code class="inline_code">gcc</code> 但沒有 <code class="inline_code">libc</code> 跟標準 C 的 include files 可以用。要嘛就通通不要有開發工具，要嘛就基本的要裝齊全。不是每個人都是 kernel developer 好嗎？</p>
<p>後記：後來我們又發現，從頭到尾，我們是在 live cd 上工作，而不是在真正已經安裝在硬碟裡的 ubuntu 裡工作。難怪網路怎麼設都不會通。-.-||</p>
]]></content:encoded>
			<wfw:commentRss>http://www.jeffhung.net/blog/articles/jeffhung/1086/feed/</wfw:commentRss>
		<slash:comments>21</slash:comments>
		</item>
		<item>
		<title>Where to place mutable members in class?</title>
		<link>http://www.jeffhung.net/blog/articles/jeffhung/1191/</link>
		<comments>http://www.jeffhung.net/blog/articles/jeffhung/1191/#comments</comments>
		<pubDate>Thu, 01 Oct 2009 17:54:05 +0000</pubDate>
		<dc:creator>jeffhung</dc:creator>
				<category><![CDATA[Programming]]></category>
		<category><![CDATA[cpp]]></category>

		<guid isPermaLink="false">http://www.jeffhung.net/blog/?p=1191</guid>
		<description><![CDATA[在 C++ 裡，若 member data 被宣告為 mutable，表示即使是在「常數情境」下，如物件本身被宣告為 const，或執行於宣告成 const 的 member function 裡時，該 member data 仍然可以被修改...]]></description>
			<content:encoded><![CDATA[<p>在 C++ 裡，若 member data 被宣告為 mutable，表示即使是在「常數情境」下，如物件本身被宣告為 const，或執行於宣告成 const 的 member function 裡時，該 member data 仍然可以被修改。</p>
<p>這通常用於，如 mutex 這種，其實不屬於概念上的物件內容，但又需要存在於物件裡以維持正確運作的那些 member data。可是，這種「不屬於概念上的物件內容，但又需要存在於物件裡的」member data，該怎麼擺放於物件裡？</p>
<p>於是，在 <code class="inline_code">#cpp-tw</code> 裡，有了這樣的討論：</p>
<pre class="screen">
11:38 &lt;jeffhung&gt; 假設我們有一個 mutable mutex 的 member data，要放在 class 裡最前面，還是
                 最後面？
11:39 &lt;jeffhung&gt; 放最後面的好處是，這些 mutable 的 member，在轉成 persistent form 時，可
                 以直接 memcpy 然後把尾巴的 mutable members 去掉。
11:40 &lt;jeffhung&gt; 放最前面的好處是，sub object 的 construct order 是按順序來的，所以可以保
                 證 mutex object 比其他 member 還要早 construct，還要 晚 destruct。
11:40 &lt;jeffhung&gt; 這樣甚至可以在 initialization-list 裡 lock mutex。 (but how?!)
11:43 &lt;jeffhung&gt; (另一個擺法是，把 mutex 放在 parent)
11:43 &lt;l*&gt;       或者放在外頭.... ? XD
11:43 &lt;l*&gt;       (use a key-value table to store that....?)
11:44 &lt;jeffhung&gt; l*: 那這個 key-value table 可能也要 lock，會產生 bottleneck</pre>
<p>目前我傾向於擺在前頭。我認為，物件內容的正確性，是第一要務，使用方便與否，則在其次。但關於這個議題，可能還有還沒考慮到的地方，因此也暫時沒有定論。諸位覺得呢？</p>
]]></content:encoded>
			<wfw:commentRss>http://www.jeffhung.net/blog/articles/jeffhung/1191/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Avoid anonymous namespace in header files</title>
		<link>http://www.jeffhung.net/blog/articles/jeffhung/2144/</link>
		<comments>http://www.jeffhung.net/blog/articles/jeffhung/2144/#comments</comments>
		<pubDate>Tue, 01 Sep 2009 14:29:59 +0000</pubDate>
		<dc:creator>jeffhung</dc:creator>
				<category><![CDATA[Programming]]></category>
		<category><![CDATA[cpp]]></category>

		<guid isPermaLink="false">http://www.jeffhung.net/blog/?p=2144</guid>
		<description><![CDATA[不過因為今天寫 C++ 程式的時候，碰到這樣的 warning：warning: 'Foo' has a field 'Foo::impl_' whose type uses the anonymous namespace. 原來是因為，我在 foo.hpp 這個 header 檔裡，寫了一個 anonymous namespace…]]></description>
			<content:encoded><![CDATA[<p>不過因為今天寫 C++ 程式的時候，碰到這樣的 warning：</p>
<pre class="code">
warning: 'Foo' has a field 'Foo::impl_' whose type uses the anonymous namespace.</pre>
<p>原來是因為，我在 <code class="inline_code">foo.hpp</code> 這個 header 檔裡，寫了一個 <a href="http://www.cppreference.com/wiki/keywords/namespace#anonymous_namespace">anonymous namespace</a>，如下：</p>
<pre class="code">
#ifndef FOO_HPP_INCLUDED
#define FOO_HPP_INCLUDED

namespace { // anonymous, so only library implementer know how to use it
    class FooImpl { ... };
}

class Foo
{
public:
    ...
private:
    FooImpl impl_;
};

#endif /* FOO_HPP_INCLUDED */
</pre>
<p>因為還沒有引入如 <code class="inline_code">boost::shard_ptr&lt;&gt;</code> 的機制，考量到 exception/thread safety，不想要讓 <code class="inline_code">impl_</code> 是個指標，因此利用 anonymous namespace 的方式，把 <code class="inline_code">FooImpl</code>「藏」起來。不過，這樣的寫法，有潛在的問題，因此 gcc 發出 warning 警告。</p>
<p>Compiler 在編譯時 anonymous namespace 時，相當於以 compilation unit 為單位，為裡面的 anonymous namespace，取一個獨一無二的名字，然後自動 using 之。若 header 檔被不同的 source 檔引入，成為不同的 compilation unit，則 anonymous namespace 所自動產生的名字，皆不相同，共同運作時，就會產生問題。</p>
<p>以上例來說，<code class="inline_code">foo.hpp</code> 可能被 <code class="inline_code">a.cpp</code> 與 <code class="inline_code">b.cpp</code> 分別引入，兩者分別建構了 Foo 類別的物件，依據以上說明，<code class="inline_code">a.cpp</code> 裡面的 <code class="inline_code">Foo</code> 類別的物件，其 <code class="inline_code">impl_</code> 成員的型別，可能是 <code class="inline_code">anonymous_a</code>；而 <code class="inline_code">b.cpp</code> 裡面的 <code class="inline_code">Foo</code> 類別的物件，其 <code class="inline_code">impl_</code> 成員的型別，則可能是 <code class="inline_code">anonymous_b</code>。同樣是 <code class="inline_code">Foo</code> 類別的物件，在不同的 compilation unit 裡，其 <code class="inline_code">impl_</code> 成員，型別卻不同，這個歧異，肯定會造成問題。</p>
<p>因此，我們應該避免在 header 檔，使用 anonymous namespace。</p>
<p>可是，若不能在 header 裡使用 anonymous namespace 的話，那 anonymous namespace 到底有甚麼存在的意義呢？畢竟，我們大可以直接在 source 檔裡宣告類別，反正其他 source 也看不到，不就有了 anonymous namespace 的效果了嗎？</p>
<p>其實，anonymous namespace 還是有其存在意義的，畢竟，anonymous namespace 依然是一個 namespace，而 namespace 的作用，就在於避免 name collision。好比說，我們永遠無法保證，source 檔所引入的其他程式庫的 header 檔，在未來版本裡，會不會冒出一個，名字跟我們想要藏起來自己用的類別、函式或變數。而有了 anonymous namespace，我們就可以保證，此時不會發生 name collision。</p>
<p><strong>參考資料：</strong></p>
<ul>
<li>comp.lang.c++.moderated &raquo; <a href="http://groups.google.com/group/comp.lang.c++.moderated/browse_thread/thread/39070498954c5781">warning: ... has a field ... whose type uses the anonymous namespace</a></li>
</ul>
]]></content:encoded>
			<wfw:commentRss>http://www.jeffhung.net/blog/articles/jeffhung/2144/feed/</wfw:commentRss>
		<slash:comments>3</slash:comments>
		</item>
		<item>
		<title>Simple continuous test solution via make</title>
		<link>http://www.jeffhung.net/blog/articles/jeffhung/2156/</link>
		<comments>http://www.jeffhung.net/blog/articles/jeffhung/2156/#comments</comments>
		<pubDate>Mon, 31 Aug 2009 05:28:54 +0000</pubDate>
		<dc:creator>jeffhung</dc:creator>
				<category><![CDATA[Devel]]></category>
		<category><![CDATA[Programming]]></category>
		<category><![CDATA[cpp]]></category>
		<category><![CDATA[shellscript]]></category>

		<guid isPermaLink="false">http://www.jeffhung.net/blog/?p=2156</guid>
		<description><![CDATA[Perl hacker gugod demoed Test::Continuous in OSDC.tw 2008 lightning talk. This is a great idea that breaks the code/build/debug cycle so we can continuously focus on the code we're writing and let the build/debug stuffs go background (mentally). But Test::Continuous is a perl module that best suite for perl hacking. I'm a C/C++ programmer now [...]]]></description>
			<content:encoded><![CDATA[<p>Perl hacker gugod <a href="http://gugod.org/2008/04/osdctw.html">demo</a>ed <a href="http://www.slideshare.net/gugod/osdctw-2008-lightening-talk">Test::Continuous in OSDC.tw 2008 lightning talk</a>. This is a great idea that breaks the code/build/debug cycle so we can continuously focus on the code we're writing and let the build/debug stuffs go background (mentally). But Test::Continuous is a perl module that best suite for perl hacking. I'm a C/C++ programmer now and cannot benefit from his work. So I wrote a little script that brute forced the idea with the help of make.</p>
<p>The most complex part of Test::Continuous is to detect whether we should build and test, or just waiting for source file changes. I'm not sure how gugod did that in Perl, but this is absolutely what make(1) good at. The <code class="inline_code">-q</code> argument for GNU make will return an exit status indicating whether specified targets are already up to date or not. So we may write a<ins>n</ins> infinite loop and ask GNU make to check whether depended source <ins>is</ins> changed or not, and really do the build/test if <code class="inline_code">make -q</code> returns nonzero.</p>
<p>And, with the help of <a href="http://blogs.divisibleprime.com/ronin/articles/2008/03/10/command-line-gnome-notification">gnome-cli-notify.py</a>, it is easy to show a notification popup when build/test failed or vice versa.</p>
<p>So, here comes the script. Enjoy.</p>
<pre class="code">
#!/usr/bin/env bash
# ---------------------------------------------------------------------------
# Amarganth Development Environment
# Copyright (c) 2007, Jeff Hung
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions
# are met:
#
#  - Redistributions of source code must retain the above copyright
#    notice, this list of conditions and the following disclaimer.
#  - Redistributions in binary form must reproduce the above copyright
#    notice, this list of conditions and the following disclaimer in the
#    documentation and/or other materials provided with the distribution.
#  - Neither the name of the copyright holders nor the names of its
#    contributors may be used to endorse or promote products derived
#    from this software without specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
# ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL COPYRIGHT
# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
# ---------------------------------------------------------------------------
# $Date: 2009-07-27 10:18:22 +0800 (Mon, 27 Jul 2009) $
# $Rev: 489 $
# $Author: jeffhung $
# ----------------------------------------------------------------------------
# revid: &quot;@(#) $Id: scm.old 489 2009-07-27 02:18:22Z jeffhung $&quot;
# ----------------------------------------------------------------------------

__exe_name__=`basename $0`;
__revision__=`echo '$Rev: 560 $' | cut -d' ' -f2`;
__rev_date__=`echo '$Date: 2007-07-19 11:07:45 +0800 (星期四, 19 七月 2007) $' | cut -d' ' -f2`;

continuous_timeout=5
error_stop_timeout=30

if [ $# -lt 3 ]; then
    echo &quot;\
Usage: continuous-test &lt;build-target&gt; &lt;test-target&gt; &lt;notify-cmd&gt; ...

The continuous-test script make(1) your &lt;test-target&gt; continuously while we
developing.  The script repeatedly asking GNU make(1) to determine whether
&lt;build-target&gt; need to be built or not.  If it needs to be built, build it
and then &lt;test-target&gt;.  If any of &lt;build-target&gt; or &lt;test-target&gt; failed,
run &lt;notify-cmd&gt; to notify we developer.  If build successed, &lt;build-target&gt;
no longer need to be built, and this script go back to check continuously.

This script requires GNU make(1) and bash(1).

Example:

  $ continuous-test all test \\
    \&quot;gnome-cli-notify.py 'Test Failed...' 'Check error console please.'\&quot; \\
    ;

Revision: r$__revision__ ($__rev_date__)&quot;;
    exit;
fi;
build_target=&quot;$1&quot;; shift;
test_target=&quot;$1&quot;; shift;
notify_cmd=&quot;$1&quot;; shift;
while [ $# -gt 0 ]; do
    notify_cmd=&quot;$notify_cmd '$1'&quot;;
    shift;
done;
#echo &quot;$notify_cmd&quot;;

while [ -z '' ]; do
    make -q &quot;$build_target&quot;;
    if [ $? -ne 0 ]; then
        echo &quot;Change detected, build and test!!&quot;;
        rm -f &quot;$temp_file&quot;;
        make &quot;$build_target&quot; &amp;&amp; make &quot;$test_target&quot;;
        if [ $? -ne 0 ]; then
            echo &quot;&gt;&gt;&gt; TEST FAILED!!&quot;;
            if [ -n &quot;$notify_cmd&quot; ]; then
                eval $notify_cmd;
            fi;
            read -n 1 -t $error_stop_timeout \
                 -p &quot;(`date '+%H:%M:%S'`+$error_stop_timeout) Stop continuous test (y/N)? &quot; \
                 ans;
#           if [ \( $? -ne 0 \) -o \( &quot;$ans&quot; = 'y' \) ]; then
            if [ \( $? -eq 0 \) -a \( &quot;$ans&quot; = 'y' \) ]; then
                echo;
                break; # while
            fi;
        fi;
    fi;
    printf &quot;\r&quot;;
    read -n 1 -t $continuous_timeout \
         -p &quot;(`date '+%H:%M:%S'`+$continuous_timeout) Stop continuous test (y/N)? &quot; \
         ans;
    if [ \( $? -eq 0 \) -a \( &quot;$ans&quot; = 'y' \) ]; then
        echo;
        break; # while
    fi;
done;
echo 'End.';
</pre>
<p>This script is written in Bash for the <code class="inline_code">read</code> builtin, cooperate with GNU make.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.jeffhung.net/blog/articles/jeffhung/2156/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
		<item>
		<title>API Name Postfix</title>
		<link>http://www.jeffhung.net/blog/articles/jeffhung/1846/</link>
		<comments>http://www.jeffhung.net/blog/articles/jeffhung/1846/#comments</comments>
		<pubDate>Wed, 01 Jul 2009 14:09:23 +0000</pubDate>
		<dc:creator>jeffhung</dc:creator>
				<category><![CDATA[Devel]]></category>
		<category><![CDATA[Murmuring]]></category>
		<category><![CDATA[Programming]]></category>
		<category><![CDATA[cpp]]></category>

		<guid isPermaLink="false">http://www.jeffhung.net/blog/?p=1846</guid>
		<description><![CDATA[Windows SDK API 裡有個奇怪的命名慣例 (naming convention)，學過 Windows Programming 應該都耳熟能詳：API 的加強版，通常是在原來的名字後面，加上個 Ex 後綴字尾以命名之。例如，CreateWindow() 的加強版，就叫做 CreateWindowEx()。]]></description>
			<content:encoded><![CDATA[<p>來聊聊 API 命名時，後綴字的處理。</p>
<p><strong>版號後綴字</strong></p>
<p>Windows SDK API 裡有個奇怪的命名慣例 (naming convention)，學過 Windows Programming 應該都耳熟能詳：API 的加強版，通常是在原來的名字後面，加上個 <code class="inline_code">Ex</code> 後綴字尾以命名之。例如，<code class="inline_code">CreateWindow()</code> 的加強版，就叫做 <code class="inline_code">CreateWindowEx()</code>。</p>
<p>之所以說這種命名慣例會「奇怪」是因為，如果還要對 <code class="inline_code">Ex</code> 版增強的時候，該怎麼辦？難道還要來一個 <code class="inline_code">CreateWindowExEx()</code> 嗎？那豈不沒完沒了？</p>
<p>針對這種問題，常見的解法有兩種。第一種比較簡單，就是棄 <code class="inline_code">Ex</code> 改數字。例如 <code class="inline_code"><a href="http://www.sqlite.org/c3ref/open.html">sqlite3_open</a>()</code> 與 <code class="inline_code"><a href="http://www.sqlite.org/c3ref/open.html">sqlite3_open_v2</a>()</code> 和 <code class="inline_code">xmlrpc_client_init()</code> 與 <code class="inline_code"><a href="http://xmlrpc-c.sourceforge.net/doc/libxmlrpc_client.html#client_init2">xmlrpc_client_init2</a>()</code>。<a href="http://www.sqlite.org/">SQLite</a> 的命名法比較囉唆，可是應該是因為還要支援 UTF-16 版的緣故；<a href="http://xmlrpc-c.sourceforge.net/">xmlrpc-c</a> 的方法比較常見，個人認為也比較簡潔方便。</p>
<p><strong>型別後綴字</strong></p>
<p>除了利用後綴字來表明版號之外，有時候，函式名稱後綴字也用來表明，其所處理的資料型別。舉例來說，Microsoft 的 <a href="http://www.jeffhung.net/blog/articles/jeffhung/522/">generic-text-mapping</a> 技術，利用 <code class="inline_code">A</code> 或 <code class="inline_code">W</code> 後綴，來區分輸出入使用 <code class="inline_code">char</code> 或 <code class="inline_code">wchar_t</code> 型別。</p>
<p>前述提到 <code class="inline_code">sqlite3_open()</code> 有 UTF-16 版本，命名為 <code class="inline_code"><a href="http://www.sqlite.org/c3ref/open.html">sqlite3_open16</a>()</code>，也是類似的手法。</p>
<p>我個人覺得，這種後綴字還算可以接受，可是如果是在用 C++，那就很搞笑了。事實上，若有機會用 C++ 把這類 C 的 API 包裝起來的話，我都會利用 overloading 機制，把型別後綴字幹掉，圖個清爽。</p>
<p><strong>行為後綴字</strong></p>
<p>另一種常見的 API 後綴字，代表著函式的行為不太一樣。例如很多 non-thread-safe 的 API，其 <a href="http://en.wikipedia.org/wiki/Reentrant_(subroutine)">reentrant</a> 版，也就是可安全地重複使用的版本，會在名稱後面加上 <code class="inline_code">_r</code> 的後綴字。例如 <code class="inline_code">strtok()</code> 的 reentrant 版叫 <code class="inline_code">strtok_r()</code>。通常加上 <code class="inline_code">_r</code> 的 API，其參數都會不太一樣，否則直接解掉問題即可，沒必要再另外發明一個 API。</p>
<p>Microsoft 在 Visual C++ 裡，對這類 non-thread-safe 的 API，解法不太一樣。使用 multithread 版 run-time library 時，若要從 non-thread-safe 變成 thread-safe 時，參數可以一樣不變，則同上述直接改掉實作，解掉問題，API 不變。可是，當碰到需要修改 prototype 更動參數時，Microsoft 用的是骯髒的手段，如利用 <a href="http://en.wikipedia.org/wiki/Thread-local_storage">TLS</a> 儲存需要增加的參數，使得 API 還是不變。原本因呼叫了 non-thread-safe API 的程式，立地成佛變成 thread-safe。</p>
<p>這其實會產生一些問題，<a href="http://blog.hubert.tw/2009/04/18/%E5%BE%9E-inet_ntoa-%E7%9C%8B-thread-safe-%E7%9A%84-api/">妨礙 porting</a> 我認為問題還小，讓 programmer 自以為安全，才是真正的隱憂。我認為，Microsoft 應該要做的，其實是當使用 multithread 版 run-time library 時，像 security enhanced crt 那樣，在 compile 時產生 deprecation warning，建議使用 reentrant 版 API 才對。</p>
<p>講到 VC 的 <a href="http://msdn.microsoft.com/en-us/library/8ef0s5kh.aspx">security enhanced crt</a> 就好笑，Microsoft 會在有安全疑慮的 API 後面，加上個 <code class="inline_code">_s</code> 後綴字。但問題是，M$ 根本沒有在管，參數是否相同，只要有安全疑慮，即使參數相同，也會發明一個對應的 <code class="inline_code">_s</code> 版 API。若目的是為了讓 programmer 可以認知道其實這個 API 並不安全，那就很搞笑了，參數相同時，讓 API 實作能夠安全，不正是 vendor 的責任嗎？更別提，<a href="http://fsfoundry.org/codefreak/2008/09/15/security-crt-safer-than-standard-library/">實際上 <code class="inline_code">_s</code> 版 API，並沒有多安全</a>的問題了。</p>
<p><strong>隨便亂選後綴字</strong></p>
<p>講了這麼多，其實我是想碎碎念，新同事你實作 thread-safe 版 API，在後面加上 <code class="inline_code">_s</code> 後綴字是想怎樣？畫虎不成反類犬，真是厲害。</p>
<p>還好我不再需要面對這種隨便亂選後綴字的 API 了。阿彌陀佛，善哉，善哉。</p>
]]></content:encoded>
			<wfw:commentRss>http://www.jeffhung.net/blog/articles/jeffhung/1846/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>libcurl, openssl, and ca-bundle - on Windows</title>
		<link>http://www.jeffhung.net/blog/articles/jeffhung/1966/</link>
		<comments>http://www.jeffhung.net/blog/articles/jeffhung/1966/#comments</comments>
		<pubDate>Tue, 30 Jun 2009 14:18:35 +0000</pubDate>
		<dc:creator>jeffhung</dc:creator>
				<category><![CDATA[Devel]]></category>
		<category><![CDATA[Programming]]></category>
		<category><![CDATA[cpp]]></category>

		<guid isPermaLink="false">http://www.jeffhung.net/blog/?p=1966</guid>
		<description><![CDATA[整理一下在 Windows 上，應付 libcurl、openssl 與 ca-bundle 的筆記。]]></description>
			<content:encoded><![CDATA[<p>整理一下在 Windows 上，應付 libcurl、openssl 與 ca-bundle 的筆記。</p>
<p>我們需要讓使用的 libcurl 支援 SSL，目標是要讓後續的開發，只在 IDE 進行即可，必須要做到從 repository 裡 checkout 出來，只要打開 IDE 就可以做完所有的事。</p>
<p>原本 curl 的 source tarball 裡，有包含了 <code class="inline_code">lib/curllib.dsp</code> 這個 VC6 的 project 檔，但很可惜的，這是給 <a href="http://en.wikipedia.org/wiki/Newbie">newbie</a> 用的，所以不包含 3rd-party 組件，如 OpenSSL。見 <code class="inline_code">docs/INSTALL</code> 檔：</p>
<blockquote>
<p>Intentionally, these reference VC++ 6.0 projects and configurations don't use third party libraries, such as OpenSSL or Zlib, to allow proper compilation and configuration for all new users without further requirements.</p>
</blockquote>
<p>但是我們已經使用這個 <code class="inline_code">curllib.dsp</code> 建立好了 for IDE 的開發環境，所以必須想辦法，把「老鳥的手段」放進「菜鳥的籃子」裡。</p>
<p>為了少編譯一個 OpenSSL 套件，我選擇使用 <a href="http://gnuwin32.sourceforge.net/packages/openssl.htm">GnuWin32 預先編譯好的 native openssl package</a>，抓下安裝程式，安裝到預設的目錄即可。</p>
<p>OpenSSL 共有兩個 DLL 檔：<code class="inline_code">libeay32.dll</code> 與 <code class="inline_code">libssl32.dll</code>。可是，GnuWin32 這個預先編譯好的套件，雖然說包含了 development files，但沒有包含 VC6 用的 DLL import library 或 def 檔，所以必須自己生出來。</p>
<p>一開始我先試著從 GnuWin32 版 OpenSSL 的 source tarball (<code class="inline_code">openssl-0.9.8h-1-src.zip</code>)，解出裡面的 libeay32.def 與 ssleay32.def，以製作對應的 import libraries：</p>
<pre class="code">
SHELL&gt; RENAME ssleay32.def libssl32.def
SHELL&gt; &quot;C:\Program Files\Microsoft VisualStudio\VC98\Bin\VCVARS32.BAT&quot;
SHELL&gt; LIB /DEF:libeay32.def
SHELL&gt; LIB /DEF:libssl32.def
</pre>
<p>其中，<code class="inline_code">ssleay32.def</code> 其實是 <code class="inline_code">libssl32.dll</code> 對應的 <code class="inline_code">DEF</code> 檔，故先正名之。然後引入 VC6 開發環境，呼叫 <code class="inline_code">LIB.exe</code> 工具，從 <code class="inline_code">DEF</code> 檔做出 import libraries。</p>
<p>測試結果發現，程式一啟動，就跳出視窗抱怨，找不到 <code class="inline_code">libeay32.dll</code> 序號 968 的 API。用 <code class="inline_code">depends.exe</code> 查，ordinal 968 這個 API 根本就是 N/A，從 <code class="inline_code">libeay32.def</code> 裡也找不到編號 968。</p>
<p>所以只好自己來，利用 <code class="inline_code">DUMPBIN.exe</code> 工具，直接從 DLL 裡列出 exported symbols，如下：</p>
<pre class="code">
SHELL&gt; DUMPBIN /EXPORTS libeay32.dll &gt; libeay32.def
SHELL&gt; DUMPBIN /EXPORTS libssl32.dl
</pre>
<p>然後編輯這兩個 <code class="inline_code">.DEF</code> 檔，把無關的東西去掉，僅剩下 exported symbols，弄成下面這個樣子：</p>
<pre class="code">
EXPORTS
        symbol1
        symbol2
        ...
</pre>
<p>從第二行開始，使用 TAB 字元縮排。</p>
<p>然後重複上面的程序，利用 <code class="inline_code">LIB.exe</code> 工具，製作出 import libraries，這次就可以正常使用了。把產生的 <code class="inline_code">.def</code>、<code class="inline_code">.lib</code> 與 <code class="inline_code">.exp</code> 檔，放到 <code class="inline_code">%ProgramFiles%\GnuWin32\lib</code> 目錄下，把兩個 DLL，放到 <code class="inline_code">%ProgramFiles%\GnuWin32\bin</code> 目錄下，然後如下修改 <code class="inline_code">curllib.dsp</code> 的 project setting，即可正常編譯出具備 SSL 功能的 libcurl：</p>
<ul>
<li>Add preprocessor definition: <code class="inline_code">USE_SSLEAY</code>；</li>
<li>Add include directories: <code class="inline_code">%ProgramFiles%\GnuWin32\include</code> 與 <code class="inline_code">%ProgramFiles%\GnuWin32\include\openssl</code>；</li>
<li>Add additional library path: <code class="inline_code">%ProgramFiles%\GnuWin32\lib</code>；</li>
<li>Add additional libraries: <code class="inline_code">libeay32.lib</code> 與 <code class="inline_code">libssl32.lib</code>。</li>
</ul>
<p>不過，要正常使用 SSL 功能，還是必須想辦法搞到 <a href="http://en.wikipedia.org/wiki/Certificate_Authority">CA</a> 給 curl 用才行。目前最新的 curl 版本，已經不再內附 CA certifications 了，所以我們要自己生才行。查 curl 文件，我們需要 <code class="inline_code">PEM</code> 格式的 CA。本來想說直接開 IE 或 firefox 把裡面用的 CA 匯出，可是發現沒辦法匯出成 <code class="inline_code">PEM</code> 格式。還好翻查 FreeBSD 裡灌的 curl，在 <code class="inline_code">/usr/local/share/curl</code> 目錄下，可以找到 <code class="inline_code">curl-ca-bundle.crt</code> 檔，將之如下傳給 CURLOPT_CAINFO 選項即可：</p>
<pre class="code">
curl_easy_setopt(handle, CURLOPT_CAINFO, &quot;/path/to/curl-ca-bundle.crt&quot;);
</pre>
<p>至此，大功告成。</p>
<p>參考資料：</p>
<ul>
<li><a href="http://www.vividreflection.com/blog/secret-to-curl-in-php-on-windows/">The Secret to cURL in PHP on Windows...</a></li>
</ul>
]]></content:encoded>
			<wfw:commentRss>http://www.jeffhung.net/blog/articles/jeffhung/1966/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>用程式檢查程式 - 一定要引入的 header</title>
		<link>http://www.jeffhung.net/blog/articles/jeffhung/1205/</link>
		<comments>http://www.jeffhung.net/blog/articles/jeffhung/1205/#comments</comments>
		<pubDate>Mon, 29 Jun 2009 14:25:20 +0000</pubDate>
		<dc:creator>jeffhung</dc:creator>
				<category><![CDATA[Devel]]></category>
		<category><![CDATA[Programming]]></category>
		<category><![CDATA[cpp]]></category>
		<category><![CDATA[perl]]></category>

		<guid isPermaLink="false">http://www.jeffhung.net/blog/?p=1205</guid>
		<description><![CDATA[專案一大，就會設定一些「慣例」，以簡化專案複雜度。但是我們無法叫 compiler 幫我們檢查「慣例」，久而久之，這些「慣例」反而會成為混亂的來源。因此，最終還是得想個辦法，自動檢查這些「慣例」是否有被遵守才行。例如，檢查某個一定要被引入的 header 有否被引入。]]></description>
			<content:encoded><![CDATA[<p>專案一大，就會設定一些「慣例」，以簡化專案複雜度。但是我們無法叫 compiler 幫我們檢查「慣例」，久而久之，這些「慣例」反而會成為混亂的來源。因此，最終還是得想個辦法，自動檢查這些「慣例」是否有被遵守才行。例如，檢查某個一定要被引入的 header 有否被引入。</p>
<p>我習慣一個函式庫，會有一個 <code class="inline_code">config.h</code>，將所有 compile-time options 放在裡面，並規定函式庫裡的所有 header/source files 都一定要引入此 <code class="inline_code">config.h</code>，這樣子裡面的設定，才能廣為周知。</p>
<p>假設這個函式庫叫 <code class="inline_code">messy</code>，則 <code class="inline_code">config.h</code> 會位於 <code class="inline_code">include/messy/config.h</code>，平常以 <code class="inline_code">#include &lt;messy/config.h&gt;</code> 引入。那我們可以用以下的 perl 程式片段，檢查是否每個 header/source files 皆有引入 <code class="inline_code">config.h</code>：</p>
<pre class="code">
use File::Grep;
use File::Find::Rule;

print &quot;CHECK: C/C++ sources must include &lt;messy/config.h&gt;.\n&quot;;
print join(
    &quot;\n&quot;,
    map { &quot;| $_-&gt;{'filename'}&quot; }
    grep { $_-&gt;{'count'} == 0 }
    File::Grep::fgrep { /^\s*#\s*include\s+&lt;messy\/config.h&gt;/ }
    grep { $_ !~ m/messy\/config.h$/o }
    File::Find::Rule-&gt;or(
        File::Find::Rule-&gt;directory-&gt;name('.svn')-&gt;prune-&gt;discard,
        File::Find::Rule-&gt;file()-&gt;name(qr/\.(h|hpp|c|cpp)$/i)
    )-&gt;in('include', 'src')
) || &quot;[ok]&quot;;
print &quot;\n\n&quot;;</pre>
<p>把這段程式，放在 post-build 裡，每次編譯函式庫時，最後就會執行，檢查除了 <code class="inline_code">include/messy/config.h</code> 以外的所有 header/source 檔案，是否皆有引入 <code class="inline_code">include/messy/config.h</code>，沒有引入的檔名，就會列出來，若統統都有引入，就會印出 <code class="inline_code">[ok]</code> 字樣。如下：</p>
<pre class="code">
CHECK: C/C++ sources must include &lt;messy/config.h&gt;.
| include/messy/debug_tool/func_trace.hpp
| src/messy.debug_tool.func_trace.cpp
</pre>
<p>修正後就會變成如下：</p>
<pre class="code">
CHECK: C/C++ sources must include &lt;messy/config.h&gt;.
[ok]
</pre>
<p>檢查結果會一併出現在 build log 的最後面，有需要時，就可以依據指定的「慣例」，去修正那些有問題的檔案。</p>
]]></content:encoded>
			<wfw:commentRss>http://www.jeffhung.net/blog/articles/jeffhung/1205/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Building VC Projects in Console Mode</title>
		<link>http://www.jeffhung.net/blog/articles/jeffhung/1922/</link>
		<comments>http://www.jeffhung.net/blog/articles/jeffhung/1922/#comments</comments>
		<pubDate>Fri, 08 May 2009 09:52:13 +0000</pubDate>
		<dc:creator>jeffhung</dc:creator>
				<category><![CDATA[Devel]]></category>
		<category><![CDATA[Programming]]></category>
		<category><![CDATA[cpp]]></category>

		<guid isPermaLink="false">http://www.jeffhung.net/blog/?p=1922</guid>
		<description><![CDATA[通常我們在 Visual C++ 裡開發，是以其圖形介面的 IDE 為主要操作環境。若為了要把一些工作自動化，就必須要找到方法，在 console mode 下編譯建置專案。]]></description>
			<content:encoded><![CDATA[<p>通常我們在 Visual C++ 裡開發，是以其圖形介面的 IDE 為主要操作環境。若為了要把一些工作自動化，就必須要找到方法，在 console mode 下編譯建置專案。</p>
<p>因為工作氛圍之故，無法擺脫 Visual C++ IDE 那一套 build configuration 機制，所以只好寫了這一個 vc-build.bat 的批次檔，直接在 console mode 下進行原本要按按鈕、點選單，才能做的事。</p>
<p>程式碼在此：<a href="http://github.com/jeffhung/blog-share/blob/8cb0d589e7415582204167e7d5d61083db7bfda5/vc-build.bat">vc-build.bat on github</a>，使用起來很簡單：</p>
<pre class="code">
------------------------------------------------------------------------------
Usage: vc-build.bat [vc] [solution] [project] [configuration]
Usage: vc-build.bat [vc] [solution] [project] [configuration] [action]
------------------------------------------------------------------------------
            [vc] could be vc6, vc8, or vc9.
      [solution] is the .dsw or .sln file.
       [project] is the project to build.
 [configuration] could be Debug or Release, normally.
        [action] could be BUILD, REBUILD, or CLEAN (case insentively).</pre>
<p>依據以上的參數說明，執行即可。包括於 project 檔裡設定好的 pre/post events 的外部程式，都會正確執行。除了可以與 <a href="http://buildbot.net/">buildbot</a> 這類 <a href="http://en.wikipedia.org/wiki/Continuous_integration">continuous integration</a> 工具整合之外，設定進 <a href="http://www.vim.org">vim</a> 的 <code class="inline_code">makeprg</code> 參數，亦是擺脫 IDE 的好方法。</p>
<p>參考資料：</p>
<ul>
<li><a href="http://avhacker.blogspot.com/2007/07/incredibuild-console-build-project.html">Incredibuild 在 console 模式 build project 的語法</a></li>
<li>MSDN - <a href="http://msdn.microsoft.com/en-us/library/xee0c8y7.aspx">Devenv Command Line Switches</a></li>
</ul>
]]></content:encoded>
			<wfw:commentRss>http://www.jeffhung.net/blog/articles/jeffhung/1922/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
		<item>
		<title>wp2docbook.pl - DocBook from wordpress export</title>
		<link>http://www.jeffhung.net/blog/articles/jeffhung/1006/</link>
		<comments>http://www.jeffhung.net/blog/articles/jeffhung/1006/#comments</comments>
		<pubDate>Thu, 07 May 2009 14:55:06 +0000</pubDate>
		<dc:creator>jeffhung</dc:creator>
				<category><![CDATA[Programming]]></category>
		<category><![CDATA[perl]]></category>
		<category><![CDATA[web]]></category>
		<category><![CDATA[wordpress]]></category>

		<guid isPermaLink="false">http://www.jeffhung.net/blog/?p=1006</guid>
		<description><![CDATA[看到 gslin 最近連續寫了兩篇文章，談論 wordpress 的 export 檔，就想起之前在 wordpress 2.3 的時候，寫過一個 wp2docbook.pl，可以將 wordpress export 檔，轉成 DocBook 格式。]]></description>
			<content:encoded><![CDATA[<p>看到 <a href="http://blog.gslin.org/about/">gslin</a> 最近連續寫了兩篇<a href="http://blog.gslin.org/archives/2009/04/24/2002/">文章</a>，談論 <a href="http://blog.gslin.org/archives/2009/05/07/2016/">wordpress 的 export 檔</a>，就想起之前在 <a href="http://www.wordpress.org/">wordpress</a> 2.3 的時代，寫過一個 <code class="inline_code">wp2docbook.pl</code>，可以將 wordpress export 檔，轉成 <a href="http://www.google.com/url?sa=t&amp;source=web&amp;ct=res&amp;cd=1&amp;url=http%3A%2F%2Fwww.docbook.org%2F&amp;ei=DXwCSq6WA5uG6AO1-4GYAw&amp;usg=AFQjCNHtErWInFJy1-AwBBy3I2J3NS2ZoA&amp;sig2=vMWJRAXlTRNSiHuoyjayjg">DocBook</a> 格式。</p>
<p>轉成 DocBook 格式有很多好處，除了是一種 wordpress independent 備份方式外，亦可以再輸出成 PDF、Word 等做更進一步的處理。有時候要把文章給別人的時候，不方便直接把網頁印下來，若有 DocBook 版，就可以先轉成 word，再修補一下就搞定了。</p>
<p>這個 <code class="inline_code">wp2docbook.pl</code>，讀入 wordpress export 後，會試著理解文章裡的 HTML 碼，轉換成對應的 DocBook 標籤。可以輸出成一個超級大 DocBook 檔，裡面有很多 <code class="inline_code">&lt;article&gt;</code>，也可以每一個 <code class="inline_code">&lt;article&gt;</code> 輸出一個檔，方便備份整理。我用自己的 blog 測試過，六百多篇文章，沒什麼問題。</p>
<p>不過那是在那個時候。XD</p>
<p>如同 gslin 所述，wordpress 的 export 檔，格式問題很多。在寫 <code class="inline_code">wp2docbook.pl</code> 的時候，就搞了很多 hacks，試著解決格式問題。這些 hacks 雖然沒有像 gslin 那樣暴力，但也差不多了。最後發現，export 檔格式一直在變，隨著版本更新，有的欄位修好了，有的欄位又爛了。後來覺得，跟著 wordpress 一直修修補補轉檔程式，實在很沒意思，就放棄更新了。</p>
<p>本來那時就要撰文記錄之，但標題有了就擺在 draft 好久。現在既然提到了，那就釋出吧，順便練練 <a href="http://git-scm.com/">git</a> 與 <a href="http://github.com/">github </a>的使用。這個版本已經不能處理目前最新的 wordpress 2.7.1 版的 export 檔了，不過以 <a href="http://www.opensource.org/licenses/bsd-license.php">BSDL</a> 釋出，有需要的人就自己再 hack 吧。</p>
<p>請享用：<a href="http://github.com/jeffhung/blog-share/blob/233b461d10eb48d8141cc7f12421b157d07fd030/wp2docbook.pl">wp2docbook on github</a>。</p>
]]></content:encoded>
			<wfw:commentRss>http://www.jeffhung.net/blog/articles/jeffhung/1006/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>字串處理風格</title>
		<link>http://www.jeffhung.net/blog/articles/jeffhung/1870/</link>
		<comments>http://www.jeffhung.net/blog/articles/jeffhung/1870/#comments</comments>
		<pubDate>Tue, 21 Apr 2009 14:44:01 +0000</pubDate>
		<dc:creator>jeffhung</dc:creator>
				<category><![CDATA[Programming]]></category>
		<category><![CDATA[cpp]]></category>
		<category><![CDATA[visualbasic]]></category>

		<guid isPermaLink="false">http://www.jeffhung.net/blog/?p=1870</guid>
		<description><![CDATA[最近有在用 wxWidgets 寫一些東西練功，初步的感想是…]]></description>
			<content:encoded><![CDATA[<p>最近有在用 <a href="http://www.wxwidgets.org/">wxWidgets </a>寫一些東西練功，初步的感想是，wxWidgets 其實走得不是 <a href="http://en.wikipedia.org/wiki/Microsoft_Foundation_Class_Library">MFC</a> 風格，而是 <a href="http://en.wikipedia.org/wiki/Visual_Basic">VB</a> 風格。然後我看到了這篇《<a href="http://www.planet-source-code.com/vb/scripts/ShowCode.asp?txtCodeId=169&amp;lngWId=10">_ A String manipulation example in VB.NET, EQUIVALENTS: Len, Mid, Replace, InStr, UCase, Split etc _ </a>》，就想到，對啊，這也是證據之一，<code class="inline_code"><a href="http://docs.wxwidgets.org/stable/wx_wxstring.html">wxString</a></code> 的介面，幾乎就跟 VB 一樣嘛。</p>
<p>不過我還是不習慣 VB/wxWidgtes 的字串處理風格，感覺上太沒有效率了，邊寫就會邊想，這如果是在 C/C++，可以怎樣寫，效率可以好上多少倍。</p>
<p>所以我還是直接用 <code class="inline_code">std::string</code> 寫好了，字串處理完畢，再轉成 <code class="inline_code">wxString</code> 送回去。</p>
<p>實際上，用 MFC 的 CString 也是一整個彆扭，個人感覺甚至比 VB 的字串、<code class="inline_code">wxString</code> 還要來的更糟。</p>
]]></content:encoded>
			<wfw:commentRss>http://www.jeffhung.net/blog/articles/jeffhung/1870/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
		<item>
		<title>650 行的 constructor</title>
		<link>http://www.jeffhung.net/blog/articles/jeffhung/1838/</link>
		<comments>http://www.jeffhung.net/blog/articles/jeffhung/1838/#comments</comments>
		<pubDate>Mon, 13 Apr 2009 06:51:04 +0000</pubDate>
		<dc:creator>jeffhung</dc:creator>
				<category><![CDATA[Devel]]></category>
		<category><![CDATA[Murmuring]]></category>
		<category><![CDATA[Programming]]></category>
		<category><![CDATA[Work]]></category>
		<category><![CDATA[database]]></category>

		<guid isPermaLink="false">http://www.jeffhung.net/blog/?p=1838</guid>
		<description><![CDATA[原來我也會寫出 650 行的 constructor。XD]]></description>
			<content:encoded><![CDATA[<p><span class="status-body"><span class="entry-content">原來我也會寫出 650 行的 constructor，滾輪轉半天，還看不到放在下面的 destructor。XD</span></span></p>
<p><span class="status-body"><span class="entry-content">哼哼，一切都是註解太多的錯。</span></span></p>
<p><span class="status-body"><span class="entry-content">另一個錯的是</span></span> Windows Programming 的 style，要不是已經把 error handling 另外濃縮處理過了，否則不含註解的部份，還要再膨脹個 5 倍左右。</p>
]]></content:encoded>
			<wfw:commentRss>http://www.jeffhung.net/blog/articles/jeffhung/1838/feed/</wfw:commentRss>
		<slash:comments>5</slash:comments>
		</item>
		<item>
		<title>Who&#039;s code caught the error, who deal with it?</title>
		<link>http://www.jeffhung.net/blog/articles/jeffhung/1016/</link>
		<comments>http://www.jeffhung.net/blog/articles/jeffhung/1016/#comments</comments>
		<pubDate>Sun, 05 Apr 2009 14:53:14 +0000</pubDate>
		<dc:creator>jeffhung</dc:creator>
				<category><![CDATA[Devel]]></category>
		<category><![CDATA[Murmuring]]></category>
		<category><![CDATA[Programming]]></category>
		<category><![CDATA[Work]]></category>

		<guid isPermaLink="false">http://www.jeffhung.net/blog/?p=1016</guid>
		<description><![CDATA[這是一個很久很久以前就有的 murmur。在工作上，有時候會聽到這樣的怒吼：「吼～又是你的 assertion 把程式當掉。」可是…]]></description>
			<content:encoded><![CDATA[<p>這是一個很久很久以前就有的 murmur。</p>
<p>在工作上，有時候會聽到這樣的怒吼：「吼～又是你的 assertion 把程式當掉。」可是，細查之後卻又發現，之所以會 assertion failure，是某個應該存在的值不存在，或某件事情在之前就失敗了，於是當程式執行到 assertion 處，一檢查就發現問題，然後把程式當掉。</p>
<p>Assertion 之所以存在，為的就是希望能夠揪出，所有不應該存在的異常情形，是故，assertion 其實應該是多多益善，埋的越多，越有機會揪出程式的不良之處。所以，我很喜歡埋 assertion，凡是無法處理的特殊輸入、必須存在的情境參數，甚至是失敗就等於整個程式根本無法運作的 API 呼叫，我都會使用 assertion 以便盡早地把程式當掉，輔助我們揪出錯誤的發生點。</p>
<p>問題是，當 assertion 發生時，總是要有某個人負責去找出問題的成因，而理所當然地，在只有 assertion failure 這條線索的情況下，問題就被丟給寫 assertion 的人。但因為 assertion 通常是用來抓「不是在此處發生的錯誤」，因此產生了這麼一個吊詭：</p>
<p><strong>越認真地(寫程式)使錯誤顯現的人，反而越容易領到爛攤子，必須承擔解決(多半)不是其程式所造成之錯誤的責任。</strong></p>
<p>一般來說，程式設計師比較喜歡寫新程式，而不喜歡維護舊程式，更討厭的就是負責維護不是他寫的舊程式，所以上述的吊詭，等於是在懲罰認真的人，而且是越認真(埋 assertion 炸彈)，等著的是越多的爛攤子。</p>
<p>也就是說，可以討論的，應該是 assertion 的標的是否合適，而不是 assertion 是否應該把程式當掉。只要標的合適，當程式當掉時，我們該慶幸，assertion 讓我們發現，程式會當掉，而不是等到移交給客戶後，隱藏的炸彈才一個一個爆發。</p>
]]></content:encoded>
			<wfw:commentRss>http://www.jeffhung.net/blog/articles/jeffhung/1016/feed/</wfw:commentRss>
		<slash:comments>3</slash:comments>
		</item>
		<item>
		<title>Marginal Leak vs. Fixed Leak</title>
		<link>http://www.jeffhung.net/blog/articles/jeffhung/1787/</link>
		<comments>http://www.jeffhung.net/blog/articles/jeffhung/1787/#comments</comments>
		<pubDate>Sun, 29 Mar 2009 14:44:06 +0000</pubDate>
		<dc:creator>jeffhung</dc:creator>
				<category><![CDATA[Programming]]></category>

		<guid isPermaLink="false">http://www.jeffhung.net/blog/?p=1787</guid>
		<description><![CDATA[在實務上，一般我們將 resource leak 分成兩大類，一種是程式執行一次，僅會發生一次的 leak，另一種則會隨著每次運算而屢次發生的 leak。今天我突發奇想，這兩者，是否可稱之為「fixed leak」與「marginal leak」？]]></description>
			<content:encoded><![CDATA[<p>在實務上，一般我們將 <a href="http://en.wikipedia.org/wiki/Resource_leak">resource leak</a> 分成兩大類，一種是程式執行一次，僅會發生一次的 leak，另一種則會隨著每次運算而屢次發生的 leak。今天我突發奇想，這兩者，是否可稱之為「fixed leak」與「marginal leak」？<strong><br />
</strong></p>
<p>在經濟學裡，「<a href="http://en.wikipedia.org/wiki/Marginal_cost">邊際成本 (marginal cost)</a>」指會隨著每單位生產的新產品而增加的成本，例如製作每個麵包，所需要之麵粉的購買成本。<a href="http://zh.wikipedia.org/w/index.php?title=%E8%BE%B9%E9%99%85%E6%88%90%E6%9C%AC&amp;variant=zh-tw">Wikipedia 如是說</a>：</p>
<blockquote>
<p>在經濟學和金融學中，邊際成本指的是每一單位新增生產的產品（或者購買的產品）帶來到總成本的增量。</p>
</blockquote>
<p>與之相對的則是「<a href="http://en.wikipedia.org/wiki/Fixed_cost">固定成本 (fixed cost)</a>」，不會隨著業務量而變化的成本，如烘培麵包所需之烤爐的購置成本。<strong><br />
</strong></p>
<p>而當在寫程式的時候，可能會碰到的 resource leak，其行為也可以分成兩大類：程式執行一次，不管做了多少事，固定會洩漏一定量的 leak，以及隨著做的事情越多，洩漏的量越多的 leak。</p>
<p>套用經濟學的概念，我在想，其實可以把上面這兩大類 resource leak，稱呼為：</p>
<ul>
<li>fixed leak：程式執行一次，不管做了多少事，固定會洩漏一定量的 leak；</li>
<li>marginal leak：隨著做的事情越多，洩漏的量越多的 leak。</li>
</ul>
<p>其中，marginal leak 較為嚴重，會隨著程式執行越久，吃掉越多的資源，最後導致資源不足，使程式當掉，甚至當掉整台電腦。</p>
<p>而 fixed leak 問題較小，若時程不夠，通常我們會忽略之。不過要小心的就是，若程式支援某種 reset 機制，如某些 server 收到 <code class="inline_code">SIGHUP</code> 會重讀設定檔，則可能某些 fixed leak 會現出真面目，其實是 marginal leak，會隨著 reset 而增加洩漏的量。</p>
<p>好奇，用加雙引號的 &quot;marginal leak&quot; 查了一下 google，發現只有 160 筆。區分這兩種 leak，一般都是用什麼專有名詞啊？</p>
]]></content:encoded>
			<wfw:commentRss>http://www.jeffhung.net/blog/articles/jeffhung/1787/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>Code Review 什麼時候沒有用？</title>
		<link>http://www.jeffhung.net/blog/articles/jeffhung/1776/</link>
		<comments>http://www.jeffhung.net/blog/articles/jeffhung/1776/#comments</comments>
		<pubDate>Mon, 23 Mar 2009 01:49:53 +0000</pubDate>
		<dc:creator>jeffhung</dc:creator>
				<category><![CDATA[Murmuring]]></category>
		<category><![CDATA[Programming]]></category>
		<category><![CDATA[Work]]></category>

		<guid isPermaLink="false">http://www.jeffhung.net/blog/?p=1776</guid>
		<description><![CDATA[今天早上一上班，看到上禮拜下班前跑的測試，果然還是死掉了。 面對體無完膚，多個臟器破裂，到處都是還沒找到的破洞，在這個情況之下，這種內出血嚴重的程式模組，code review 已經沒有用了。 更別提原作者在會中，一直嚷嚷，看起來都沒問題啊。XD 2009-03-23 13:00 更新：補一下這篇《The only valid measurement of code quality: WTF/m》。]]></description>
			<content:encoded><![CDATA[<p>今天早上一上班，看到上禮拜下班前跑的測試，果然還是死掉了。</p>
<p>面對體無完膚，多個臟器破裂，到處都是還沒找到的破洞，在這個情況之下，這種內出血嚴重的程式模組，code review 已經沒有用了。</p>
<p>更別提<a href="http://www.jeffhung.net/blog/articles/jeffhung/861/">原作者</a>在會中，一直嚷嚷，看起來都沒問題啊。XD</p>
<p><strong>2009-03-23 13:00 更新：</strong>補一下這篇《<a href="http://www.osnews.com/story/19266/WTFs_m">The only valid measurement of code quality: WTF/m</a>》。</p>
]]></content:encoded>
			<wfw:commentRss>http://www.jeffhung.net/blog/articles/jeffhung/1776/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>真㊣的 porting</title>
		<link>http://www.jeffhung.net/blog/articles/jeffhung/1744/</link>
		<comments>http://www.jeffhung.net/blog/articles/jeffhung/1744/#comments</comments>
		<pubDate>Thu, 19 Mar 2009 13:17:14 +0000</pubDate>
		<dc:creator>jeffhung</dc:creator>
				<category><![CDATA[Programming]]></category>
		<category><![CDATA[cpp]]></category>
		<category><![CDATA[socket]]></category>
		<category><![CDATA[tcpip]]></category>

		<guid isPermaLink="false">http://www.jeffhung.net/blog/?p=1744</guid>
		<description><![CDATA[在工作上，常常需要把同樣的功能，porting 到不同的平台上去。有些 porting 很簡單，找出對應的 API 代換即可，但有些 porting，就真的很需要對各個平台，非常細緻的理解與熟習。]]></description>
			<content:encoded><![CDATA[<p>在工作上，常常需要把同樣的功能，porting 到不同的平台上去。有些 porting 很簡單，找出對應的 API 代換即可，但有些 porting，就真的很需要對各個平台，非常細緻的理解與熟習。</p>
<p>對應的 API 代換很簡單，例如 <code class="inline_code">pthread_create()</code> 換到 Win32 平台，可以用 <code class="inline_code">_beginthreadex()</code> 代替，頂多需要注意一下參數的不同即可。</p>
<p>有些 API 代換則比較複雜一些，牽涉到程式邏輯的不同。不過因為在功能面上是對等的，只是寫法不同，所以也不是什麼大問題。</p>
<p>例如，「列出目錄內容」這件事，在 UNIX 上要用 <code class="inline_code">opendir</code>/<code class="inline_code">readdir</code>/<code class="inline_code">closedir</code> 這組函式：</p>
<pre class="code">
void list_dir(const char* dir)
{
    DIR* d = opendir(dir);
    dirent* e;
    while ((e = readdir(d)) != NULL) {
        printf(&quot;%s\n&quot;, e-&gt;d_name);
    }
    closedir(d);
}
</pre>
<p>但是到了 Windows 上，則要用 <code class="inline_code">FindFirstFile</code>/<code class="inline_code">FindNextFile</code>/<code class="inline_code">FindClose</code> 這組函式：</p>
<pre class="code">
void list_dir(const char* dir)
{
    // prepare wildcard path
    char wild[MAX_PATH];
    strcpy(wild, dir);
    strcat(wild, &quot;\\*&quot;);

    WIN32_FIND_DATA wfd;
    memset(&amp;wfd, 0, sizeof(wfd));
    HANDLE h = FindFirstFile(wild, &amp;wfd);
    if (h == INVALID_HANDLE_VALUE) {
        throw &quot;FindFirstFile() failed.&quot;;
    }
    do {
        printf(&quot;%s\n&quot;, wfd.cFileName);
    } while (FindNextFile(h, &amp;wfd));
    FindCloseFile(h);
}
</pre>
<p>兩組函式的寫法不太一樣，差別在於，第一筆資料在 UNIX 上，同其他筆資料，係由 <code class="inline_code">readdir()</code> 取得，但在 Windows 上，卻是第一筆資料由 <code class="inline_code">FindFirstFile()</code> 取得，其他筆資料由 <code class="inline_code">FindNextFile()</code> 取得。這點差異，導致了兩邊的程式寫法不太一樣。</p>
<p>不過如何，最近我碰到的這個例子，才真的叫難。</p>
<p>話說 socket programming 是由 BSD (UNIX) 發展出來的，之後才移植到各個平台，如 Windows 上的 WinSock。當一條 TCP 連線使用完畢時，我們可以呼叫 <code class="inline_code">close()</code> 將這個 socket 關閉，也可以用 <code class="inline_code">shutdown()</code> 取代 <code class="inline_code">close()</code>。</p>
<p>在 UNIX 裡，<code class="inline_code">close()</code> 會做 reference counting，若有多個 socket 對應到同一條 TCP connection，則只有當最後一個 socket 被 <code class="inline_code">close()</code> 時，這條 TCP connection 才真的會被結束掉。這通常發生於使用 <code class="inline_code">fork()</code> 來處理 incoming connection 時，母行程 <code class="inline_code">accept()</code> 得到 socket 後，會呼叫 <code class="inline_code">fork()</code> 複製出子行程來處理。此時，母行程與子行程各自擁有一個 socket，同時對應到這條 TCP connection。當母行程 <code class="inline_code">fork()</code> 完畢後，會馬上呼叫 <code class="inline_code">close()</code> 將 socket 關閉，因為母行程再也不需要用到這條 TCP connection。如果這個 <code class="inline_code">close()</code> 真的把 TCP connection 給結束掉，那子行程就沒戲唱了。所以，<code class="inline_code">close()</code> 會做 reference counting，只有在子行程也 <code class="inline_code">close()</code> 時，才會真的把 TCP connection 關閉。</p>
<p>在 Windows 裡，socket 不能用 <code class="inline_code">close()</code> 關閉，要用一個特別版本的 <code class="inline_code">closesocket()</code> 才行。<code class="inline_code">closesocket()</code> 與 UNIX 上的 <code class="inline_code">close()</code>，功能相同。</p>
<p>至此，porting 仍處在 API 對應的等級，很簡單。可是，問題就出在可以取代 <code class="inline_code">close()</code> 的 <code class="inline_code">shutdown()</code>。</p>
<p>兩者之間的差別在於，<code class="inline_code">shutdown()</code> 將不理會 socket 的 reference counting，馬上送出 <code class="inline_code">FIN</code> 將 TCP connection 結束。使用 <code class="inline_code">shutdown()</code> 的好處顯而易見，我們可以確定這個&nbsp;TCP connection 立刻結束，可加強資源控管。</p>
<p>依據《UNIX Network Programming》這本 socket programming 的聖經級書本所述：</p>
<blockquote>
<p>If we really want to send a <code class="inline_code">FIN</code> on a TCP connection, the <code class="inline_code">shutdown</code> function can be used instead of <code class="inline_code">close</code>.</p>
</blockquote>
<p>我們可以用 <code class="inline_code">shutdown()</code> 完全取代 <code class="inline_code">close()</code>：用了 <code class="inline_code">close()</code> 就不要用 <code class="inline_code">shutdown()</code>，用了 <code class="inline_code">shutdown()</code> 就不要用 <code class="inline_code">close()</code>。</p>
<p>可是，MSDN 卻說：</p>
<blockquote>
<p>The <code class="inline_code">shutdown</code> function does not close the socket. Any resource attached to the socket will <em>not</em> be freed until <code class="inline_code">closesocket</code> is invoked.</p>
</blockquote>
<p>意思是說，在 WinSock 裡，用了 <code class="inline_code">shutdown()</code> 之後，還要用 <code class="inline_code">closesocket()</code>，才能「關乾淨」把資源全部釋放。</p>
<p>就是因為這一點細節的不同，若是忽略了，就會產生 leak。要不是不小心看到 MSDN 的這一句，我也會疏忽掉，產生極難追蹤的 bug。</p>
<p>唯有通曉這類精微的細節，才能做到「真㊣的 porting」。面對浩如瀚海的 Windows APIs，我也只能兢兢業業地，一步一腳印，細讀 MSDN 的每一字每一句，希望能夠捕捉到，這些會害死人不償命的微光掠影。</p>
]]></content:encoded>
			<wfw:commentRss>http://www.jeffhung.net/blog/articles/jeffhung/1744/feed/</wfw:commentRss>
		<slash:comments>13</slash:comments>
		</item>
		<item>
		<title>好大的嵌入式資料庫</title>
		<link>http://www.jeffhung.net/blog/articles/jeffhung/1712/</link>
		<comments>http://www.jeffhung.net/blog/articles/jeffhung/1712/#comments</comments>
		<pubDate>Wed, 04 Mar 2009 13:06:16 +0000</pubDate>
		<dc:creator>jeffhung</dc:creator>
				<category><![CDATA[Programming]]></category>
		<category><![CDATA[database]]></category>

		<guid isPermaLink="false">http://www.jeffhung.net/blog/?p=1712</guid>
		<description><![CDATA[剛剛在裝 MySQL 5.1 for Windows，直接選 msi 檔安裝，然後就看到下面這個畫面…]]></description>
			<content:encoded><![CDATA[<p>剛剛在裝 MySQL 5.1 for Windows，直接選 msi 檔安裝，然後就看到下面這個畫面：</p>
<p style="text-align: center;"><a href="http://flickr.com/photos/jeffhung/3327991680/"><img alt="" src="http://farm4.static.flickr.com/3588/3327991680_9a74412bf8.jpg" /></a></p>
<p>有趣的是 Feature Description：</p>
<blockquote>
<p>The MySQL Embedded Server offers the MySQL-Server functionality in a small library that can be bundled with other applications.</p>
<p>This feature requires 151MB on your hard drive.</p>
</blockquote>
<p>真是厲害，embedded server 要 151 MB，還說明是 a small library，不曉得是小到哪裡去了。</p>
]]></content:encoded>
			<wfw:commentRss>http://www.jeffhung.net/blog/articles/jeffhung/1712/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>謹慎使用 atoi()</title>
		<link>http://www.jeffhung.net/blog/articles/jeffhung/1635/</link>
		<comments>http://www.jeffhung.net/blog/articles/jeffhung/1635/#comments</comments>
		<pubDate>Mon, 16 Feb 2009 15:01:53 +0000</pubDate>
		<dc:creator>jeffhung</dc:creator>
				<category><![CDATA[Programming]]></category>
		<category><![CDATA[cpp]]></category>

		<guid isPermaLink="false">http://www.jeffhung.net/blog/?p=1635</guid>
		<description><![CDATA[剛剛追一個 bug，結果發現問題出在 atoi()，果然還是要用 C++ 才會比較有 type-safety，要不然碰到這類問題，連自己死了都不知道。]]></description>
			<content:encoded><![CDATA[<p>剛剛追一個 bug，結果發現問題出在 <code class="inline_code">atoi()</code>，果然還是要用 C++ 才會比較有 type-safety，要不然碰到這類問題，連自己死了都不知道。</p>
<p>事情是這樣子的。在一組透過 HTTP 溝通的網路程式裡，其中一端把某個型別為 <code class="inline_code">uint32_t</code> 的參數，用 <code class="inline_code">sprintf()</code> 轉成文字形式，附在 URL 的 <code class="inline_code">QUERY_STRING</code> 裡，傳送到另一端，接收端收到後，從 URL 裡 parse 出文字形式的這個參數，丟給 <code class="inline_code">atoi()</code> 轉回 <code class="inline_code">uint32_t</code>。整個流程大概如下圖：</p>
<pre class="code">
uint32_t ID = 0x9fffffff;              uint32_t ID = 0x7fffffff;
            |                                      ^
            |                                      |
sprintf(qstr, &quot;%u&quot;, ID);                  atoi(&quot;2684354559&quot;);
            |                                      |
            v                                      |
       &quot;2684354559&quot;                          &quot;2684354559&quot;
            |                                      ^
            |                                      |
            +--(append to URL, pass via Internet)--+
</pre>
<p>問題出在 <code class="inline_code">atoi()</code> 這個函式，其回傳值是個 <code class="inline_code">int</code>。所以只要這個數字大於 <code class="inline_code">INT_MAX</code>，也就是 <code class="inline_code">0x7FFFFFFF</code>，就一定會回傳 <code class="inline_code">0x7FFFFFFF</code>。於是，這個數字就被改掉了，不正確的結果，導致程式其他部份行為不正常。</p>
<p>解法有兩種：使用 <code class="inline_code">std::istringstream</code>，或是 <code class="inline_code">sscanf()</code>，這兩個都可以反應型別的不同，只不過前者可以自動反應，後者需手動使用 <code class="inline_code">&quot;%u&quot;</code> 格式字串。</p>
<p>如果嫌這兩個方法，寫起來又臭又長，也可以使用 Boost 的 <code class="inline_code">lexical_cast</code>。不過這個一樣得寫明輸出型別為 <code class="inline_code">uint32_t</code>，但比 <code class="inline_code">&quot;%u&quot;</code> 好懂許多。不曉得在 C++0x 裡，可不可以利用 <code class="inline_code">auto</code>，寫成下面那樣，會比較方便：</p>
<pre class="code">
ID = lexical_cast&lt;auto&gt;(&quot;2684354559&quot;);
</pre>
<p>如果 <code class="inline_code">auto</code> 能夠省去，那就更方便了。</p>
]]></content:encoded>
			<wfw:commentRss>http://www.jeffhung.net/blog/articles/jeffhung/1635/feed/</wfw:commentRss>
		<slash:comments>18</slash:comments>
		</item>
		<item>
		<title>英文的斷句也很重要</title>
		<link>http://www.jeffhung.net/blog/articles/jeffhung/1646/</link>
		<comments>http://www.jeffhung.net/blog/articles/jeffhung/1646/#comments</comments>
		<pubDate>Mon, 09 Feb 2009 06:46:36 +0000</pubDate>
		<dc:creator>jeffhung</dc:creator>
				<category><![CDATA[Devel]]></category>
		<category><![CDATA[Fun]]></category>
		<category><![CDATA[Programming]]></category>

		<guid isPermaLink="false">http://www.jeffhung.net/blog/?p=1646</guid>
		<description><![CDATA[剛剛在讀 MSDN，被一句參數說明嚇到，本以為是 MSDN 白爛，後來仔細一想，才發覺其實是斷句的問題…]]></description>
			<content:encoded><![CDATA[<p>剛剛在讀 MSDN，被一句參數說明嚇到，本以為是 MSDN 白爛，後來仔細一想，才發覺其實是斷句的問題。</p>
<p>在 MSDN 裡，<code class="inline_code">CreateNamedPipe()</code> 這個函式的第三個參數 <code class="inline_code">dwPipeMode</code> 的說明裡，有這麼一句話：</p>
<blockquote>
<p>The functions fails if <code class="inline_code">dwPipeMode</code> specifies anything other than <code class="inline_code">0</code> or the flags listed in the following table.</p>
</blockquote>
<p>我第一眼讀完的理解是：</p>
<blockquote>
<p>The functions fails if <code class="inline_code">dwPipeMode</code> specifies <strong>(</strong> anything other than <code class="inline_code">0</code> <strong>)</strong> or <strong>(</strong> the flags listed in the following table <strong>)</strong> .</p>
</blockquote>
<p>於是大驚小怪的叫：有沒有搞錯，寫 <code class="inline_code">0</code> 或者「沒有列在」下面表格的那些 flag，才不會失敗？！</p>
<p>兩分鐘後，仔細重讀才發現，原來是我斷句錯誤。這句應該是這麼被理解的：</p>
<blockquote>
<p>The functions fails if <code class="inline_code">dwPipeMode</code> specifies<span style="font-weight: bold;"> </span>anything other than <strong>(</strong> <code class="inline_code">0</code> <strong>)</strong> or <strong>(</strong> the flags listed in the following table <strong>)</strong> .</p>
</blockquote>
<p>兩種斷句，應該都可以，只不過第一種很搞笑，第二種才合理。</p>
<p>習慣上，我在撰寫英文句子時，會儘量讓 &quot;or&quot; 兩邊的子句，長度差不多，感覺比較對稱。也許就是因為這種習慣，讓我把句子讀錯了吧。</p>
]]></content:encoded>
			<wfw:commentRss>http://www.jeffhung.net/blog/articles/jeffhung/1646/feed/</wfw:commentRss>
		<slash:comments>3</slash:comments>
		</item>
		<item>
		<title>Catch All in Thread Function/Proxy</title>
		<link>http://www.jeffhung.net/blog/articles/jeffhung/1591/</link>
		<comments>http://www.jeffhung.net/blog/articles/jeffhung/1591/#comments</comments>
		<pubDate>Fri, 23 Jan 2009 04:30:40 +0000</pubDate>
		<dc:creator>jeffhung</dc:creator>
				<category><![CDATA[Devel]]></category>
		<category><![CDATA[Programming]]></category>
		<category><![CDATA[cpp]]></category>

		<guid isPermaLink="false">http://www.jeffhung.net/blog/?p=1591</guid>
		<description><![CDATA[因為某人說我的文章，看第一眼就不想看了，因為用膝蓋想也可以知道，太花腦筋。所以我還是改變一下風格，多寫點簡短的好了，剛好可以把一些零散的程式設計經驗，記錄下來，以後有空再集結整理。]]></description>
			<content:encoded><![CDATA[<p>因為某人說我的文章，看第一眼就不想看了，因為用膝蓋想也可以知道，太花腦筋。所以我還是改變一下風格，多寫點簡短的好了，剛好可以把一些零散的程式設計經驗，記錄下來，以後有空再集結整理。</p>
<p><strong>在 thread function 裡用 <code class="inline_code">catch (...)</code> 攔截所有可能的例外</strong></p>
<p>這是因為，thread function 的啟動，是透過系統完成，無法傳遞 exception，更因為 thread function 相當於 <code class="inline_code">main()</code> of thread，是 <a href="http://en.wikipedia.org/wiki/Call_stack">stack unwinding</a> 的終點，如果不用 <code class="inline_code">catch (...)</code> 把「所有」可能的例外攔截下來，則視系統不同，輕則整個 process 結束，重則僅該 thread 無聲無息地消失，程式裡其他 thread 繼續執行，整個程式看起來沒什麼問題，但實際上卻有一個 thread 已經不見了，等到要發現問題，可能要在一個月以後。</p>
<p>最簡單的 thread function，應該這麼寫：</p>
<pre class="code">
int thread_func(void* param)
{
    // nothing above try
    try {
        // do real job
    }
    catch (...) {
        // ellipsis to catch all possible exceptions
    }
    // nothing after catch
    return 0;
}
</pre>
<p><strong>在 thread proxy 裡用 <code class="inline_code">catch (...)</code> 攔截所有可能的例外，並提供預設處理機制</strong></p>
<p>同上面的理由，如果&nbsp;thread library 是我們自己撰寫的，則通常會有個 thread proxy，library 把 thread proxy 傳給 OS，OS 啟動新的 thread 時，先執行 thread proxy，然後 thread proxy 才轉呼叫真正的 thread function。因為不同平台的 native thread function 的 prototype、calling convention 可能都不一樣，因此依據平台的不同，必須準備不同版本的 thread proxy，這樣才可以讓 thread function 的 prototype、calling convention 一致化。</p>
<p>因此，對於 OS 來說，這個 thread proxy 才是真正的 thread function，thread proxy 是第一線，原來的 thread function 是第二線。因為寫 library 是要給人用，有可能用的人沒有注意到前一點，忘記在 thread function 裡 <code class="inline_code">catch (...)</code>，還好我們還有更底層的 thread proxy，可以在此 <code class="inline_code">catch (...)</code> 並做一些處理，就可以避免意外發生。</p>
<p>例如 <a href="http://www.boost.org/doc/libs/1_37_0/doc/html/thread.html">Boost.Thread 1.37.0</a> 的 thread proxy 是這麼寫的：</p>
<pre class="code">
void* thread_proxy(void* param)
{
    boost::detail::thread_data_ptr thread_info
        = static_cast&lt;boost::detail::thread_data_base*&gt;(param)-&gt;self;
    thread_info-&gt;self.reset();
    detail::set_current_thread_data(thread_info.get());
    try {
        thread_info-&gt;run();
    }
    catch(thread_interrupted const&amp;) {
    }
    catch(...) {
        std::terminate();
    }

    detail::tls_destructor(thread_info.get());
    detail::set_current_thread_data(0);
    boost::lock_guard&lt;boost::mutex&gt; lock(thread_info-&gt;data_mutex);
    thread_info-&gt;done=true;
    thread_info-&gt;done_condition.notify_all();
    return 0;
}
</pre>
<p>Boost 選擇的處理方式，是呼叫 <code class="inline_code">std::terminate()</code>，把程式結束，這樣至少 thread 不會無聲無息地消失。</p>
]]></content:encoded>
			<wfw:commentRss>http://www.jeffhung.net/blog/articles/jeffhung/1591/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
	</channel>
</rss>

