본문 바로가기
컴퓨터/전산, 그 외

개인적인 코딩 규칙

by adnoctum 2009. 12. 27.


아래 나열하는 coding convention은 어디까지나 개인적인 것으로 몇몇 사안의 경우, 보다 더 좋은 방법이 있더라도 나의 개인적인 취향 때문에 사용되는 것도 있을 것이다. 그러나 나는 그 더 좋은 방법이 나의 취향 - 취향은 일관성을 제공한다 - 을 어기면서까지 그것을 사용할 어떤 큰 장점을 발견하지 못해서 사용하고 있는 것이므로, 만약 그것을 사용함으로 인해 얻게 되는 이점이 내가 생각하고 있던 것보다 더 크다는 것을 알게 되면 언제라도 바꿀 준비가 되어 있다. 가장 좋은 코드는 코드 그 자체가 곧바로 설명을 하는듯한 코드이고, 따라서 묵시적인 것을 최대한 줄이려 한다.


언급되지 않으면 기본적으로 C++ 문법에 대한 것이고, 구분할 필요가 있을 때만 Windows와 linux를 구분하였다.


syntax highlight를 사용한다.
    syntax highlight는 단순히 예쁜 코드 그 이상을 의미한다. 주로 사용하는 keyword가 예약되어 있어 그러한 예약어들이 특정 색으로 나타날 때, 만약 그렇게 될 것을 기대했는데 나오지 않았다면 오타가 난 것이다. 종종 사소한 오타 때문에 몇 시간동안 디버깅하는 일이 있는데 그와 같은 비효율성을 미연에 방지할 수 있게 된다.


 
tab은 space 4개로 사용한다.
    4 개로 하느냐 2 개로 하느냐. 코드는 보아서 확실히 표시가 나는 것이 좋다. 2중, 3중 으로 안으로 들어가는 경우가 잦고, 특히 닫히는 } 의 경우 연이어 2, 3 줄이 있는 경우가 많으므로 4 칸이 보기 좋다.



default parameter는 될수 있으면 사용하지 않는다.

    함수명이든 변수명이든 그 이름에서 그 함수/변수가 무엇을 하는지 알기 쉬워야 하고, 특히 함수의 경우 각 parameter가 어떤 것인지 알기 쉬워야 한다. 그런데 default parameter를 사용하게 되면 함수의 이름에서 예상되는 parameter가 있을수도 있고 없을 수도 있다는 가능성 때문에, 그리고 더 중요하게는 프로그래머가 의도적으로 생략하지 않은 parameter가 default로 setting 된 값으로 넘어갈 가능성 때문에 default parameter는 될수 있으면 사용하지 않는다.



if 문의 경우 명시적으로 boolean을 나타낸다.
    C/C++ 의 경우 if 문은 식의 값이 0 이 아니면 모두 true로 하는 것은 때때로 유용하게 사용되나, if 문의 판별식이 프로그래머가 보았을 때 즉각적으로 이해할 수 없는 것이라면 혼란을 가중시킨다. if ( ! file_handle ){} 이렇게 해도 되지만 if( file_handle != NULL) {} 이렇게 한다. 만약 반환값이 int 인 함수를 if 문의 판별식으로 사용할 경우, 0 이 반환되었다는 것은 무슨 의미인가? 함수 이름에서 그 의미가 명확해야 하지만, 때때로 에러 코드가 -1 을 반환한 수도 있다. 따라서 if 문은 꼭 if ( expr == true) or if ( expr == false) 와 같이 true, false를 명시해 준다.



font는 자신이 보기 편한 것을 사용한다.
    주로 대문자 i 와 소문자 L 의 구분, 괄호 () 와 부등호 <> 를 구분하기 쉬운 폰트가 좋다. Tahoma, Courier, Courier New 를 사용하다, 지금은 terminal 을 사용한다. 이 font의 경우, 그러나, 괄호와 부등호 구분이 좀 안되지만 이런 것은 컴파일시 에러가 나기 때문에 찾기 쉬워 그냥 사용하고 있다.



