Pages

Subscribe:

Ads 468x60px

Labels

2011年12月11日 星期日

Motivation
源起
I have developed a lot of programs, so alought there is no one formal description, I already have one set of naming rules. Recently, I took apart in several group-developing projects. These exprements make me feel that: something such as naming is unnecessary (even awkward) if it is hardness; but is important to list them if you need a general direction.
雖然不能稱得上非常多,但是我個人開發過的程式還算不少,所以雖然沒有很正式的規定,但是已經有自己的一套命名規則;加上最近參加過幾次團體開發的經驗,覺得這種東西,硬性規定的確沒有必要,或者說,還蠻鳥的,不過提些大方向,尤其是條列出來,還蠻重要的。

This document will list several basic naming rules, maybe a few concepts about source code writing. In addition, I will suggest some minor issues, such as indenting. But just like many other documents on Internet, do that as a natural sequence is most important.
這份文件打算條列出一些基本的命名規則,或許還會寫寫一些關於原始碼的想法,什麼地方該怎麼寫,什麼地方不該怎麼寫;還有些更無關緊要的,像是縮排要幾格?我會提些建議,但是就像其他網路上文件提過的,自己習慣還是最重要的。

Variable Prefixes
變數前置字元
Prefix Meaning Description
g global Always used on global variables. But some global variable such as those in a library but that library doesn't provide any interface to access them, because they are global only in their own library, conceptually they are local in that module. So I will use m_ as a prefix on these variables.
通常使用在全域變數上,但是有些全域變數,像是一個函式庫的全域變數,因為它只在那個函式庫中全域,在概念上,我覺得反而比較像是那個模組裡的區域變數,所以這時我會選用 m 開頭,而不是使用 g 。
m module, member You could think them as module variables or member variables. But I like conceptual explanation more personally. So whenever I name an variable, not only class member variables will be prefixed by m_. Above situation is a example. Basically, only variables which span two or more modules will be prefixed by g_.
模組和成員變數,這兩種解釋都通,但因為我個人比較喜歡概念上的解釋,所以通常在使用上,不是只有在物件中(物件才有成員變數)才會用到 m 開頭的變數,上面提的例子就是一個,基本上,會超越模組(跨模組)使用的變數我才會以 g 開頭。
s static In principle, I will use s_ on those variables which are specified by static keyword (in C/C++). Of course, this kind of variables is rare. If you have a lot of s_ varialbes, there are two possible reasons: 1. your program is very special, or 2. you make a mistake! (maybe you confuse the concept of static variable.) Besides, some people like use st_ to be a prefix of static variables. Maybe st_ comes from static, but since one character will not confuse with other prefix characters, why we use two characters?
靜態變數,原則上,使用 static 這個關鍵字(在 C/C++ 語言中)的變數,我就會使用 s 開頭,當然,這在程式中是很少使用的,靜態變數多有兩種可能,一個是程式特殊,另一個就是你寫錯了!或是搞錯靜態變數的概念,基本上應該是不常用到才對;另外也有人喜歡用 st 開頭,取自 static 吧,不過既然一個字元就不會重複了,沒什麼必要用兩個字元。
local There is usually no any prefix before local variable. Here "local" means usually the area between two braces of functions. I list a identical variable in four representations: g_foo, m_foo, s_foo, and foo.
區域變數,通常前面是不會有任何前置字元的,而這邊所謂的區域,通常在函式的兩個大括號中間。這邊列出同一個變數,用四種表示法分別為 g_foo, m_foo, s_foo, foo 。

