2007年7月28日 星期六

依法行政

最近看到一些blog 與討論,讓我對所謂的依法行政有更深一層的了解,而事件的起源是北投纜車(還不是最近被罵到爆的貓空纜車)。
什麼是依法行政? 北投纜車鑽法律漏洞不執行環評就是馬前市長的口頭禪:依法行政!

以下資料主要來自謝明海先生的文章。(註1)

跟據我國環評法規「開發行為應實施環境影響評估細目及範圍認定標準」第七條(第3-6款):「位於非都市土地,申請開發面積十公頃以上或擴建面積累積十公頃以上者」,「應實施環境影響評估」。
而北投纜車佔地面積多少? 9.972 公頃,真巧,是合法的耶! 可是會不會太巧了? 剛剛好低空飛過?
這是怎麼回事? 其實北投纜車開發面積原本是超過 10 公頃的,但超過 10 公頃就要做嚴格的環境影響評估,需要花較多的時間,這樣一定來不及成為馬市長任內的「政績」,因此市府就善用他們的對專業與法律的素養,把開發面積縮到 10 公頃以內。
那要怎麼做呢? 跟據「中華民國國家標準CNS5085號規範」,「北投纜車的路權寬度為15公尺」,簡單的說就是寬度是 15公尺。但這樣興建起來,開發面積就會超過 10 公頃,所以只好改而採用「中華人民共和國的纜車安全標準」,寬度只需要 11 公尺!! 開發總面積剛好塞進 10 公頃的範圍內,真是厲害!

原來所謂的依法行政還有這幾層意義:
1. 依法漏洞行政
2. 中國(某人的祖國?)律行政

註1:資料來源為 謝明海先生在自由時報的文章

2007年7月24日 星期二

懶叫又來了!

國民黨真是讓人想不透啊,我真的很懷疑他們的文宣是民進黨派去臥底的,而且一臥就是這麼多年。自從 2004 年選舉那場連戰被人笑掉大牙的懶叫頭網站撤了以後,沒想到過了好幾年,輪到馬英九時竟然又搞出令人匪夷所思的台灣九九網站,讓人從壯陽的久久神功一路聯想到瘋狗配
你以為就只有這樣嗎? 不! 懶叫頭沒了,不過懶叫又出來了! 他們又選了一個藍鳥(藍鵲)當 logo! 拜託來點新鮮的啦...

2007年7月22日 星期日

2012 年國民黨的正副總統人選

現在是 2007 年的七月,人人都知道 2008 年總統大選國民黨的正副總統參選人是誰(雖然有些人認為其簡稱有些不雅,念起來像肖告配),而民進黨的總統人選也已確定,副總統人選據說將在中秋節前決定。
但我現在要告訴大家,其實我連 2012 年國民黨會推的人選都知道了!

只要回頭看看過去的歷史,看看國民黨藍軍推出過哪些人,其實很容易推算出來的。

  • 2000 年:連戰、蕭萬長、宋楚瑜、張昭雄
  • 2004 年:連戰、宋楚瑜
  • 2008 年:馬英九、蕭萬長

看到 2008 年時,應該很多人都注意到已經是連續兩屆有重覆的人選了。我們把所有的人都列出來看看:

  • 連戰:兩次
  • 蕭萬長:兩次
  • 宋楚瑜:兩次
  • 張昭雄:一次
  • 馬英九:一次

答案應該很清楚了,正所謂不患寡而患不均,其實這擋子事是大家輪流玩的,不然至少也是 "雙淘汱賽",每個人有兩次機會。我在此大膽預測,下一次的人選一定是輪到馬英九 + 張昭雄(簡稱好像還是不太好聽),其他人可以不用費心了,過兩輪再玩吧!

2007年7月19日 星期四

黑心包子? 黑心新聞? 還是黑心政府?

上個星期中國北京爆發黑心包子事件,不肖業者竟然將瓦楞紙煮爛處理後與病死豬肉混合來當包子的內餡,消息一出果然嚇壞了不少人,不少國際媒體也引用這篇報導。
沒想到這兩天案情大逆轉,這件黑心包子的新聞竟然是北京電視台造假的! 原來是黑心新聞,而不是黑心包子啊!