parameter로는 참조를 사용하지 않는다.
    만약 참조를 사용해야만 할 경우 함수 안에서 그 값을 바꾸면 반드시 const 로 지정한다. 참조의 경우, 함수의 안에서 그 값을 바꾼 효과가 그 함수가 끝나도 사라지지 않는다는 사실 때문에, 참조로 넘긴 값이 변경되는지 안되는지 함수의 호출자는 항상 생각을 해야 한다. 바로 이 점이 혼란을 가중시킨다. 따라서 만약 함수 안에서 parameter로 받은 값을 변경시켜야 한다면 pointer로 넘기고 아니라면 value로 넘긴다.



자주 compile 하여 test 해본다.
    함수 하나, 또는 함수 안의 한 기능을 구현했을 때마다 compile 하여 에러를 확인한다. 디버깅의 핵심은 '어디'에서 에러가 났는지 알아내는 것이고, 수정/추가한 부분이 적을 때 compile 하여 test할수록 '어디'에서 에러가 났는지 확인하기가 쉬워 진다. 나는 왜 compile을 자주 하지 말라는지 모르겠다.



inline이나 register keyword는 사용하지는 않는다.
    어차피 compiler가 알아서 잘 해줄 것이라 생각하기 때문이다. 그러나, 몇몇 플랫폼에서는 이러한 keyword를 명시해 주어야 하는 경우가 있는 것으로 보인다.



MFC 6.0 의 경우, control을 사용할 경우 DDX를 사용하기보다는 그 control을 직접 다룬다.
    edit box에 CString 변수를 연결하거나 하기보다는, CEdit instance에 대한 변수만을 설정해 사용한다. DDX가 valid value를 확인해 주는 등의 몇 가지 기능이 있기는 하나, 하나의 컨트롤에 대하여 값을 설정하는 그 이상의 무엇을 할 때가 있다. control 을 감추거나/보이거나, 움직이거나 등등. 그 때 CString 만으로 연결되어 있으면 다시 control을 부르는 등의 추가 작업이 필요하므로 아예 처음부터 control 형 변수만 선언해 사용한다.



중첩은 될수 있으면 적게한다.
    개인적인 것이라기보다는, 잘 그리고 많이 알려진 것. 중첩이 5, 6 중 으로 되고 있다면 코드가 잘못되고 있다고 생각하면 된다. if 문의 경우 특정 조건의 경우만 안으로 들어간다면, 그 조건의 부정일 때 들어가지 않도록 하는 식으로 중첩을 줄이도록 한다. continue는 의외로 쓸 곳이 많이 있다.



함수와 함수 사이는 보기 좋게 한다.
    이건 visual basic editor에서 나오는 것인데, 꽤 편해서 다른 것에서도 이렇게 일일이 하고 있다. 다음처럼 함수 하나하나를 바로바로 알수 있도록 함수 정의 부분에 긴 주석 몇 개를 넣어 준다.






code에 직접 사용되는 변수를 주석에 사용할 경우 [ ] 안에 넣어 준다.
    변수 이름을 일일이 생각하는 것은 힘들다. 자주 사용되는 변수가 있고, 그것이 주석에서 사용되는 일반적 의미의 단어와 동일할 경우 혼란을 줄이기 위해, 코드에 사용되며 직접적인 변수명으로 사용되는 단어의 경우 The file size is [size] 와 같이 [ ] 로 감싸 준다.



member 변수 이름은 밑줄 ( underline, _ ) 으로 시작한다.
    변수 이름을 통해 변수의 역할을 추측할 때 쉽게 하기 위해 변수명은 최대한 설명적으로 만들게 되는데, 그러다 보면 지역 변수와 member 변수의 이름이 같을 때가 있다. 즉, scope만 다를 뿐 하는 역할은 비슷할 때가 있고, 이름으로 그 역할을 쉽게 알게 하다 보면 변수 이름이 겹칠 때가 있다. 이럴 때 쉽게 구분하기 위해 member 변수 이름은 underline으로 시작하게 한다. 물론 사람에 따라 m_ 으로 시작하는 경우도 있다.



control 변수의 경우 control type을 알 수 있는 접두어를 사용한다.
    edit control의 경우 edt, check box의 경우 chk 등. 헝가리언 표기법은 꽤 유용한 경우가 있고, 특히 control type의 변수에 그러하다. CButton type의 변수이나 check box가 될 수도 있고 radio button이 될 수도 있는데 그럴 때는 그 역할에 따라 부여한다. 즉, CButton type이라 하더라도 check box이면 chk, radio 버튼이면 rdo 식으로. 중요한 것은 '역할'이지 누구한테 상속받았는지가 아니므로. 이것은 델파이에서도 그대로 유지된다(사실 델파이할 때 들인 버릇이다).