Variables Type
變數型態
Type Meaning Description
a array 老樣子,我還是喜歡邏輯上的意義,所以同樣是 int * 的型態,如果變數是代表著一個整數陣列,就用 a 開頭,若是代表著一個整數指標,則用 p 開頭。
ch char 字元型態,我不知道有沒有人只有 c 開頭的就是了。
d double 就目前來講,我希望所有浮點數都是倍精確度,所以只有定義 d 開頭的代表 double ,而沒有指定 float 所使用的縮寫。
f flag 其實應該就是布林代數,不過 C/C++ 語言中並沒有這個型態,我個人習慣使用 BOOL 來代表這個抽象型態;其實還有另一種命名法,就是使用 b 開頭代表 boolean ,然後 f 就可以用來代表 float ,但是至少目前我打算採用。
h handle, height 如果用過 handle 這種抽象型態的人,應該知道特地定出來是有其必要的,當然,如果沒用過,就別理它了;另外在不會搞混的情況下,代表高度的變數用 h 開頭也不錯。
i index, int 通常都用來當做索引變數,或是整數變數,雖然我比較喜歡前者,但是整數實在是太常用了,所以在我的程式碼中還是蠻常採用後者。
l long 長整數。
n number 通常用來代表量的變數,配合 i 使用不錯,例如 for (iFoo = 0; iFoo < nFoo; ++i) 。
p pointer 代表某種型態的指標,通常還會配合其他的型態。
sz string end of zero 以 NULL 結尾的字串型態,通常是 char * ,至於為什麼要叫 sz ,則是學別人的,也有人用 str (因為 string ),當然,也有人只有 s 開頭的(因為沒有別的型態衝突)。
t time_t 不一定是 time_t ,代表時間的型態應該都可以使用 t 開頭的變數。
u unsigned 無號數,通常會配合其他的型態一起使用。
w width 代表寬度的變數,通常會和 h 開頭一起用。

Variable Naming
變數命名
由於除了型態和前置字元之外,剩下部分的命名就跟英文沒什麼差別了,所以也無法一一列出,在這邊只會列出一些大方向,以及建議,沒有一定,重點有兩個,首先,自己要看得懂,用起來覺得方便(簡潔),再來,就是希望別人也能一眼就看出來,不然就失去了定標準的意義了。

最簡單的,像是 i, j, x, y 還有 tmp 之類的變數,不用增加多餘的字元了。
一定要使用之前的提的前置字元和型態。
還有一套專門給 Windows Programming 的型態命名法,像是 wnd, bmp 之類的,可能有空再整理出來。
對於不在上面所列的型態(前置字元應該不會有例外才對),取縮寫的方式如下
刪母音,例如 region 可以縮成 rgn (這例子是學 MFC 的), table 可以縮成 tbl 。
取前面數個字元,例如 prefix 可以縮成 pre , binary可以縮成 bin 。
上面兩種,取意義比較明顯的那一個,像是 prefix 只取母音的話變成 prfx ?而 region 取前三個字元的話變成 reg ,太廣泛了,要想到 region 很難,我猜很多人會想到 register 吧?
有時可以多取幾個字元,像是 function 縮成 fun 或許不怎麼樣,但是縮成 func 就不錯(也有人縮成 fn 就是了)。
若是懶得想,或是都沒什麼意義,建議還是採用刪母音的方法。
最後,能短就短,不過有時為了意義要表達清楚,最後整個單字都用上了也無可厚非,看個人喜好,在這邊是建議越簡潔越好。
一個整數陣列要取成 aiFoo 還是 iaFoo 呢?我的習慣是前者,我覺得知道這個變數是一個 array 比知道每個元素是 int 要來得重要;它是一個陣列,而不是一個整數。
同理 piFoo 和 ipFoo 我也是喜歡 piFoo ,因為說到底, piFoo 是一個指標,不是一個整數。
那 paiFoo 跟 apiFoo 有什麼不同呢?前者代表一個指向(整數)陣列變數的指標;後者則是代表一個陣列變數,其中每個元素都是一個(整數)指標。這樣又有什麼不同?邏輯上不同罷了,程式碼都是 int * * ,不過在使用上會有差, (*paiFoo)[i] 跟 *(apiFoo[i]) 才是有意義的,而 *(paiFoo[i]) 跟 (*apiFoo)[i] 可能會使程式當掉。
其他的部分,就把整個變數的意義直接用英文打出來即可;取縮寫的技巧可以參考上面提的。
例如一個依據原子密度來排序的陣列,其中第 i 個元素表示第 i 個原子的密度是在所有原中子的名次,這個名次,我想用 int 就可以了,那麼這個陣列變數我會命名成 aiAtmDnstyRnk ;其中 Atm 代表原子( atom ),Dnsty 代表密度( density ),而 Rnk 代表排名 (Rank) ,至於順序則是用語言的習慣,"原子密度的排名",最後一個詞最為重要,通常在語法上是名詞,有人說英文是 rank of atom-density (rank of density of atom) ,這就真的看個人了,我喜歡把代表性的名詞放在最後,也可以放在最前,其他的順序,就真的不重要了,甚至去掉變成 aiRnk 也無妨(在不會混淆的情況下)。
再強調一次,能短就短,有時取太長,還不如在宣告的地方加個註解來得清楚。
Function Prefixes
函式前置字元
Prefix Meaning Description
i inline inline functions, which are after "inline" keyword (in C++). Because inline functions are more efficient so I create the prefix character. Then whenever I want to call some functions, I could find inline functions first for efficiency.
擁有 inline 這個關鍵字的函式,這是只有在 C++ 中才有的, C 語言並沒有。另外,使用這個函式並沒有什麼特別,我當初定的原因只是因為 inline function 執行起來比較有效率,加上這個前置字元時,我會知道呼叫這個函式會比較有效率罷了。
m module, member You could think them as module functions or member functions. But I like conceptual explanation more personally. So whenever I name an function, not only class member functions will be prefixed by m_. Even some public class member functions, I wouldn't use m as their prefixes. Only pretected and private class member functions are prefixed by _m_. Besides, some functions in a library, although they are not class member functions, if that library doesn't provide a interface to access these functions (so-called module function), then I would use _m_ as their prefixes.
模組和成員函式,這兩種解釋都通,但因為我個人比較喜歡概念上的解釋,所以通常在使用上,不是只有在物件中(物件才有成員函式)才會用到 m 開頭的變數,事實上,在物件中若是 public 的成員函式,我依然不會加上 m 的前置字元,只有受保護的成員函式( protected, private )我才會用;而在某些函式庫中的函式,就算不是一個物件的成員函式,但是若是這個函式庫沒有提供此函式的介面給外界呼叫,如同 m 的變數一樣,我稱之為模組函式,應以 m 開頭。
s static In principle, I will use s_ on those functions which are specified by static keyword (in C/C++).
靜態函式,原則上,使用 static 這個關鍵字(在 C/C++ 語言中)的函式,我就會使用 s 開頭。
other There is usually no local functions. So global (so-called "other") functions are the most common. I list a identical function in four representations: _i_foo(), _m_foo(), _s_foo(), and foo().
通常函式是不分全域還是區域的,所以這邊所謂的其他,就是一般的全域函式。這邊列出同一個變數,用四種表示法分別為 _i_foo(), _m_foo(), _s_foo(), foo() 。