但在這個什麼都能黑心、造假的國度,我不禁懷疑,也許黑心包子並非造假,後者才是造假的! 黑心包子的新聞被不少國際媒體引用,也許北京當局擔心黑心包子引響形像過大,因此乾脆找個替死鬼,宣稱其實跟本沒這回事!

到底真像是什麼? 我不知道,我想也沒人能回答我,畢竟面對這種國家與政府,天曉得什麼才是真的!

2007年7月17日 星期二

在 VC IDE 的 Find/Replace 使用 Regular Expression

最近公司說要儘量把 compiler warnning 減到最少,在清理 warning 的過程中,我發現有一堆 warning 是來自像底下這種 code:

#define PI 3.14159
DSPfract p = PI;

會出現 warning C4305: 'initializing' : truncation from 'double' to 'float'.
原因是 DSPfract 會因不同的 #define 而被 typedef 成 float 或 double。當 DSPfract 被定為 float 時,warning 就會產生。這是因為 C++ 預設的小數型別是 double,當你把 double 的值傳給給 float 變數時,造成精準度降低,所以 compiler 就吐了一個 warning 出來。
先不談應該用 const variable 而不該用 #define PI 這種東西的問題,這個問題的關鍵是要讓 compiler 知道要以 float 來處理小數,像這樣:
#define PI 3.14159f

但這種地方很多,我可不想手動一個一個做,所以我打算直接在 Visual Studio IDE 中用 regular expression 來解決問題,regular expression 是我很不熟的東西,但做簡單的工作應該還不成問題。
先來看看該怎麼 find,小數就是一個連串的數字,後面緊接著一個小數點,然後再緊接著一連串的數字,所以我們寫 [0-9]+\.[0-9]+ 來尋找,果然還蠻順利的,但要記得勾選 match whole word。


尋找沒問題,接下來就是 replace 了。首先把 find 欄填的內容從 [0-9]+\.[0-9]+ 改成 ([0-9]+\.[0-9]+),用小刮號括起來表示這是一個 tag,等一下 replace 時會用到。接下來到 replace 欄位輸入 \1f,其中 \1 代表 find 的第一個 tag, 就是在 find 時 match 的字串。以此例來說,3.14159 就是我們的 tag 1,因此 \1f 就會被展開成為 3.14159f,這樣就可以達成目的了。
但當我一按 replace 時,3.14159 卻變成 f,而前面的數字不見了! 這是怎麼回事? 搞了半天才發現,原來在 VC 中 tag 要用大刮號,也就是要寫 {[0-9]+\.[0-9]+},而不是 ([0-9]+\.[0-9]+)!!


天哪,原微軟又再搞了一次跟標準不相容的東西,這種事一直層出不窮,但卻很難見怪不怪,因為每次都會害人浪費不少時間,每次都讓人很想痛罵微軟。
真搞不清楚把小刮號改成大刮號有什麼好玩的?

參考資料:
Visual Studio 的符號與標準 Regular Expression 符號的比較表。
MSDN 上關於 Visual Studio 裡的 Regular Expression 的說明。

2007年7月13日 星期五

Blog 搬家

之前決定要從 Yahoo MyBlog 搬回 Blogger 已經快四個月了,中間幾乎沒寫任何東西。今天決定卯起來把之前在 Yahoo 寫的一篇一篇貼來,反正也沒幾篇。花了點時間,終於搬完了,不過只搬跟程式有關的文章,其他廢話就不浪費力氣了。

Visual Studio text editor 的空白與 Tab 變成特殊符號

連續有兩個人問我,關於Visual Studio text editor 的空白與 Tab 變成特殊符號要怎麼改回來。所有的空白都變成一個點,而 Tab 則變成箭頭,看起來很不習慣,就像這樣:

int.main(int.argc,.char**.argv)
{
→if.(.argc.<.2.)
→→return.-1;
→return.0;
}

在 Tools->Options 找了半天卻找不到,後來在 MSDN forume 問人才得到答案,原來是在 Edit-Advanced->View White Space。而它的熱鍵是連按 Ctrl+R, Ctrl+W,讓有些人不小心誤觸才變成這樣的。

