메뉴 건너뛰기


Developer > Application

C,C++ 그놈 프로젝트 프로그래밍 지침서

2013.11.27 02:41

푸우 조회 수:18685


Introduction

그놈에서 사용하는 기초적인 UI 라이브러리인 GTK+ 는 소프트웨어 디자인 측면에서 우리에게 많은걸 가르쳐주고 있다. GTK+ 코드는 깨끗하고, 조화가 잘되고(consistent) 유지가 용이하며(maintainable) 이치에 맞다. 이런 코드는 일을 할때도 좋지만 이 코드를 확장하거나 변경하려는 사람들에게 좋은 연습이된다.
이 글에서는 여러분들이 그놈 프로젝트를 할때 고려해야할 만한 몇가지 지침들을 보여줄것이다. CVS를 사용하여 다른 사람의 코드를 고치거나 그놈이 의도에 부합되는 코드를 만들었는지 확인해 볼때 따라야 하는 것들을 제시해 보일것이다. 또한 Package Maintainer에게 유용한 정보도 가르쳐 주겠다.
이 문서 보고나면 GNU Coding Standard라는 문서를 꼭 읽어봐라.

좋은 코드를 작성의 중요성

그놈은 참 대규모적인 프리 소프트웨어 프로젝트이며 많게건 작게건 서로 관련을 가진 패키지들로 구성되었다. (생략...)
소프트웨어는 오랜시간동안 만들어졌으며 많은 량의 작업들로 이루어졌다. 그래서 대부분의 짬을 내서하는 자원자들이 아주 큰 프로젝트를 시작할수 없는것이다. 따라서 즉시 결과를 볼수 있는 기존에 있는 프로젝트에 참여하는 것이 더 효과적일지도 모르겠다.
위에 말했던 점과 프리소프트웨어 프로그래머가 적다는 점을 고려해보면, 기존에 있는 프로젝트에 참여하는 사람들이 참여하기 쉽도록 프로젝트를 만드는 것이 중요하다는 것을 알 수 있다. 이런 방법 중에 하나로 읽기쉽고 이해하기 쉽고 바꾸기 쉬운 프로그램을 만드는 것이다.
지저분하게 코드를 만들면 읽기도 어렵고, 사람들이 분석하다가 막히면 흥미를 잃기쉽다. 프로그래머로서 사람들이 소스를 쉽게 이해해서 짧은 시간동안 기존 소스의 버그를 고치고 개선하게 하는 것이 중요하다. 소스코드도 하나의 커뮤니케이션의 한 형식으로, 사람들이 오자에 잘못된 문법의 책을 읽기 싫어하는 것처럼 , 프로그래머도 다른사람이 이해하기 쉽고 고쳐보기도 쉬운 코드를 만드는데 노력해야 한다.
다음에는 좋은 코드이기 위한 중요한 요건들이며 이것들이 프리 소프트웨어 개발자에게 왜 중요한지도 설명해두었다.
Cleanliness
코드가 읽기 쉬워 이해하는데 적은노력으로 이해할수 있게 한다.
Consistency
프로그램이 어떻게 돌아가는지 쉽게 이해하도록 한다. consistent 한 코드를 읽게되면 자신도 모르게 코드가 어떻게 돌아가는지에 대한 추측과 예상이 떠오르게 되며 이는 그런 코드를 수정하는데 보다 쉽고 안전하게 한다.
Extensibility
범용적인 코드는 어려운 내용(?)으로 이루어진 그런 매우 특정한 소스보다 재사용하거나 고치는데 용이하다. 처음에 만들때부터 Extensibility 를 고려한 프로그램은 나중에 누군가 프로그램에 새로운 것들을 추가하려 했을때 더욱 편하다. 첨부터 그것을 고려치 않은 코드들은 새로운 특성을 부여하기 위해 굉장히 어글리한 행동을 유도하게 될것이다.
Correctness
마지막으로 Correctness 를 고려해서 만든 프로그램은 나중에 사람들이 프로그램을 디버깅하거나 개선하는데 드는 시간을 줄인다. 충돌이 일어나는 프로그램은 아무도 좋아하지 않을 것이기에 , 사용자들은 그런 Correct 한 코드에 감사해 한다.
이것들을 정리해 보면, 프로그래머들은 짬을내서 프리 소프트웨어 프로젝트에 참여하거나 정규적으로 참여하더라도 중간에 그만둘 여지도 충분히 있기때문에 소스를 만들때 쉽게 고칠수 있도록 만드는 것이 중요하다. 결과는 프로그래머들이 개선하고 싶어하는 보다 나은 프로그램이 될것이다.