函式的前置字元應該是我自己定的,至少,我沒有參考過別人的文件;至於之所以變數的前置字元是 m_ 開頭,而函式則由 _m_ 開頭,這多一個底線的原因,主要是因為排序的關係,我希望同類的函式能排在一起,而是沒多這個底線的話,那麼所有模組函式可能會和一個 m 開頭的一般函式排在一起,我覺得很醜,所以才這樣定。

Function Naming
函式命名
函式命名和變數命名大同小異,除了函式不要求型態之外(沒看過有人要求,我目前也覺得還好),還有我自訂的函式前置字元,應該沒什麼特別的,但是下面還是列出一些在函式命名和變數命名上的差別。

通常在 C 中,大家喜歡全小寫的函式名,中間用底線分開;例如 get_car_speed() 。
通常在 C++ 中,大家喜歡用大寫的字來分隔單字;例如 GetCarSpeed() 。
通常在 JAVA 中,大家喜歡用小寫開頭,但是又用大寫的字來分隔單字;例如 getCarSpeed() 。
函式通常最重要的意義在動詞,放在最前面,像上面的例子"取得車子的速度",用這個順序幾乎沒有人會有意見。
函式取縮寫的習慣似乎不怎麼普遍,完全不縮寫也蠻常見的,不過還是可以取,像是 rgb2hsv() (原本應該是 convert_red_green_blue_format_to_hue_saturation_value_format() 才對);其中 red 等縮成 r 蠻直覺的,而用 2 取代 to 更是常見;然後利用 to 的意義,進而省略 convert 也蠻直覺的。
上面的例子,雖然很特別又有點極端,但是又說明了一次,能短就短,何況函式的註解相當重要,所以與其取一個長到懶得打的函式名,還不如在旁邊寫一個詳細的註解。
Indent
縮排
使用跳格 (Tab) 字元當作最基本的單位。
跳格建議設為 8 個空白,很多人會不習慣,但是空白越多,在視覺上越輕鬆,用一用就會習慣了。
函式的左大括號,必須換行,例如