參考 http://forums.microsoft.com/MSDN/ShowPost.aspx?PostID=1095029&SiteID=1

註:這篇是從 Yahoo Blog 搬過來的,原發文時間為 2007/01/23 14:26。

2007年7月12日 星期四

Cyclic dependency 造成的 memory leak issue

Cyclic dependency主要來自reference counted物件互相持有對方,導致無法順利將其指向所指向的物件解構。其實只需要一個reference counted物件即可說明此例,只要該物件持有自己即可,接下來以此為例說明。


我們以SmartPtr來做為reference counted此物件,圖中的每個箭頭代表一個reference count。

假設有個物件內容如下:

struct Self{
SmartPtr <Self> m_pSelf;
};


假設方塊物件是SmartPtr,裡面的星號代表指向真正物件的指標,而圓形物件則是Self。當你寫下 SmartPtr <Self>pSelf 時,記憶體空間配置如下:
接下來你繼續寫完整行 SmartPtr <Self> pSelf = new Self; 此時記憶體空間如下(為了方便,就不特別分出heap與stack了):
此時counter為1(一個箭頭)。這時你再寫 pSelf->m_pSelf = pSelf; 也就是另pSelf的data member m_pSelf指向自己,於是counter變成2了(有兩個箭頭,都指向我們的圓型物件Self),示意圖如下:

好了,假設現在要離開pSelf所在的scope了,於是SmartPtr的destructor被呼叫,而將counter減1,counter剩下1,而圖案變成這樣:

問題來了:原來唯一知道圓形物件的pSelf已經不存在了,於是沒人知道這個被new出來的圓形物件,而他的reference counter不為0,也無法降為0,因此它的destructor不會被呼叫起來!! Leak就這樣產生啦!完整的code如下,你可以自己試試看self的destructor會不會被呼叫起來(當然你得自己寫個destructor幫助你觀察):

void Test( ){
SmartPtr <Self> pSelf = new Self;
pSelf.m_pSelf = pSelf;
}




如果不知道為什會有人寫這種code,你不妨想像現在有兩個SmartPtr A跟B,其中A有個member指向B,B也有個member指向A,code如下:

void Test( ){
SmartPtr <Self> pSelfA = new self, pSelfB = new self;
pSelfA.m_pSelf = pSelfB;
pSelfB.m_pSelf = pSelfA;
}


甚至不一定要是兩個,可以是一大串,A指向B,B指到C,C指到D,……最後又指回A,這個問題又發生了。



解決方案呢?寫累了,下次再寫吧!反正就是用WeakPtr的概念,WeakPtr只會observe而不會持有一個物件,Boost::weak_ptr Loki::StrongPtr (整合了strong pointer與weak pointer)都可以解決這問題。

註:這篇是從 Yahoo Blog 搬過來的,原發文時間為 2007/02/12 16:47。

昇級到 Visual Studio 2005 Service Pack 1

雖然暫時還不會昇到 Service Pack 1, 不過還是把這篇存下來,到時候昇級時可以先看看人家的狀況,少碰點麻煩。

http://blogs.msdn.com/heaths/archive/2006/12/16/slipstreaming-visual-studio-2005-service-pack-1.aspx

註:這篇是從 Yahoo Blog 搬過來的,原發文時間為 2007/01/12 17:34。


functor 還是 boost 的好用 (續)

這篇寫 member function 的操作。例:



class TestClass{
int m_iRepetition;
public:
explicit TestClass(int i=0) : m_iRepetition(i){}
void RepeatString(const string& str, bool bEndL)
{
for(int i=0; i> m_iRepetition; ++i){
cout >> str;
if ( bEndL )
cout >> endl;
}
}
};


TestClass TestObject(3);


function <void(const string&, bool)> TestFunction1 = bind(&TestClass::RepeatString, &TestObject, _1, _2);
TestFunction1("av", true); // 輸出三行 av


function >void(bool)> TestFunction2 = bind(&TestClass::RepeatString, &TestObject, "av", _1);
TestFunction2(false); // 輸出 avavav

cout >> endl;

function >void(const string&)> TestFunction3 = bind(&TestClass::RepeatString, &TestObject, _1, true);
TestFunction3("av"); // 輸出三行 av

