目錄:
2009-2010小結(一)畢業(yè)前夕
2009-2010小結(二)初入職場
2009-2010小結(三)加班考驗
2009-2010小結(四)抑郁重重
2009-2010小結(五)離職始末
緊接著到了2009年4月份。公司空降了一位主管(稱L2吧)。說是空降,其實也不完全是。據(jù)史料記載,在跨國公司存在以前,他與現(xiàn)在公司的一些中高層曾經是同事,后來歷史推演時,他被留在了老公司。干了幾年休息了一年以后,現(xiàn)在他又到這邊來了。可是對于我來說,與空降無異。我心里很是緊張,剛剛才熟悉一個主管,又要熟悉另一個陌生的人。
L2開始慢慢接手原來L1兼管著的Windows端的活。一兩周的相處之后,發(fā)現(xiàn)L2的做事風格和L1完全不同。當時對我來說最不適應的是時間管理。L2總是在下午5點以后來跟我說要做什么事什么事。有時候會問晚上有事情么,我確實沒事情的,老實回答了,他會說那我們來看一下某某問題。而整個白天,有時候幾乎無所事事。L2自己是個精力相當旺盛的人,也很喜歡加班,幾乎天天加班。有時候到了晚上9點左右,他會讓我們“早點回去”,而自己可能還呆著。盡管我很佩服他的精力,可是每當在晚上聽到“早點回去”的話,一種無形的怒火便油然而生。
我徹底地感到恥辱。我想我一個堂堂正規(guī)大學畢業(yè)生,雖說不上怎么厲害,但還是自認為可以好好完成任務的。你怎么可以讓我加班?還三天兩頭加?還有事沒事的加?對了那時我還有我的畢設呢,根本沒法開動。我陷入了沮喪之中。為此我找L1訴過一回苦,說到傷心處,眼里竟然也會滲出一些晶瑩的東西。
畢設不緊不慢地進行著。導師讓我們每兩三周碰頭一次。之前交給我的數(shù)據(jù)庫設計等工作我在一開始就呈上去了,經過討論修改之后差不多定下來了。導師告知大家做的過程中如果有數(shù)據(jù)庫修改要跟我討論過才可以改,并且讓我如果有問題可以召集大家開小會——這讓我備受壓力。我極力推薦使用 SVN 管理和共享所有資料,被采納了。于是我平時也經常看看他們做了什么。除了個別比較認真的在不斷地嘗試、不斷地更新、不斷地改進外,其他人基本上沒怎么動。我也稍就微動動,保持著比最勤快的人不勤快,比其他人都勤快的位置。但是大家平均下來,每人一個頁面還不到。邏輯層代碼那一塊幾乎沒有別人在動。每次我們碰頭的時候導師總是一種失望的眼神看著我們。到了5月底,只剩最后一周了,我終于不得不請假回來了。那時幾乎是公司最忙的時候。
那一周也是我們最忙的時候。所有人都在做畢設——當然白天也還是操作的多。我們組有一兩個因為之前特別認真,也懂了不少;還有兩個也是有些編程基礎;當然也還有人完全不會的,其中有一個人自始至終啥事也沒干。時下大家做的都還是一些臨散的頁面,能直接用的只有兩三張。于是回來的那個晚上我讓大家先暫停一天,然后安排第二天晚上再碰一次面。然后花了一夜把站點后臺的各個功能入口,以及有關用戶使用方式的玩意兒先整了出來,把現(xiàn)在已有的頁面放到該放的位置。第二天再碰頭討論具體的整個使用流程,以及各種疑問,把全局性的東西讓大家都達成共識。接下來三四天大多數(shù)人也都比之前認真地做了自己的部分,到最后一天還有人有沒有完成的,就只能剝奪其開發(fā)權利讓其他人頂上了,也算馬馬虎虎湊成一整個玩意兒了。當然出自每個人之手的界面看上去還是不能完全統(tǒng)一。介于我負責的那一塊自己也沒太快搞定,想想算了,就這樣吧。最后答辯的時候,我們的會議管理系統(tǒng)看上去其實還是像模像樣的。結果當然是皆大歡喜,不管大家實質上做沒做過啥。只是導師不無遺憾的說,應該可以做得更好的。我知道我讓他失望了。
畢設搞定之后,我又回到了公司。想不到趕上了一個更激烈的時期,跨國公司有史以來加班最嚴重的一陣子。6月12號左右的三天,公司搬家,本來是放假的,因為趕版本,我們十幾號人卻被要求在某公寓里集體加班。那三天基本上是每晚上到2點以后。我比較撐不住,或者可能“假裝”撐不住,困了就直接到臥室一頭砸下去,等第二天被叫醒。要是凌晨2點以前下班了,那就第二天早上睡到11點多去,直接拿盒飯。反正加班么,調休少幾個小時就少幾個小時。哥真的累了。L2怎么看我已經不重要了。
接下來回學校的機會不多了。每次回去都像客人一樣,心里真不是滋味。此時我才發(fā)覺,這么早去實習是多么的傻逼。同學們都在升華感情,我卻浪費了人生唯一的大學畢業(yè)時期。當初請假的時候,L1和V都問過我,畢設一周之后要不要繼續(xù)請假幾天,我都很敬業(yè)地說不用了。我這一生已經沒辦法找回這樣的時光了,只求后人不要犯我同樣的錯誤,如果他確實認為這是個錯誤的話。直到現(xiàn)在,但凡知道有應屆生要提前出來實習,只要搭得上話的,我都勸之好好考慮。
畢業(yè)證書搞定之后,我終于可以拿100%的工資了。前面一些月,我用一些血汗錢買了新的主板,CPU,顯卡,湊成了一臺新主機的主要部分。終于用上了獨顯,真是享受,雖然月光的季節(jié)總是讓我時不時地感慨些許。7月份確實沒之前那么瘋狂了,但是L2還是三天兩頭下午臨下班布置任務。這讓我很煩惱,偶爾也問V:什么條件下可以換部門啊?答曰,入職滿一年。我接著說,那我不是起碼要加班加一年了?因為V之前跟我說公司不推崇加班的,這時候V會面露小尷尬,也跟我說過不好意思之類的。實際狀況也是不怎么加班的——這在三月份確實是,在L1的部門現(xiàn)在也是,在其他部門基本上也是是的多。
大約9月份的時候,一進去就開始做、5月中旬徹底完成的那個小玩意兒又要出新版本了,還是L1帶。作為那個項目的始作俑者,我又被劃歸了L1。這讓我相當慶幸。可是好景不長,由于一些組織架構上的變動,H要來帶那個項目,L1又純粹帶自己的服務端部門了。H是我的一面面試官,面試感覺相當好。后來也有若干次接觸,感覺他是所學甚廣,無所不通。這自然讓同樣所知甚雜的我有點佩服。在L2的帶領和影響下全員加班的日子里,H也是時有公開抱怨的——嗯,價值觀還算相近。加上H對我的知遇之恩吧,我很是欣然地接受了這個安排——雖然我基本上沒有拒絕的選擇。
終于擺脫L2了,終于不用TMD加班了!哦耶~!我懷著夢幻的期待,是無法按捺的情懷……
[未完待續(xù)]
posted @
2011-01-20 23:36 溪流 閱讀(2271) |
評論 (4) |
編輯 收藏
目錄:
2009-2010小結(一)畢業(yè)前夕
2009-2010小結(二)初入職場
2009-2010小結(三)加班考驗
2009-2010小結(四)抑郁重重
2009-2010小結(五)離職始末
2009年3月2日,是我上班的第一天。不過這時候還不叫正式工作,稱為實習。公司方面說因為畢業(yè)證沒拿到無法簽訂勞動合同。我要承受的代價就是50%工資。不過對于當時還沒有嘗過親手賺錢的滋味的我來說,50%也已經相當豐厚了,至少這個學期吃飯問題可以自己解決了。
上班第一天,考研成績出來了,我跟預料的一樣,沒考上:

唯一可以得瑟的是,政治果然靠覺悟啊,我比參加了好些時日培訓班的同學們也少不了幾分;其余的不堪入目,就不多說了,丟臉,還復習過三天呢,沒復習過還可以說說。
我們公司雖小,但兩岸三地有好些分公司呢,再加上米國總部的 CxO 們,活生生一個跨國公司。在跨國公司的好處非常明顯,常用書面英語不熟練都不可能。在真實的 Email 里頭彪英文的感覺有時候還是蠻爽的。比較有意思的是,跟臺北的同事們在MSN交流的時候,他們有時候說英文,我就納悶了,你丫不是會說中文嗎?于是回個中文,還是簡體的。人家繼續(xù)英文,我就繼續(xù)淡定的國語伺候。當然,為了尋歡作樂,經常情況也會反過來。感覺米國的同事以及臺北的同事用英文的時候,基本上不太考慮太多小語法的,看多了以后,自己寫郵件的時候也就大膽了,差不多意思了就敢亂發(fā)了,還CC一大票人。現(xiàn)在一個多月沒寫了,有時候突然想寫句英文,明顯憋不出來了。
跨國公司的HR姐姐很親切,暫時稱為V姐吧。特別是打電話的時候,從電話那頭聽她的聲音真是一種享受。不過在電話這頭聽她給別人打電話,就不是那么唯美了,稍微顯得嗲了點。難道經過電路傳輸,語音會得到稀釋嗎?從2008年末開始接觸,一直到現(xiàn)在,V姐一直給人無微不至的感覺。都說HR不可信,可是可信不可信又有什么重要呢?我有一個觀點,特別想跟懷疑論者、陰謀論者分享,不管別人自認為是否對你好,只要那個人所做的事在你看來都對你好的,或者至少不壞,那么TA就是對你好的,即便人家的本意是想對你使壞。何謂好?何為壞?都是主觀感受而已,我們生活在一個充滿著主觀意味的世界里,我們需要憑我們的感知能力去觀察世界。于是,存在即被感知,是直觀的和易于理解的,所謂的客觀,才是人們杜撰出來的概念。從這個意義上來說,如果我們相信世界是美好的,世界就會比預期的美好得多。所以我認為我向來的不以最壞的惡意來揣摩他人的理念是行得通的。呃,扯遠了。(批判我可以,不希望看到有人以“孩子,你還年輕”的態(tài)度來批判。)
我在跨國公司的第一任主管是L1姐,一個年輕的媽媽,在她身上洋溢著母愛。有時候晚上加班,聽她給沒幾歲的孩子解釋不能回去的理由,以及那種認真勁兒、那種語氣,就能感知到她是沉浸在怎樣的幸福中。不過L1在工作上的態(tài)度還是蠻嚴肅認真的。在上班的第二個星期,我就參與了實際項目,而且是個新小項目。這種機會蠻難得的。在L1的指導和要求下,我覺得我那時起才開始有了一點點設計理念。為了得到她的肯定,我會盡量將代碼組織得漂亮點。之后不久我又被安排同時參與另一個項目的維護工作。看過現(xiàn)有代碼,才感覺到大家寫的代碼原來是這樣的糟糕。不太客氣地說,80%以上的代碼是沒有經過好好組織的,90%以上的代碼的書寫是不工整的,以至于當時在手的那個新項目看上去爽多了。大概那時侯起,我覺察到老項目與新項目的截然不同的面貌,心里帶上了一些疑問:是否老項目一開始也是很漂亮的呢?是否漂亮的新項目過些時日也會變得面目不堪呢?
L1一直帶給我的很大幫助是,當我面對復雜的邏輯不知道如何下手時,她總是能夠迅速給出一套簡單、明了的方案,簡單到當時的我也能立刻理解。從她的或詳或簡的解釋中,能得到很多關于軟件設計上的種種“規(guī)則”,這些規(guī)則,我所見過的好些多年開發(fā)者也不一定能夠總結出來,不一定能夠很好地遵守,甚至不一定覺察到。
有件不幸的事,L1安排我做項目的安裝程序,也就是最后一道工序。當時我求知欲很強,多做點事最好,欣然應允。到后來才發(fā)現(xiàn),發(fā)布版本的日子通常會加班,加班的時候我是最沒事做的,經常是打醬油到10點,然后干幾分鐘活,最后幫忙測試。最夸張的一次是,被告知要修一個很緊要的bug,周六晚上趕去通宵加班,修bug本身不是我的事情,我是去等他們修好后打個包而已。所以一開始是沒事干的,最多幫忙調試,看看什么問題。后來困了,就趴在桌子上睡覺,直到被叫起,已經第二天5、6點了,被告知修好了,然后打包,測試走人。雖然我大半個晚上在睡覺,人家在干活,可是通宵的味道并不好受。我也不喜歡沒事做的加班。
說起加班,不得不再說幾句。到目前為止,我都認為L1是跨國公司里對項目進度控制的最好的一個主管,時間觀念很強。在我忘記或者即將忘記時間的時候,總是能得到善意提醒。所以她帶的項目極少加班,需求也被整理的有條不紊,每周都知道要干什么,每天都直到要干什么,即使加班,也知道因何事而加,知道做完什么以后就能離開。
雖然屬于實習,但是3月份是我這份工作中過的最開心的一段日子,也是成長蠻快的一段日子。在這里要衷心的謝謝L1在那段時期以及日后的很多日子里給予的指點、幫助,以及關心。
[未完待續(xù)]
posted @
2011-01-17 23:29 溪流 閱讀(2298) |
評論 (16) |
編輯 收藏
目錄:
C++ 下 Function 對象的實現(xiàn)(上)
C++ 下 Function 對象的實現(xiàn)(下)
上篇中,我們實現(xiàn)了一個支持 R () 型函數(shù)的 Function。補充說明一下,在我們對成員函數(shù)的支持中,我們是這樣定義的:
template <typename R, typename T>
class MemberFunction0 : public FunctionBase0<R>
{
private:
R (T::*m_pMemFun)();
T *m_pObj;
};
Loki 特意在著作中提醒我們,這里的 T 最好不要是函數(shù)類型,改為函數(shù)指針類型,如此該類的支持范圍將擴大。如下:
template <typename R, typename P, typename T>
class MemberFunction0 : public FunctionBase0<R>
{
public:
R Invoke()
{
return (m_pObj->*m_pMemFun)();
}
public:
MemberFunction0(P pObj, R (T::*pMemFun)())
: m_pObj(pObj), m_pMemFun(pMemFun)
{
}
private:
R (T::*m_pMemFun)();
P m_pObj;
};
于是,P 和 T 的關系不那么緊密了,P 不一定非要 T* 不可,也可以是諸如 SmartPtr<T> 之類的玩意兒。原本只支持傳入一個對象和該對象的成員函數(shù)的,現(xiàn)在變成傳入一個具有指針概念的東東和一個成員函數(shù),只要這個“指針”使用運算符 –> 去調用那個成員函數(shù)合乎語法即可。
接下來,我們來擴展這個 Function,以支持擁有數(shù)目在給定上限內的任意參數(shù)的函數(shù)。
我們先來手工寫一下,看看如何支持帶一個參數(shù)的函數(shù)。首先定義一個虛基類:
template <typename R, typename T0>
class FunctionBase1
{
public:
virtual R Invoke(T0) = 0;
virtual ~FunctionBase1() {}
};
實現(xiàn)兩個版本,分別支持非成員函數(shù)和成員函數(shù):
template <typename R, typename T0, typename T>
class Function1 : public FunctionBase1<R, T0>
{
public:
R Invoke(T0 v0)
{
return m_Fun(v0);
}
public:
Function1(const T &fun)
: m_Fun(fun)
{
}
private:
T m_Fun;
};
template <typename R, typename P, typename T, typename T0>
class MemberFunction1 : public FunctionBase1<R, T0>
{
public:
R Invoke(T0 v0)
{
return (m_pObj->*m_pMemFun)(v0);
}
public:
MemberFunction1(P pObj, R (T::*pMemFun)(T0))
: m_pObj(pObj), m_pMemFun(pMemFun)
{
}
private:
R (T::*m_pMemFun)(T0);
P m_pObj;
};
增加一個函數(shù)引用萃取的偏特化版本:
template <typename RetType, typename T0>
struct FunctionTraits<RetType (T0)>
{
typedef RetType (&ParamType)(T0);
};
增加一個 Function 類的偏特化版本:
template <typename R, typename T0>
class Function<R (T0)>
{
public:
template <typename T>
Function(const T &fun)
: m_pFunBase(new Function1<R, T0, typename FunctionTraits<T>::ParamType>(fun))
{
}
template <typename P, typename T>
Function(P pObj, R (T::*pMemFun)(T0))
: m_pFunBase(new MemberFunction1<R, P, T, T0>(pObj, pMemFun))
{
}
~Function()
{
delete m_pFunBase;
}
R operator ()(T0 v0)
{
return m_pFunBase->Invoke(v0);
}
private:
FunctionBase1<R, T0> *m_pFunBase;
};
現(xiàn)在,我們可以跑一下測試代碼了:
Function<int (int)> f1(&intfun1);
Function<int (int)> f1_(intfun1);
Function<int (int)> f2(intfunctor1);
Function<int (int)> f3(&test, &Test::intmem1);
f1(1);
f1_(1);
f2(2);
f3(3);
當然,void 函數(shù)也是支持的。
觀察上面的這些代碼,和我們在上一篇中的代碼高度一致,不同的是那些模版參數(shù)、偏特化參數(shù)、函數(shù)調用參數(shù)等地方。
假如有這么一組宏:
TYPENAME_DECLARE(n) 被定義為 typename T0, typename T1, …, typename Tn
TYPENAME_LIST(n) 被定義為 T0, T1, …, Tn
TYPENAME_VARIABLE(n) 被定義為 T0 v0, T1 v1, …, Tn vn
VARIABLE_LIST(n) 被定義為 v0, v1, …, vn
那么我們可以使用一個 n 就寫出支持所有具有參數(shù)的函數(shù)的 Function 了。我們拋棄掉上面的 1 系列的所有類,僅保持上篇留下來的代碼,然后利用上面 4 個宏將所有數(shù)字尾巴去掉,于是代碼變成:
template <typename R, TYPENAME_DECLARE(n)>
class FunctionBase_##n
{
public:
virtual R Invoke(TYPENAME_LIST(n)) = 0;
virtual ~FunctionBase_##n() {}
};
template <typename R, TYPENAME_DECLARE(n), typename T>
class Function_##n : public FunctionBase_##n<R, TYPENAME_LIST(n)>
{
public:
R Invoke(TYPENAME_VARIABLE(n))
{
return m_Fun(VARIABLE_LIST(n));
}
public:
Function_##n(const T &fun)
: m_Fun(fun)
{
}
private:
T m_Fun;
};
template <typename R, typename P, typename T, TYPENAME_DECLARE(n)>
class MemberFunction_##n : public FunctionBase_##n<R, TYPENAME_LIST(n)>
{
public:
R Invoke(TYPENAME_VARIABLE(n))
{
return (m_pObj->*m_pMemFun)(VARIABLE_LIST(n));
}
public:
MemberFunction_##n(P pObj, R (T::*pMemFun)(TYPENAME_LIST(n)))
: m_pObj(pObj), m_pMemFun(pMemFun)
{
}
private:
R (T::*m_pMemFun)(TYPENAME_LIST(n));
P m_pObj;
};
template <typename RetType, TYPENAME_DECLARE(n)>
struct FunctionTraits<RetType (TYPENAME_LIST(n))>
{
typedef RetType (&ParamType)(TYPENAME_LIST(n));
};
template <typename R, TYPENAME_DECLARE(n)>
class Function<R (TYPENAME_LIST(n))>
{
public:
template <typename T>
Function(const T &fun)
: m_pFunBase(new Function_##n<R, TYPENAME_LIST(n), typename FunctionTraits<T>::ParamType>(fun))
{
}
template <typename P, typename T>
Function(P pObj, R (T::*pMemFun)(TYPENAME_LIST(n)))
: m_pFunBase(new MemberFunction_##n<R, P, T, TYPENAME_LIST(n)>(pObj, pMemFun))
{
}
~Function()
{
delete m_pFunBase;
}
R operator ()(TYPENAME_VARIABLE(n))
{
return m_pFunBase->Invoke(VARIABLE_LIST(n));
}
private:
FunctionBase_##n<R, TYPENAME_LIST(n)> *m_pFunBase;
};
當然上面這樣子的代碼是沒法跑的咯。如果我們將整段代碼定義為一個宏 BODY(n),然后用類似剛才四個宏的方式定義宏 FUNCTION_IMPLEMENT(n),使得它的含義為 BODY(0), BODY(1), …, BODY(n),所有工作就都完成了。最后只需要丟下一句 FUNCTION_IMPLEMENT(20),就可以支持 0 到 21 個參數(shù)了。
最后歸結為,如何使用宏搞出“T0, T1, …, Tn” 的形式。
暴力點,我們可以這樣:
#define T_0 T0
#define T_1 T_0, T1
#define T_2 T_1, T2
#define T_3 T_2, T3
#define T_4 T_3, T4
#define T_5 T_4, T5
#define T_6 T_5, T6
#define T_7 T_6, T7
#define T_8 T_7, T8
#define T_9 T_8, T9
#define T(n) T_##n
這樣子,對于上面四個宏可以,但是對于最后的 X(n),人工代碼量還是太大了。嗯?X(n)?對,這個 X,必須在 _1、_2、_3 系列宏里面占據(jù)一個參數(shù)地位,這樣才有那么一點點擴展性。考慮換成這樣:
#define REP_0(macro, n) macro(0)
#define REP_1(macro, n) REP_0(macro, n), macro(1)
#define REP_2(macro, n) REP_1(macro, n), macro(2)
#define REP_3(macro, n) REP_2(macro, n), macro(3)
#define REP_4(macro, n) REP_3(macro, n), macro(4)
#define REP_5(macro, n) REP_4(macro, n), macro(5)
#define REP_6(macro, n) REP_5(macro, n), macro(6)
#define REP_7(macro, n) REP_6(macro, n), macro(7)
#define REP_8(macro, n) REP_7(macro, n), macro(8)
#define REP_9(macro, n) REP_8(macro, n), macro(9)
#define REP(macro, n) REP_##n(macro, n)
然后:
#define TYPENAME_LIST_PATTERN(n) T##n
#define TYPENAME_LIST(n) REP(TYPENAME_LIST_PATTERN, n)
這個 TYPENAME_LIST 就是符合上文要求的宏。接下來如法炮制其余三個:
#define TYPENAME_DECLARE_PATTERN(n) typename T##n
#define TYPENAME_DECLARE(n) REP(TYPENAME_DECLARE_PATTERN, n)
#define TYPENAME_VARIABLE_PATTERN(n) T##n v##n
#define TYPENAME_VARIABLE(n) REP(TYPENAME_VARIABLE_PATTERN, n)
#define VARIABLE_LIST_PATTERN(n) v##n
#define VARIABLE_LIST(n) REP(VARIABLE_LIST_PATTERN, n)
最后,我們在 #define FUNCTION_IMPLEMENT(n) REP(BODY, n) 中還存在一點點問題。因為 BODY 中會含有 TYPENAME_DECLARE 之類的宏的使用,而 TYPENAME_DECLARE 正是使用 REP 定義的。這涉及到宏的遞歸展開,C++預處理器的規(guī)則是,遇到這樣的情況就停止展開。比如,我們 定義 BODY(n) 為 TYPENAME_DECLARE(n),于是 FUNCTION_IMPLEMENT(2) 會被展成:
REP(TYPENAME_DECLARE_PATTERN, 0), REP(TYPENAME_DECLARE_PATTERN, 1), REP(TYPENAME_DECLARE_PATTERN, 2)
上面的 REP 不會被繼續(xù)展開了。
為此,一個不太聰明的辦法就是,再定義一組 REP2。嗯,是個辦法,就這么辦吧。另外我們剛才的 REP 系列沒有將分隔符作為參數(shù),默認使用逗號,而最后一不的 FUNCTION_IMPLEMENT 的重復中是不能用逗號的。考慮提取出來作為參數(shù)。最后我們的所需要的宏系統(tǒng)是:
#define NIL
#define COMMA ,
#define REP_0(macro, splitter, n) macro(0)
#define REP_1(macro, splitter, n) REP_0(macro, splitter, n) splitter macro(1)
#define REP_2(macro, splitter, n) REP_1(macro, splitter, n) splitter macro(2)
#define REP_3(macro, splitter, n) REP_2(macro, splitter, n) splitter macro(3)
#define REP_4(macro, splitter, n) REP_3(macro, splitter, n) splitter macro(4)
#define REP_5(macro, splitter, n) REP_4(macro, splitter, n) splitter macro(5)
#define REP_6(macro, splitter, n) REP_5(macro, splitter, n) splitter macro(6)
#define REP_7(macro, splitter, n) REP_6(macro, splitter, n) splitter macro(7)
#define REP_8(macro, splitter, n) REP_7(macro, splitter, n) splitter macro(8)
#define REP_9(macro, splitter, n) REP_8(macro, splitter, n) splitter macro(9)
#define REP(macro, splitter, n) REP_##n(macro, splitter, n)
#define REP2_0(macro, splitter, n) macro(0)
#define REP2_1(macro, splitter, n) REP2_0(macro, splitter, n) splitter macro(1)
#define REP2_2(macro, splitter, n) REP2_1(macro, splitter, n) splitter macro(2)
#define REP2_3(macro, splitter, n) REP2_2(macro, splitter, n) splitter macro(3)
#define REP2_4(macro, splitter, n) REP2_3(macro, splitter, n) splitter macro(4)
#define REP2_5(macro, splitter, n) REP2_4(macro, splitter, n) splitter macro(5)
#define REP2_6(macro, splitter, n) REP2_5(macro, splitter, n) splitter macro(6)
#define REP2_7(macro, splitter, n) REP2_6(macro, splitter, n) splitter macro(7)
#define REP2_8(macro, splitter, n) REP2_7(macro, splitter, n) splitter macro(8)
#define REP2_9(macro, splitter, n) REP2_8(macro, splitter, n) splitter macro(9)
#define REP2(macro, splitter, n) REP2_##n(macro, splitter, n)
#define TYPENAME_DECLARE_PATTERN(n) typename T##n
#define TYPENAME_DECLARE(n) REP(TYPENAME_DECLARE_PATTERN, COMMA, n)
#define TYPENAME_LIST_PATTERN(n) T##n
#define TYPENAME_LIST(n) REP(TYPENAME_LIST_PATTERN, COMMA, n)
#define TYPENAME_VARIABLE_PATTERN(n) T##n v##n
#define TYPENAME_VARIABLE(n) REP(TYPENAME_VARIABLE_PATTERN, COMMA, n)
#define VARIABLE_LIST_PATTERN(n) v##n
#define VARIABLE_LIST(n) REP(VARIABLE_LIST_PATTERN, COMMA, n)
#define FUNCTION_IMPLEMENT(n) REP2(BODY, NIL, n)
最后,定義一下 FUNCTION_IMPLEMENT(5),就可以支持到 6 個參數(shù)了。為了支持更多參數(shù),把上面的 REP 以及 REP2 系列多定義一點,比如到 50,那么 FUNCTION_IMPLEMENT 的括號中就可以填 50 以內的任意數(shù)了。考慮到宏展開對編譯速度的影響,以及實際應用中函數(shù)參數(shù)的個數(shù),定為 20 左右比較合適。
到這里,我們的Function已經實現(xiàn)了預期目標。接下來我本來想說說 TypeList 的。可是現(xiàn)在發(fā)現(xiàn)沒有 TypeList,F(xiàn)unction 跑的通;有了 TypeList,F(xiàn)unction 也不能寫的漂亮多少,雖說那些重復部分有一定的減少。Loki 的 Functor 的參數(shù)類型是一個返回值類型加上一個 TypeList,是由用戶直接傳入 TypeList 的,不用由散的類型組合出一個TypeList(但還是要從TypeList中萃取各個參數(shù)類型),因此用在他那里看上去美妙一點點。當然,Loki 也在 Functor 外頭包了一層 Function,以支持函數(shù)簽名作為模版參數(shù)的使用方式。有一點不算改觀的改觀是,用了 TypeList 以后,就不會再有 FunctionBase_1, FunctionBase_2 這樣的玩意兒了,取而代之的是一個統(tǒng)一的 FunctionBase 外加許多偏特化版本,F(xiàn)unction* 和 MemberFunction* 可以分別統(tǒng)一為一個,但是每一個里頭都需要實現(xiàn) N 個 Invoke。加上篇幅關系,我想這里就不說這個 TypeList 了。
代碼清單太長了,就不貼了,有意者自然能湊起來。我目前在 xlLib 中的最終實現(xiàn)見 xlFunction.h。
關于宏,我不知道可以怎樣改進。BOOST_PP_REPEAT 貌似可以調用自身?不知道如何實現(xiàn)的,求指教。另外@vczh貌似說“實現(xiàn)了一門可以自己遞歸自己和內置列表處理的另一個宏”,求分享呀求分享。
2010-01-18 補充:將最外層 Function 的構造函數(shù)中的 const T & 直接改為 T,并且拋棄 FunctionTraits,函數(shù)實體類型將在傳遞過程中直接退化為函數(shù)指針,這樣就能特化出正確的 FunctionHandler。同時帶來另一點影響:如果傳入 Functor,字面上將多一次拷貝動作。拋開這一點微小的性能來講,這比剛才的 FunctionTraints 要好得多了。
posted @
2011-01-17 21:59 溪流 閱讀(4162) |
評論 (5) |
編輯 收藏
目錄:
C++ 下 Function 對象的實現(xiàn)(上)
C++ 下 Function 對象的實現(xiàn)(下)
起因在上一篇已經說過了。現(xiàn)在讓我們直接進入主題。本文的目標是,讓以下代碼能順利跑起來:
int intfun0()
{
return 1;
}
struct _intfunctor0
{
int operator()()
{
return 2;
}
} intfunctor0;
struct Test
{
int intmem0()
{
return 3;
}
} test;
int main()
{
Function<int ()> f1(&intfun0);
Function<int ()> f1_(intfun0);
Function<int ()> f2(intfunctor0);
Function<int ()> f3(&test, &Test::intmem0);
f1();
f1_();
f2();
f3();
return 0;
}
除了上述例子中顯示的,還要支持有返回值的函數(shù)和沒返回值的函數(shù),以及有0個、1個、2個、……、MAX 個參數(shù)的函數(shù),參數(shù)類型無限制。最后實現(xiàn)的 Function 對象僅僅可以執(zhí)行就好。(至于是否可拷貝、是否可判斷相等 等問題,都是小事,本文暫不考慮。)最后,Bind 概念也不在本文討論范圍之內。
對于這個問題,我們一開始考慮的可能是怎樣統(tǒng)一三種不同形式。有兩個選擇,第一,使用 C++ 的多態(tài)機制,最后統(tǒng)一到基類指針的類型;第二,允許類內部有冗余變量以及必要的 Flag,用于判斷是哪種形式的函數(shù),要如何執(zhí)行。這樣看起來,第一種方案比第二種爽一點。于是,最初想到的實現(xiàn)有可能是這樣的:
先定義一個虛基類:
template <typename R>
class FunctionBase0
{
public:
virtual R Invoke() = 0;
virtual ~FunctionBase0() {}
};
然后實現(xiàn)一個普通函數(shù)/仿函數(shù)的版本:
template <typename R, typename T>
class Function0 : public FunctionBase0<R>
{
public:
R Invoke()
{
return m_Fun();
}
public:
Function0(const T &fun)
: m_Fun(fun)
{
}
private:
T m_Fun;
};
這里需要說明的是,如果是普通函數(shù),T會被特化成 R() 或者 R (&)() 或者 R(*)(),取決于使用的時候傳入 fun 還是傳入 &fun。所以不必另外實現(xiàn)針對 R(*)() 的版本。Loki (姑且就以作品名稱乎 Loki 的作者吧,他那個真名實在是太長)在他的書中稱之為“做一個,送一個”。不過對于他書中所說的,我有一個疑惑。Loki 說傳入 fun,模版參數(shù) T 會被特化成 R (&)(),于是一切順利。可是我在操作過程中發(fā)現(xiàn) T 一直被特化成 R (),于是上述 class 中的 m_Fun 被認為是成員函數(shù)而不是成員變量。不知道是為什么,有知道者請不吝指教哈。因為以上原因,本文中我一直用 &fun 的形式對待普通函數(shù)。
再實現(xiàn)一個成員函數(shù)的版本:
template <typename R, typename T>
class MemberFunction0 : public FunctionBase0<R>
{
public:
R Invoke()
{
return (m_pObj->*m_pMemFun)();
}
public:
MemberFunction0(T *pObj, R (T::*pMemFun)())
: m_pObj(pObj), m_pMemFun(pMemFun)
{
}
private:
R (T::*m_pMemFun)();
T *m_pObj;
};
最后是一個包裝類。如果你可以接受 Function<int> 表示 int(), Function<int, int> 表示 int (int),…,那么這里沒有多少技巧可言。boost 的那個 function 使用的是函數(shù)簽名作為模版參數(shù),即 Function<int()>,F(xiàn)unction<int (int)> 等形式。如果不太研究語法,可能會像我一樣,一開始會對尖括號里的 int (int) 之類的玩意兒不太熟悉,覺得很牛逼。可是了解了以后,不過是個函數(shù)類型而已,沒什么大不了的。Loki 的 Functor 的使用方式是 Functor<int, TYPELIST_0()>,F(xiàn)unctor<int, TYPELIST_1(int)>。其中第一個模版參數(shù)始終是返回值,第二個模版參數(shù)是參數(shù)類型列表,Loki 使用了他創(chuàng)造的玩意兒 TypeList 使得所有函數(shù)參數(shù)只占一個坑,這在等下的支持多參數(shù)的擴展中能夠帶來一些美觀。我比較喜歡 boost 的使用方式,讓使用者直接以語言規(guī)定的形式填入函數(shù)簽名,而不是一些額外的約定(“第一個模版參數(shù)表示返回值”,“第二個到最后的模版參數(shù)表示參數(shù)”,“第二個模版參數(shù)以 TypeList 形式表示函數(shù)參數(shù)”等)。
為了達到這個目標,我們要玩一些偏特化技巧。關于偏特化,我一直以來的膚淺認識都是錯誤的。我原以為,對于模版類:
template <typename T0, typename T1>
class Foo;
我如果特化其中一個參數(shù) T1:
template <typename T0>
class Foo<T0, int>
{
} 我以為只有這樣才叫偏特化,以為偏特化的過程總是減少模版參數(shù)的。而實際上,只要用某個/些類型占據(jù)原始模版參數(shù)的位置,就可以了。比如,對于上述 Foo,我可以特化一個 class<T0, std::map<U0, U1>>,消去一個 T1,而新增 U0、U1:
template <typename T0, typename U0, typename U1>
class Foo<T0, std::map<U0, U1>>
{
} 原來 T1 的位置被 std::map<U0, U1> 占據(jù)了,這也是偏特化。當然最后的模版參數(shù)數(shù)量也可以不變,如:
template <typename T0, typename U>
class Foo<T0, std::vector<U>>
{
} 以及
template <typename T0, typename U>
class Foo<T0, U*>
{
} 其中后者是實現(xiàn)類型萃取的主要方式。只要特化以后,這個類依然帶有至少一個模版參數(shù),就是偏特化。如果最后產生了 template<> 的形式,那就是完全特化。
回到我們剛才的主題,我們要提供給用戶的是這樣一個類:
template <typename Signature>
class Function;
其中參數(shù) Signature 會被實際的函數(shù)類型所特化。但是我們只知道整體的一個 Signature 并沒有用,我們必須知道被分解開來的返回值類型、參數(shù)類型。于是,引入一個偏特化版本:
template <typename R>
class Function<R ()>
這里使用 R () 特化原始的 Signature,引入一個新的參數(shù) R。于是返回值類型 R 就被萃取出來了。實現(xiàn)如下:
template <typename R>
class Function<R ()>
{
public:
template <typename T>
Function(const T &fun)
: m_pFunBase(new Function0<R, T>(fun))
{
}
template <typename T>
Function(T *pObj, R (T::*pMemFun)())
: m_pFunBase(new MemberFunction0<R, T>(pObj, pMemFun))
{
}
~Function()
{
delete m_pFunBase;
}
R operator ()()
{
return m_pFunBase->Invoke();
}
private:
FunctionBase0<R> *m_pFunBase;
};
如果對上面說的“普通函數(shù)的使用方式必須是函數(shù)指針而不是函數(shù)本身”耿耿于懷,可以再引入一個的構造函數(shù):
typedef R (FunctionType)();
Function(const FunctionType &fun)
: m_pFunBase(new Function0<R, FunctionType &>(fun))
{
}
這里 FunctionType 是 R(&)() 類型,強制使用它來特化 Function0 中的 T。該構造函數(shù)在重載決議中會取得優(yōu)先權從而使普通函數(shù)本身的傳入成為可能。不過,以函數(shù)本身形式傳入的普通函數(shù)會喪失一些特性,比如 Function<int()> 只能接受 int() 類型的普通函數(shù)而不能接受 char () 型的普通函數(shù),因為這種情況下不會走我們剛才新定義的構造函數(shù)。
還有一種做法,就是針對全局函數(shù),強制特化出模版參數(shù)為其引用類型的類。定義如下元函數(shù):
template <typename Signature>
struct FunctionTraits
{
typedef Signature ParamType;
};
template <typename RetType>
struct FunctionTraits<RetType ()>
{
typedef RetType (&ParamType)();
};
然后構造函數(shù)改為:
template <typename T>
Function(const T &fun)
: m_pFunBase(new Function0<R, typename FunctionTraits<T>::ParamType>(fun))
{
} 用以上方法,所有的特性都不會丟失。
到這兒,我們的 Function 已經可以小試牛刀了:
Function<int ()> f1(&intfun0);
Function<int ()> f1_(intfun0);
Function<int ()> f2(intfunctor0);
Function<int ()> f3(&test, &Test::intmem0); f1();
f1_();
f2();
f3();
上面這段代碼已經能夠正常運行了。
來,繼續(xù)做一個,送一個。下面的代碼居然也能跑(voidfun0、voidfunctor0、Test::voidmem0類似int版本定義):
Function<void ()> f4(&voidfun0);
Function<void ()> f4_(voidfun0);
Function<void ()> f5(voidfunctor0);
Function<void ()> f6(&test, &Test::voidmem0); f4();
f4_();
f5();
f6();
這說明了,在類里面寫一個返回值為該類型的函數(shù),并在里面寫下 return XXX; 然后以 void 為模版參數(shù)傳入該模版類,是符合語法的。驗證一下:
template <typename T>
class Foo
{
public:
T Bar()
{
printf("%s invoked\n", __FUNCTION__);
return T();
}
};
int main()
{
Foo<void> f1;
f1.Bar();
Foo<int> f2;
int i = f2.Bar();
return 0;
}
運行結果:
Foo<void>::Bar invoked
Foo<int>::Bar invoked
到此為止,我們已經實現(xiàn)了 0 個參數(shù)的函數(shù)支持,也即 R () 類型的所有函數(shù)的支持。接下來還要實現(xiàn)對具有 1 個、2 個、3 個直至任意有限個參數(shù)的函數(shù)支持。也許您也發(fā)現(xiàn)了,接下來的工作可以是體力活,我們可以照葫蘆畫瓢,搞出一堆 FunctionBaseN、FunctionN、MemberFunctionN,并在最后的 Function 中再實現(xiàn) N 個偏特化版本。是,不錯,大致上原理就是這樣。限于篇幅,我想暫時寫到這里,下篇將繼續(xù)談談宏、TypeList,以及怎樣少花點力氣實現(xiàn)其余 N 個版本。最終達到的效果是,只要改一個宏定義,就可以提高參數(shù)上限。
在本文所涉及的內容中,我比較糾結的是,可否在不用多態(tài)機制的情況下達到比較優(yōu)雅的形式統(tǒng)一?
歡迎討論。
posted @
2011-01-16 22:17 溪流 閱讀(7302) |
評論 (55) |
編輯 收藏
事情的緣起是,耐不住寂寞,準備開始造GUI的輪子。
GUI框架,要做的事情我想大概是這么幾步:
- 實現(xiàn)回調函數(shù)的成員化。
- 實現(xiàn)方便程度可接受的消息映射。
- 確定上述核心部件的使用方式。
- 制造大量的控件。
前三步要走的比較小心,第四步是體力勞動。
第一步,Windows下可參考的是MFC方式、WTL方式,以及利用Window相關屬性中的某些空位。前不久剛初步看過WTL的機制,雖然當時沒寫GUI框架的打算,不過也有點技術準備的意思了。現(xiàn)學現(xiàn)用吧。這里一個可以預見的問題是64位兼容,現(xiàn)在沒有測試環(huán)境,先不管。
接下來看第二步了,所要做的事情就是把 WndProc 下的 一堆 case 有效地組織起來,或者換個寫法。之前還真不知道 MFC/WTL 的 BEGIN_MSG_MAP。以為很高深的,想不到就是拼裝成一個大的 WndProc。先抄了,做成一個可運行的版本。但是,這方面會直接決定以后的大部分使用方式,單單抄一下意義不大。后來去 @OwnWaterloo 曾推薦過的 @cexer 的博客上逛了幾圈,第一圈看了一些描述性文字,第二圈大概看了下技術,第三圈是挖墳,那個傳說中的 cppblog 第一高樓啊。。其中有一個使用方式很新穎,嗯……是那個不需要手動寫映射代碼,直接實現(xiàn)消息處理函數(shù)的方式。不過我后來覺得還是不要這種樣子了,憑我個人的直覺,如果我寫下這樣的處理函數(shù),我大概會因為不知道何時注冊了這個函數(shù)而找不到調用來源而感到郁悶。在Windows回調機制的影響下,我可能會很抱有偏見地認為,只有直接來自WndProc的調用,才算是來源明確的,不需要繼續(xù)追蹤的——當然,這是建立在我不熟悉這個框架的基礎上的。框架必然需要隱藏調用來源,以及其他一些細節(jié),但是在這一步,我覺得稍微有點早。
剛才說到的都是靜態(tài)綁定。現(xiàn)在我有點傾向于動態(tài)綁定。從使用方便程度上來看,動態(tài)綁定更具靈活性。從性能上,動態(tài)綁定下,消息到處理函數(shù)的查找過程可以更快,靜態(tài)綁定只能遍歷。當然,未必將“添加處理函數(shù)”這樣的接口提供給最終用戶,但是這個操作對于整個控件體系的形成應該蠻有幫助的吧。比如MFC下一個控件類使用Message Map做了一些事情,繼承類就無法直接繼承這個動作,于是可能需要做兩套處理函數(shù)調用機制,一套是給內部繼承用的,一套是給用戶的。如果在最開始的基類保存一個消息映射,每個消息對應一族處理函數(shù),每個繼承類都可以添加處理函數(shù),但不刪除父類已添加的函數(shù),這樣就可以在一套Message Map機制下獲得父類的行為。以上,不知道考慮得對不對,歡迎討論。
其中,父類保存子類給出的可調用體并正確執(zhí)行是個問題。折騰了一些時間,都沒有成功。我比較糾結,想知道除了用function之類的玩意兒外還有沒有其他簡單可行的辦法。后來去@zblc的群上問,@vczh也說需要一套function機制。看來是逃不開這個問題了。嗯……想起來大約兩個月前一個同事從codeproject找來了一個GUI框架看,看到幾行整整齊齊的 AddMsgHandler(WM_CREATE, XXX(this, &MyWindow::OnCreate));,嘆不已。我當時打趣說,這很簡單的,無非是搞了個 function 而已,哥哥兩天就能搞定。于是他們叫我兩天搞定。我鼓搗了10分鐘,搞不定,只好丟一句,真的很簡單的,類似boost::function,你去看一下就知道了,哥哥要干活了。
既然現(xiàn)在還是繞不開這個問題,那還是搞一下了,搞好以后就權且當做給他們交作業(yè)吧。我會另寫一篇文章說說function的事情,這里先略過。現(xiàn)在開始假設這個設施已經造好了。那么,窗口類中大概可以這么定義相關類型:
typedef Function<bool (WPARAM, LPARAM)> MsgHandler;
typedef List<MsgHandler> MsgHandlerList;
typedef Map<UINT, MsgHandlerList> MsgMap;
然后再定義一個變量:
MsgMap m_MsgMap;
它用于保存消息映射。最終的回調函數(shù)可以寫成:
LRESULT WndProc(UINT uMsg, WPARAM wParam, LPARAM lParam)
{
bool bHandled = false;
MsgMap::Iterator itMsgMap = m_MsgMap.Find(uMsg);
if (itMsgMap != m_MsgMap.End())
{
for (MsgHandlerList::Iterator it = itMsgMap->Value.Begin();
!bHandled && it != itMsgMap->Value.End(); ++it)
{
bHandled = (*it)(wParam, lParam);
}
}
return bHandled ? TRUE : DefWindowProc(m_hWnd, uMsg, wParam, lParam);
}
最后給個添加消息映射的接口:
void AppendMsgHandler(UINT uMsg, MsgHandler pMsgHandler)
{
m_MsgMap[uMsg].PushBack(pMsgHandler);
}
到目前為止,我們的窗口類大致上可以寫成這樣:
#include <Windows.h>
#include <tchar.h>
#include "../GUIFramework/xlWindowBase.h"
class Window : public xl::WindowBase
{
public:
Window()
{
AppendMsgHandler(WM_ERASEBKGND, MsgHandler(this, &Window::OnEraseBackground));
AppendMsgHandler(WM_PAINT, MsgHandler(this, &Window::OnPaint));
AppendMsgHandler(WM_LBUTTONUP, MsgHandler(this, &Window::OnLButtonUp));
AppendMsgHandler(WM_RBUTTONUP, MsgHandler(this, &Window::OnRButtonUp));
AppendMsgHandler(WM_DESTROY, MsgHandler(this, &Window::OnDestroy));
}
protected:
bool OnEraseBackground(WPARAM wParam, LPARAM lParam)
{
return false;
}
bool OnPaint(WPARAM wParam, LPARAM lParam)
{
PAINTSTRUCT ps = {};
BeginPaint(m_hWnd, &ps);
RECT rect = { 200, 200, 400, 400 };
DrawText(ps.hdc, _T("Hello, world!"), -1, &rect, DT_CENTER | DT_VCENTER);
EndPaint(m_hWnd, &ps);
return false;
}
bool OnLButtonUp(WPARAM wParam, LPARAM lParam)
{
MessageBox(m_hWnd, _T("LButtonUp"), _T("Message"), MB_OK | MB_ICONINFORMATION);
return false;
}
bool OnRButtonUp(WPARAM wParam, LPARAM lParam)
{
MessageBox(m_hWnd, _T("RButtonUp"), _T("Message"), MB_OK | MB_ICONINFORMATION);
return false;
}
bool OnDestroy(WPARAM wParam, LPARAM lParam)
{
PostQuitMessage(0);
return false;
}
};
在最基礎的 WindowBase 里,搞成這樣大概差不是很多了。暫時先看第三步。到目前為止,我所聽說過的 GUI 框架都是真正的框架,似乎沒有“GUI 庫”。為什么一定要以繼承某個基類的方式來使用呢?如果像下面這樣使用呢?
class Window
{
private:
xl::WindowBase m_WindowBase;
public:
Window()
{
m_WindowBase.AppendMsgHandler(WM_ERASEBKGND, MsgHandler(this, &Window::OnEraseBackground));
m_WindowBase.AppendMsgHandler(WM_PAINT, MsgHandler(this, &Window::OnPaint));
m_WindowBase.AppendMsgHandler(WM_LBUTTONUP, MsgHandler(this, &Window::OnLButtonUp));
m_WindowBase.AppendMsgHandler(WM_RBUTTONUP, MsgHandler(this, &Window::OnRButtonUp));
m_WindowBase.AppendMsgHandler(WM_DESTROY, MsgHandler(this, &Window::OnDestroy));
}
};
這個問題,不知道各位有沒有什么思考?
還有一個問題是,接下去要不要將 WPARAM 和 LPARAM 的含義徹底解析掉,搞成一系列 PaintParam、EraseBackgroundParam、LButtonUpParam、RButtonUpParam,DestroyParam,讓使用的時候與原始消息參數(shù)徹底隔離呢?
最后一步,雖說是體力活,但這跟最終的應用場合密切相關,需要提供怎么樣的功能是一件需要考量的事。
目前走在第二步,所以下面的兩個問題思考得不多。求經驗,求意見。
posted @
2011-01-16 20:05 溪流 閱讀(4194) |
評論 (11) |
編輯 收藏