void foo() // here need a new-line
{
...;
...;
...; // here need a new-line, too
}
迴圈 (for, while) 以及條件 (if, switch) 的左大括號,皆不用換行,例如
for (...) { // watch here, only one new-line
...;
...; // here need a new-line, too
}
while (...) { // watch here, only one new-line
...;
...; // here need a new-line, too
}
if (...) { // watch here, only one new-line
...;
...; // here need a new-line, too
}
if (...) { // watch here, only one new-line
...;
...; // here need a new-line, too
} else { // watch here, only one new-line
...;
...; // here need a new-line, too
}
switch (...) { // watch here, only one new-line
case ...: // no any word after colon
...; // here need a tab
break; // here need a new-line, too
default:
...; // here need a new-line
}
結構 (struct) 宣告的左大括號,必須換行,例如
struct // here need a new-line
{ // here need a new-line, too
double d;
int i; // here need a new-line
};
下面是我常用的程式碼,順便提一下 typedef 應該如何使用
typedef struct _DOUBLEandINDEX // the struct name is a suggestion
{
double d;
int i;
} DOUBLEandINDEX, * LPDOUBLEandINDEX; // here are two types
上面有提到結構的命名,通常跟其變數或是函式命名的原則差不多,但是如果要用來當一種型態的話(使用 typedef 定義過後),我建議全部用大寫;至於上面名稱中使用小寫的 and ,是為了分隔前後兩個單字,這樣可以一目了然這個結構倒底長什麼樣子;記得在 struct 後面的名稱中加個底線(也有人喜歡加 tag 這個單字,小寫),這樣後面才能使用。
物件 (class) 宣告的左大括號,必須換行,例如
class Car // here need a new-line
{ // here need a new-line, too
protected: // no tab, need a new-line
BOOL m_fDoorClosed;
int m_iSpeed;
public:
void CloseDoor();
void OpenDoor();
void SlowDown(int iDiff);
void SpeedUp(int iDiff); // here need a new-line
}
除了縮排外,上面提到物件的命名,跟結構不同,我建議首字大寫,其餘字母小寫,分隔時也使用大寫,例如 class MyCar ;中間的程式碼我沒有省略,主要是因為我想提醒一下之前的命名法,實際上使用大概就像這樣。
我要提一個自己訂的名詞,單行縮排(英文就先用 inline indent 好了,所以也可以叫行內縮排),因為我雖然有看過別人這樣縮,但是沒看過文件提起過,下面用實例來說明比較方便
double * adDnsty = NULL; // density of each atom, need to free
int i; // iteration variable
int iMax = -1; // index of maximum rank
int iMin = -1; // index of minimum rank
double * adDnsty = NULL; // density of each atom, need to free
int i; // iteration variable
int iMax = -1; // index of maximum rank
int iMin = -1; // index of minimum rank
看得出上面兩塊的差別嗎?我在每行中,都加入了不等的跳格鍵,為了什麼?只是為了方便閱讀而已!通常一個變數的宣告,習慣好的話,會有四個部分,型態,名稱,初始值,以及註解;上面第二塊的程式碼,就有分別對這四塊部分做縮排,現在我把跳格鍵畫出來再看一次

double *^t adDnsty = NULL; // density of each atom, need to free
int^t ^t i;^t ^t // iteration variable
int^t ^t iMax^t = -1;^t // index of maximum rank
int^t ^t iMin^t = -1;^t // index of minimum rank
上面有底線的部分,就是跳格鍵,不過有些地方是連續兩個跳格鍵,所以我會加上 ^t 的記號;而那些沒有底線的空格,就是一個空白鍵而已。不難看出來,我只對四個部分做縮排(所以一行有至少有三個跳鍵)。

而像 double * ,雖然中間有空白,但我認為整個(包括星號)才算是型態,所以就用空白;或是 = NULL ,因為大家都有 = 這個字元,所以只要前後都用跳格鍵,那這邊就算用空白也會很整齊,何必讓整行又變得更長?

另外像是 int i 那行,沒有初始值,或是 double * 比其他型態都多了一格跳格,這時就會發現 ^t 的數目不太一樣,調整這跳格字元的數目就是我所謂的單行縮排,通常在變數宣告的地方使用,例如結構宣告,物件宣告,在函式開頭做區域變數的宣告等,有時可以一群一群相關的變數才對齊,否則相當費時且如果有個變數更動,全部都得更著改,反正,只要自己看起來覺得整齊就可以了。


詳見

沒有留言:

張貼留言