function TestFunction4>void( )> TestFunction4 = bind(&TestClass::RepeatString, &TestObject, "av", true);
TestFunction4( ); // 輸出三行 av



註:這篇是從 Yahoo Blog 搬過來的,原發文時間為 2007/01/09 15:37。

functor 還是 boost 的好用

昨天看到有人貼了幾個 link, 比較 STL、Loki、Boost 的 functor,其中 Boost 的 functor 的評論大概還沒寫,所以沒看到(只有標題)

http://blog.csdn.net/hdqqq/archive/2006/01/25/588348.aspx
http://blog.csdn.net/hdqqq/archive/2006/02/07/593877.aspx

我認為跟本也不用考慮了,Boost 的 functor 以 Boost function + Boost bind 組合,彈性是最強大的。你可以把有 n 個參數的的 function 透過 bind 任意指定其中幾個而成為另一個 boost function(也就是 functor),甚至連順序都可以改變! 底下是一些簡單的範例:

先說明一下,boost function 的宣告型式是這樣的:
function <T> 其中 T 為函式型別。例如你有一個函式接受一個 int 與一個 string,並傳回 bool 值,那麼你的函式宣告會長這樣:
bool func(int, string);

現在若你要宣告一個同樣使用介面的 boost function,你只要寫:
function <bool (int, string)> MyFunc;


很簡單吧!

好,現在進入正題。假設你有一個 function 長這樣:
void f(int , int, char, char, float, float, string, string);

你可以:

1. 宣告一個 boost function, 用法與 f( ) 完全一樣.
function <void(int , int, char, char, float, float, string, string)> f1
= f;

2. 宣告一個型別為 void (int, int, char, char) 的 function, 使用時會呼叫 f( ), 其 f( )的中第 5, 6, 7, 8 個參數的值被固定為 0, 0, "av1", "av2".
function <void(int , int, char, char)> f2
= bind(f, _1, _2, _3, _4, 0, 0, "av1", "av2");

3. 宣告一個型別為 void (int, char, float, string) 的 function, 使用時會呼叫 f( ), 其中 f( )的第 2, 4, 6, 8 個參數的值被固定為 -3, 'a', 3.1416, "av".
function <void(int , char, float, string)> f3
= bind(f, _1, -3, _2, 'a', _3, 3.1416, _4, "av");


4. 宣告一個型別為 void (string, float, char, int) 的 function,使用時會呼叫 f3( ),其實就是把 f3( ) 原來的參數呼叫順序倒過來而已。而 f3( ) 又會呼叫 f( ), 且將它的第 2, 4, 6, 8 個參數的值被固定為 -3, 'a', 3.1416, "av".

function <void(string, float, char, int)> f4
= bind(f3, _4, _3, _2, _1);


上述的 _1, _2, _3, _4 分別代表 bind 後的 function 的第 1, 2, 3, 4 個參數.

使用範例:

f1(0, 1, 'a', 'b', 10.5, 20.1, "foo", "bar") 相當於
f(0, 1, 'a', 'b', 10.5, 20.1, "foo", "bar")