코딩 스타일

Conding Style 이란 소스코드가 작성되는 형식을 말한다. C를 예로들면 {}중괄호의 위치, 들인 위치, 괄호가 사용되는 방법들을 의미하는 것이다. 그놈에서는 여러 코딩스타일이 있으며 , 어느 하나도 강요하지 않는다. 보다 중요한 것은 프로그램을 Consistent 하게 만들라는 것이다. - 엉망진창인 코드는 읽기 어렵기 때문에, 안돼요.
새로운 프로그램 혹은 라이브러리를 만들때에는 중괄호 위치랑 들인위치에 대해 Consistent 한 스타일을 따르기 바란다. 당신이 참조하는 코디스타일이 없다면 리눅스 커널에 사용되는 사타일이나 GNU 코딩스타일을 추천하고 싶다.
-GNU 코딩스타일 (Standards) Writing C
-리눅스 커널 코딩 스타일 CodingStyle
위 두 문서들이 그놈 코드를 작성하는데 좋은 아이디어를 줄것이다.

들여쓰기 형식

여기서는 리눅스 커널에서 사용되는 8 space 들여쓰기를 선호한다.
이런 8 space 들여쓰기는 많은 이득이 있다. 들여쓰기 위치가 확실하기때문에 코드를 읽기 쉽게하며 , 함수들을 보다 모듈화하고 잘 정의된 덩어리로 나누는것을 유도하여 코드가 honest 하게 한다. 계속 들여쓰기를 하다가 너무 들여쓰기가 오른쪽으로 치우쳐질때까지 갔다면 함수가 잘못 디자인 되었다는 것이며, 그것을 보다 모듈화 하던지 아니면 그 부분에 대해 다시 생각해봐야 할것이다.
8 space 들여쓰기 방식은 한화면에 적당한 함수를 디자인 하는데 도움을 줄것이다. 무슨말인고 하니 위아래로 왔다갔다 할것없이 소스를 쉽게 파악할수 있게 된다는 것이다.
Emacs 을 사용한다면 .emacs 화일에다가 다음과 같은 것들을 포함시켜 리눅스 커널 들여쓰기를 선택할수 있다.
(add-hook 'c-mode-common-hook
         (lambda ()
           (c-set-style "k&r")
           (setq c-basic-offset 8)))
새로운 Emacsen 이나 새로운 cc-mode 에서는 간단히 다음과 같은것으로 대신할수 있다.
(add-hook 'c-mode-common-hook
         (lambda ()
           (c-set-style "linux")))
vim 을 사용하면 ~/.vimrc 화일에다가 다음과 같은 것을 포함시켜 GNOME 커널 들여쓰기 형식을 선택할 수 있다.
set ts8
if !exists("autocommands_loaded")
 let autocommands_loaded  1
 augroup C
     autocmd BufRead-.c set cindent
 augroup END
endif
Emacs 에서는 GNU 들여쓰기 방식이 디폴트니까 .emacs 화일에다 원하는 스타일을 갖다 붙여 여러 들여쓰기 방법을 사용할 수 있다. 명시적으로 선택하고 싶으면 위에 나오는 "linux" 대신 "gnu"를 사용하시요.(?)
다른 에디터에서도 이런 들여쓰기 방식을 쓰는 법을 알고 싶으면 연락해줘..

이름 정하기