긴 switch/if-else 보다는 포인터(배열)을 사용한다.
    코드도 이해하기 쉽고, 더 깔끔해진다. if-else나 switch 가 길어지는 많은 경우 포인터를 이용하면 간편하게 할 수 있는 경우가 많다. 다음은 한 예.


int idx = nID - IDC_BTNUPARROW;

void (CPanTiltController::*operation[])(int) = {
CPanTiltController::TiltUp, CPanTiltController::TiltDown,
CPanTiltController::PanLeft, CPanTiltController::PanRight,
CPanTiltController::FocusNear, CPanTiltController::FocusFar,
CPanTiltController::ZoomWide, CPanTiltController::ZoomTele };

if(_bMicroControl == true){
    (_ptc.*operation[idx])(20);
}
else{
    (_ptc.*operation[idx])(500);
}

연산자 앞뒤는 공백으로 한다.

    너무 빽빽하면 보기에 답답하다. 연산자 뿐만이 아니라 콤마(,) 뒤도 공백을 하나 둠으로써 parameter 구분을 쉽게 해 놓는다.


SHGetFileInfo(strDrive, 0, &sfi, sizeof(sfi), SHGFI_DISPLAYNAME | SHGFI_SYSICONINDEX);


위처럼. bit 연산자건 산술연산자이건 공백이 앞뒤 하나씩 있으면 코드가 보기 좋아진다.



한 식이 여러줄에 걸칠 때는 위아래 줄에 일관성을 둔다.
    if 문 안의 식이 여러 줄이 될 경우, 조건을 알아 보기 쉽도록 논리 연산자의 위치를 맞춰 준다.


나 는 if의 괄호가 닫히는 곳에서 중괄호를 여는데 위처럼 여러 줄이 if 문의 조건식으로 들어갈 경우 다음 줄에서 중괄호를 열어줌으로써 어디에서 열리는지 확인하기 쉽게 해 놓는다. (사실 위와 같은 경우 vector의 [] 연산자를 여러 번 부르는 것이기 때문에 임시 변수에 그것을 저장해 놓고 사용하는 것이 원칙적으로는 더 유리하나, 컴퓨터는 충분히 빠르기 때문에 저 프로그램에서는 그냥 저렇게 사용했다).


변수 이름은, linux에서 할 때는 set_maximul_length 와 같이 소문자로만 하고 underline을 쓰고, windows에서 할 때는 setMaximumLength 또는 SetMaximumLength와 같이 한다. 이건 특별한 이유는 없고, 그냥 버릇이다. -.- template이 많이 쓰이는 STL 코드를 gcc로 컴파일 할 때는 꼭 class type을 명시해 주지만, VC++에서는 잘 안 해준다. std::make_pair<int,int>(a,b)와 같은 경우.
프로그램밍을 하면서 중요한 것 중 하나는, 알아 보기 쉬운 코드를 작성하는 것 같다. 내가 만든 프로그램의 사용법을 잠시 잊는 것은 뭐 그런대로 괜찮다, 어차피 사용자가 잘 알고 있으니까. 문제는 내가 짠 코드를 나중에 보면 좀 알아 보기 힘든 경우가 있는데, 그것은 그 당시에는 너무나도 자명하다고 생각하여 별다른 주석을 달지 않았거나 좀 더 쉽게 고치지 않았던 것들이다.

                            while((logicalDrives >>= 1) != 0);

와 같이 C/C++ 언어 자체의 의해 또는 template과 STL/functor의 조합으로 인해 항상 알아 보기 쉬운 코드를 작성할 수 있는 것은 아니지만.

'컴퓨터 > 전산, 그 외' 카테고리의 다른 글

나의 친구 Segmentation fault  (0) 2010.06.04
프로그래머에게 겸손이란  (0) 2010.01.13
실행 메뉴 사용하기  (0) 2009.12.27
사소한 속도까지 신경쓰는 이유  (0) 2009.12.22
프로그래머와 수학  (1) 2009.12.22