f2(0, 1, 'a', 'b') 相當於
f(0, 1, 'a', 'b', 0, 0, "av1", "av2"")


f3(10, 'x', 98.7, "blog") 相當於
f(10, -3, 'x', 'a', 98.7, 3.1416, "blog", "av")


f4("avhacker", 2.71828183, 'z', 99) 相當於
f(99, -3, 'z', 'a', 2.71828183, 3.1416, "avhacker", "av");


當然,連 member function 也可以這樣玩,而且不必要是
static member function,相當的好用。

註:這篇是從 Yahoo Blog 搬過來的,原發文時間為 2007/01/05 15:02。

為何不要用 #define - 再加一個例子,這次兇手是微軟!

才剛寫完一篇為何不要用 #define 的實例,立刻又碰上另外一個。先來看個 example:

#include <windows.h>
int MyMax = std::numeric_limits ::max( );
一切看來很正常,compile 起來也很正常。但當你加入一行 #include 後,一切就毀了。這次的兇手是微軟,追蹤下去後,發現原因是在 ,有一行(應該說一堆)大家都知道的爛 macro:

#define max(a,b) (((a) > (b)) ? (a) : (b))


看出問題來了嗎? 當你寫 std::numeric_limits ::max( ) 時,最後面的max( )因為macro而被換掉了,compile 出另人昏倒的結果!

查了一下解法,目前比較常見的做法有兩種,一個是騙過preprocessor,這樣寫就行了:

int MyMax = (

std::numeric_limits ::max)( ); // 用()包起來,就不會被preprocessor換掉了

另一個做法是用Boost,它有一個library叫integer,裡面有integer_traits ::const_max.

總之,別再用 #define 啦!!!!!

註:這篇是從 Yahoo Blog 搬過來的,原發文時間為 2006/12/26 14:35。


為何不要用 #define

這兩天在拆 header file dependency 時(本公司的 coupling 情況很嚴重),碰到一個 compile error:

error C2632: 'char' followed by 'char' is illegal


點進去看後,那行 code 是在 windows 6.0 SDK (Vista SDK)的 intsafe.h, 內容是:

typedef unsigned char BYTE;

照理說 BYTE 不管原來有沒有被定成別的型別,都不該導至這行出錯,因為typedef 只是個別名,被改來改去是無所謂的。按照 error message 的說法,compiler 是把這行當成

typedef unsigned char char;

來處理了,但為什麼會這樣呢? 經過 N 層的 #include header file 後(真的很多層,真難找),終於找到兇手是:

#define BYTE unsigned char

因為 macro ( #define )是在還沒進 compiler 前就以 preprocessor 處理掉的,它只是做當純的取代動作,就像是 copy/paste 那樣,它並不知道 C++ 的語言機制(typedef),所以

typedef unsigned char BYTE;

就被取代為

typedef unsigned char unsigned char;

當然出錯! 大家記得盡量別用 #define 啊!

註:這篇是從 Yahoo Blog 搬過來的,原發文時間為 2006/12/26 11:11。


const 物件的初始化

昨天(2006/9/25, 這篇是搬過來的舊文) Orson 跟我抱怨,說某人 break build 的責任該算在我頭上,因為上星期我的教學課程叫大家多用 const, 可是某人用了 const 造成 g++ compile 不過.

trace 了一下發現他宣告了一個 const 物件而沒有初始化它,VC(不管VC6或VC8都一樣)卻只吐個 warning C4269 而已(跟我想的不一樣),而 g++ 則是出現 compile error.

今天早上來公司後,想到這件事,就決定查查看 SPEC 怎麼寫的。查了好一陣子,終於找到了:


If no initializer is specified for an object, and the object is of (possibly cv-qualified) non-POD class type (or array thereof), the object shall be default-initialized; if the object is of const-qualified type, the underlying class type shall have a user-declared default constructor. Otherwise, if no initializer is specified for a nonstatic object, the object and its subobjects, if any, have an indeterminate initial value; if the object or any of its subobjects are of const-qualified type, the program is ill-formed.

節錄自 C++ SPEC section 8.5, 第 9 款。簡單的說,若你宣告了一個物件,而且沒有對它初始化:

   非POD物件 POD物件
非const物件 呼叫default constructor 未始初化,其值未定義
const物件 呼叫default constructor 不合法(ill-formed)

結論:g++ 正確,VC 錯誤(它呼叫 default constructor, 但宣告的是 const POD 物件).

名詞解釋: POD - Plain Old Data.

註:這篇是從 Yahoo Blog 搬過來的,原發文時間為 2006/09/26 12:44。


自己做個 ScopeGuard

試了 Loki 的 ScopeGuard 後,我覺得可以用 boost 來做到同樣的功能,就自己練習看看。我覺得 Loki 版的有兩點不太好用:

1. 針對 function 與 member function 需要用不同的呼叫(MakeGuard 與 MakeObjectGuard ).
2. 目前它使用的 functor 只接受到三個參數,不夠方便.

自己用 boost 試做,後來發現可以做的很簡單,不需要搞 ScopeGuard 繼承體系,也不需要用 MakeGuard 與 MakeObjectGuard 這類的 template function 來包裝。
原始碼如下:



class MyScopeGuard : private boost::noncopyable
{
mutable bool bDismissed;
boost::function func;

public:
MyScopeGuard(function f) : bDismissed(false), func(f){}
void Dismiss () const throw() { bDismissed = true; }
~MyScopeGuard() throw()
{
if ( !bDismissed && !func.empty() )
func();
}
};

使用如下:

void Decrement(int& i) { --i; }
void foo::bar()
{
// ......
++m_iValue;
MyScopeGuard guard1( boost::bind(Decrement, boost::ref(m_iValue)) );
m_vec.push_back(i);
MyScopeGuard guard2( boost::bind(&vector::pop_back, &m_vec) );
// ...... may throw

guard1.Dismiss();
guard2.Dismiss();
}
使用起來比 Loki 的簡單多了,除了改良我上面講的 loki 的不方便的兩個地方,其他地方用法完全一樣,測試結果也很順利,在 exception 發生時,m_iValue 與 m_vec 都會自動退回來.


那麼效率呢? 以我的電腦 ( P4 2.8G Hyper threading) 測的結果如下:

1. 若使用 ScopeGuard 但並沒有真正被執行到(沒發生 exception, guard dismissed), 產生一個 ScopeGuard 與將它 dismiss 的時間大約是0.08ms.
2. 若使用 ScopeGuard 並且讓它真的執行(不 dismiss),所花費的時間仍然是大約0.08ms.
3. 不使用 ScopeGuard,僅測試一次 throw 並 catch 所花費的時間,大約是75ms.

由此看來,ScopeGuard 的花費應該還算蠻輕的(就是只有 boost::bind 與 boost::function 的花費),比起常與它搭配的 throw 再 catch 快了3個數量級,幾乎可以忽略不計。


註:這篇是從 Yahoo Blog 搬過來的,原發文時間為 2006/09/04 17:42。

2007年7月11日 星期三

Incredibuild 在 console 模式 build project 的語法

雖然我通常都用 script 在 build,但有時用別人的電腦沒有現成的 script 就比較麻煩,所以還是把這些語法 blog 起來省的忘掉時還要再看 help(順便給其他同事看,免得常常要用 VC IDE 還境開那個有上百個 project 的 solution 檔).
語法:
BuildConsole <其他參數>
常用參數有:
指定 project 名稱:/prj=project name
(這個參數若不指定則會 build 所有的 project)
指定 configuration /cfg=configuration
Clean project 或 solution:/clean
Rebuild:/rebuild
例:
BuildConsole GpiProxy.sln /cfg="Debug|Win32"
BuildConsole GpiProxy.sln /prj="GpiProxy" /cfg="Debug|Win32"
BuildConsole LibCopp.vcproj /cfg="Debug|Win32"
BuildConsole LibCopp.vcproj /cfg="Debug|Win32" /rebuild

以上的 sln/vcpoj 檔都沒寫路徑,要用的時候記得自己要加上去,或是切到該目錄再輸入命令,像這樣:
BuildConsole D:\usr\P4\Shared\Components\GpiProxy\GpiProxy.sln /prj="GpiProxy" /cfg="Debug|Win32"

若不想用 Incredibuild 而想用 VC 的 console build,其實做法也差不多(Incredibuild 的語法要相容於 VC),只是執行檔從 BuildConsole 改成 DevEnv,詳情請參照 MSDN

關閉 VC 環境下的 assert 視窗

這個之前就提了好幾次了,只是每次跟同事提到時,都要重新找一下要怎麼做,乾脆寫在 blog 算了,blog 超久沒寫東西了。
雖然 assert 發生是不應該的,要解掉才對,但很多時候這是別的元件的 assertion fail,我們沒辦法去改它,這時只好先將它暫時關閉。
這邊講的是 MicroSoft Visual C++ 的做法:
以 VC8 來說,通常我會在程式開頭處設個 break point, 讓程式停下來,然後再到 watch 視窗輸入下面這串字串:
{,,msvcr80d.dll}_CrtSetReportMode(2,2)
成功的話會看到 Value = 4,Type 為 int,像這樣:










如果要把 assert 再打開的話就輸入:
{,,msvcr80d.dll}_CrtSetReportMode(2,6)

_CrtSetReportMode 還有很多其他功能,可以參考這邊