이름 정하는건 무지하게 중요하다.
함수이름은 module_submodule_operation 과 같은 형태로 지어져야 한다. 예를 들면 gnome_canvas_set_scroll_region 이나 gnome_mime_get_keys 를 들수있다. 이런 이름 짓기는 심볼이름의 내부모듈 충돌(?)을 제거해준다. 이건 라이브러리에서 매우 중요하다.
심볼은 설명적인 이름이어야 한다. 리누스가 말하기를 cntusr()같은 함수를 쓰지말고, count_active_users()로 쓰라고 한다. 이는 프로그램을 읽기 쉽게 하기도 하지만 프로그램 자체적으로 설명적이 된다.
GTK+나 GNOME 라이브러리 에서 사용되는 이름짓기를 사용해 봅시다.
  • 함수이름은 소문자로 하고 단어끼리는 _(언더 스코어)로 구분한다.

gnome_canvas_set_scroll_region() , gnome_mime_get_keys()
  • 매크로나 Enum은 대문자로 쓰고 언더스코어로 단어를 구분한다.

  • GNOMEUIINFO_SUBTREE() - macro , GNOME_INTERACT_NONE - enum
  • 타입이나 구조체 이름은 대소문자를 번갈아가면서 쓴다.

    GnomeCanvasItemGnomeIconList
    • 언더스코어로 단어를 구분하는것은 빨리 둘러보기 위한 에디터 명령을 사용할 수 있어 코드를 덜 어지럽고 편집하기 쉽게해준다.
      라이브러리를 작성하다보면, 오직 라이브러리에서만 사용되는 심볼을 export할 필요가 있다. libfoo.so라는 라이브러리로 구성된 두개의 오브젝트트 화일이 있을때 , 이들이 서로 심볼을 억세스 해야할 필요가 있지만 사용자 프로그램에서 이를 사용하는것이 의도되지 않았다. 이런경우 함수 이름앞에 언더스코어를 붙이고 처음단어를 standard module/submodule 명명법에 의해 이름짓는다. 예를 들면 _foo_internal_fronbnicate()라른 것을 들수 있겠다.
      Consistency in Naming
      일관된 이름짓기는 매우중요하다. 예를 들면 리스트 조작을 하는 모듈인 경우에 간단하게 하기 위해 리스트 포인터인 "l"을 사용하여 변수를 명명할수 있다. 그렇지만 widget 나 size를 조작하는 모듈에서 모두 w를 사용한다면 일관되지도 않을뿐아니라 읽기도 어려울 것이다.
      물론 이런 짧고 간단한 명명법은 함수에 대해 지역적인 변수에 대해 사용을 해야한다. 전역적인 변수에 그런 'x'와 같은 변수를 쓰는것은 하지 말아라. 보다 설명적인 긴 이름을 사용하기 바란다.

      Cleanliness

      그놈 코드는 가능한 깨끗(?)해야 하는데, 이말은 위에서 말했더 일관된 들여쓰기를 한다던지 좋은 이름 짓기를 했다던지를 뜻하며, 다음과 같은 내용들도 포함한다.
      static 키워드에 대한 적확한 사용을 배워라. 모든 심볼을 전역으로 하지 말아라. 그러면 모든 부분에서 Visible 하지 않아 module/ submodule prefix(?)를 할 필요가 없기 때문에, 단일 소스 파일의 내부 함수에 대해 보다 짧은 심볼 이름을 사용할수 있는 이점이 있다.
      const 키워드에 대한 올바를 사용을 배워라. 일관되게 사용하여 컴파일러가 당신이 범한 어의없는 버그들을 잡는데 도와준다.
      사용자가 free하리라 생각되지 않는 함수 내부적인 데이터를 리턴하는 함수인경우 const modifier를 사용하라. 이렇게 하면 사용자가 잘못된 것을 시도한 경우 사용자에게 경고를 해줄것이다.
      const char-gnome_mime_get_info(const char * info)
      만약 사용자가 리턴되는 스트링의 데이터를 free 해주려고 한다면 컴파일러가 경고를 할것이고 이로서 많은 버그들을 잡을수 있게 된다.
      프로그램이나 라이브러리에서 random "magig value"를 사용한다면 그것들이 사용된 곳에서 hardcoding 하지 말고 macro를 사용해라.
      /* Amount of padding for GUI elements-/
      #define GNOME_PAD          8
      #define GNOME_PAD_SMALL    4
      #define GNOME_PAD_BIG      12
      
      변수에 대해 변수에 들어갈수 있는값 몇몇개가 정해져 있다면, 그 값을 위해 매크로를 사용하지 말아라 차라리 enum을 사용하고 그 type의 이름을 사용하라. 그렇게 하면 디버거에서 그런 몇몇 값에 대한 심볼릭 이름을 갖게한다.(?) 또한 enumeration 값을 저장하려고 int를 사용하지 마라. 대신 아까 만든 enum type을 사용하라. 이렇게 하면 디버거에서 이런 값에 대한 정확한 값을 보여주며 어떤 값이 사용가능한 것인가를 명확하게 하면서 컴파일러는 에러를 잡아준다.
      /* Shadow types-/
      typedef enum {
             GTK_SHADOW_NONE,
             GTK_SHADOW_IN,
             GTK_SHADOW_OUT,
             GTK_SHADOW_ETCHED_IN,
             GTK_SHADOW_ETCHED_OUT
      } GtkShadowType;
      
      void gtk_frame_set_shadow_type (GtkFrame-frame, GtkShadowType type);
      
      Bit field에 대해 value의 셋을 정의했다면 , 이렇게 해라.
      /* Update flags for items-/
      enum {
             GNOME_CANVAS_UPDATE_REQUESTED   1 << 0,
             GNOME_CANVAS_UPDATE_AFFINE      1 << 1,
             GNOME_CANVAS_UPDATE_CLIP        1 << 2,
             GNOME_CANVAS_UPDATE_VISIBILITY  1 << 3,
             GNOME_CANVAS_UPDATE_IS_VISIBLE  1 << 4
      };
      
      이렇게 하면 값의 리스트를 편집하기도 쉽고 직접 값을 입력해 주는것보다 에러가 발생활 확률이 적어진다. 또한 디버거에서 위에서 쓴 값들을 심볼로 사용할수 있게 한다.
      애매한 코드를 작성하지 말아라. 엄격한 코드를 작성하라. 표현(__EXPRESSION__)을 보다 이해하기 쉽게하기위해 사용되는 괄호(parehthsis)의 수보다 더 많이 사용하지는 마라. 괄호전, 콤마 이후, 바이너리 오퍼레이터의 양옆으로 빈칸을 넣어라
      코드에 해킹을 하지 말아라. 어설프게 해킹을 할바엔 코드가 clean하고 extensible하고 maintainable 할 수 있도록 코드를 재작성해라.
      컴파일러로 컴파일할때 warning이 일어나지 않도록 코드를 만들어라. 이렇게 해야 엉뚱한(stupid) 버그를 고치는데 도와준다. 일관되게 헤더파일에 함수의 프로토타입을 사용하라.(?)
      그놈을 보면, configure.in. 에서 GNOME_COMPILE_WARNING autoconf macro를 사용할 수 있는데, (글쎄...)
      코드에 주석을 달아라. 각각의 함수 앞에 이 함수가 어떤일을 하는지 주석을 달아주길 바란다. 꼭 필요하지 않으면 안써도 된다.(?) 코드를 읽을때 그 뜻이 명확해야 함은 당연하다. 그렇지 않으면 코드가 이해하기 쉬워질때까지 다시 작업해야 할것이다.
      라이브러리에 대한 API 함수를 문서화 할때,gnomelibs/devel-docs/api-comment-style.txt 에있는 가이드라인을 따르라. 이렇게 함으로서 당신의 소스코드가 inline 도큐먼트를 제공하게 되고 이를 gtk_doc 시스템을 사용하여 DocBook 메뉴얼을 만들수 있게된다.



      출처 : http://emstonebebop.wordpress.com/projects/programming_rules/gnom_programming_guideline/