메뉴 건너뛰기


Developer > Application

C,C++ 모질라 코딩 스타일 가이드

2013.11.27 02:43

푸우 조회 수:16424


모질라 코딩 스타일 가이드

이 문서는 모질라 코드의 근간이되는 기본 스타일과 패턴을 설명하고 있다. 새로운 코드는 기존 코드와의 연계를 쉽게하기위해서 이 표준을 따라야한다. 물론 예외의 경우도 있을수 있으나 그럼에도 불구하고 이 규칙을 아는것이 중요하다.

기본적인 C/C++

  • 컴파일러 경고를 체크해보신적 있나여? 경고는 언제나 실제 버그로 발전합니다.
  • 64-bit에 맞게 변화되고 있나여?
  • 이식성 높은 C++ 가이드라인을 참고 하십시오.
  • 포인터를 위해 NULL을 사용하지 마십시오. 어떤 시스템에서는 void * 로 선언되어 포인터가 할당될때 경고를 유발시킵니다. 0 이나 nsnull 을 대신 사용하십시오.
  • 포인터를 테스트할때 !myPtr or (myPtr) 을 사용하지말구 myPtr != nsnull or myPtr == nsnull 을 사용하십시오.
  • == PR_TRUE or PR_FALSE 와 같이 비교하지 마시고 (x) or (!x) 을 대신사용하십시오
  • 리턴후에 else 를 놓지 마시오. 들여쓰기 레벨만 증가시킬뿐이다.
  • 항상 새로운 리턴 값이 null인지 체크하시오
  • 디버그 printf()를 남겨두지 마시오
  • 어떤 새로운 클래스 헤더파일이라고 자바 스타일의 코멘트를 사용하시오.
  • 문제를 수정할때, 그 문제가 같은 파일내에 어떤 다른 곳과 연관되어 있다면 가능한 모든곳을 수정해야한다.
  • nsFoo aFoo (bFoo) or nsFoo aFoo = bFoo 첫번째 티어의 플랫폼에서는 전자가 이론적으로 더 좋다. 후자를 좋아할 이유가 없다.좀더 시간이 흐르면 = 이 더 좋아보인다. 2번째 티어의 플랫폼에서는 더욱 좋다.
  • void DoSomething(nslContent * aContent) 함수에서 nsContent클래스 선언을 #include "nslContent.h" 대신에 이 함수 전에 class nslContent 와 같이 선언해서 사용하라 가능하면.

COM and Pointers

  • nsCOMPTR<> 을 사용하라. 만약 이것의 사용법을 모른다면 예제부터 보아라.
  • 새로운 XPCOM 인터페이스를 선언할때는 XPIDL을 이용하여라.
  • 강한 참조에서는 nsCOMPtr 을 사용하고 약한 참조는 nsWiakPtr을 사용하라.
  • nsAString을 가능하면 사용하라.
  • str.Length() == 0 대신에 str.IsEmpty() 을 사용하라.
  • 직접적으로 QueryInterface를 사용하지말고 CallQueryInterface 나 do_QueryInterface를 사용하라.
  • nsresult should be declared as rv. Not res, not result, not foo.
  • For constant strings, use NS_LITERAL_STRING("...") instead of NS_ConvertASCIItoUCS2("..."), AssignWithConversion("..."), EqualsWithConversion("..."), or nsAutoString()
  • Use contractids instead of progids or class IDs.

IDL

  • IDL에 있어서 메소드나 속성은 처음에는 소문자로 시작하십시오. long updateStatusBar(); 이런 식으로 말입니다.

  • 어떤 컨텍스트도 없이 간단한 값을 세팅하는데에는 메소드보다는 attribute를 사용하십시오

    interface nsIFoo : nsISupports {
         long getLength();
         void setLength(in long length);
         long getColor();
     };
    
    • 다음코드는 C++ signature와 거의 유사하지만 좀더 친근하다.

      interface nsIFoo : nsISupports {
             attribute long length;
             readonly attribute long color;
      };
      
  • 자바 스타일로 constants 를 사용하십시오 const long ERROR_UNDEFINED_VARIABLE = 1;

    • 에러 핸들링

      • 에러 체크는 일찍 그리고 자주 하십시오.

    • 나이스한 매크로를 사용하십시오.

      Use the NS_ENSURE_SUCCESS(rv, rv) and NS_ENSURE_TRUE(expr, rv) macros in place of if (NS_FAILED(rv)) { return rv; } and if (!expr) { return rv; }, unless the failure is a normal condition (i.e. you don't want it to assert)
    • 즉시 에러로 부터 돌아오시오. 대무문의 경우 에러 상태가 발생했을때 현제 함수로부터 돌아오는 것은 갑작스런 반작용을 수반하게 됩니다.

      • 이렇게 하지 마시오

        rv = foo->Call1();
        if (NS_SUCCEEDED(rv)) {
           rv = foo->Call2();
               if (NS_SUCCEEDED(rv)) {
                   rv = foo->Call3();
               }
           }
        }
        return rv;
        
      • 이렇게 하시오

        rv = foo->Call1();
        NS_ENSURE_SUCCESS(rv, rv);
        
        rv = foo->Call2();
        NS_ENSURE_SUCCESS(rv, rv);
        
        rv = foo->Call3();
        NS_ENSURE_SUCCESS(rv, rv);
        
      • 왜냐하면 에러 핸들링은 코드의 로직을 흐리게하는것이 아니기 때문이다. 첫번째에서 성공의 경우를 3번 호출하는데 주력했지만 if() 문은 코드를이해하기 힘들게 만들어 버렸다.

      • 좀더 복잡한 경우 실제적인 버그 처리를 고려해보자

        PRBool val;
        rv = foo->GetBooleanValue(&val);
        if (NS_SUCCEEDED(rv) && val)
           foo->Call1();
        else
           foo->Call2();
        
      • val 이 false 일 경우에만 foo->call2() 가 호출되도록 고려했지만 사실 foo->GetBooleanValue(&val) 이 실패했을때 foo->Call2() 가 호출될수 있다. 다음은 향상된 버젼이다.

        PRBool val;
        rv = foo->GetBooleanValue(&val);
        if (NS_FAILED(rv)) return rv;
        if (val)
            foo->Call1();
        else
            foo->Call2();
        
      • 이 예제에서 는 매우 깨끗한 형태가 되었다. 에러 컨디션은 foo->Call() 과 foo->Call2 ()둘다 피할수 있게되었다.

      • 가능한 예외상황 : 어떤경우 만약 호출에 실패해도 치명적이지 않을 수 있다. 왜냐하면 이벤트가 퇴출됐고 이를 옵저버에게 알려준다면 이런 알림 실패는 하찮게 된다.

        for (i=0; i<length; i++) {
           // we don't care if any individual observer fails
           observers[i]->Observe(foo, bar, baz);
        }
        
      • 다른 가능성이라면 컴포넌트가 존재하는지 또는 설치됐는지 또는 컴포넌트가 발견되지 않아도 정상적으로 가능한지등은 확신하지 않아도 된다.

        nsCOMPtr<nsIMyService> service = do_CreateInstance(NS_MYSERVICE_CID, &rv);
        // if the service is installed, then we'll use it
        if (NS_SUCCEEDED(rv)) {
          // non-fatal if this fails too, ignore this error
          service->DoSomething();
        
          // this is important, handle this error!
          rv = service->DoSomethingImportant();
          if (NS_FAILED(rv)) return rv;
        }
          
        // continue normally whether or not the service exists
        
      • Strings

        • 로컬 벨류에는 auto form 을 사용하시오. 로컬에 선언할때 짧은 주기를 같는다면 nsAutoString 이나 nsCAutoString를 사용하시오 이것은 스택에 이미 64byte 버트로 할당되어 힙의 단편화를 피할수 있다. 다음과 같이 하지 마시오.

        nsresult foo() {
                nsCString bar;
                ..
        }
        
        • 이렇게 하시오.

          nsresult foo() {
                nsCAutoString bar;
                ..
          }
          
      • non-XPCOM 함수로 부터의 부실한 값을 주의하시오. 이는 매우 간단한 함정인데 내부 헬프 함수르부터 할당된 스트링을 넘겨받구 이를 값의 free없이 코드에서 inline함스로 활용되는것이다.

        static char *GetStringValue() {
             ..
             return resultString.ToNewCString();
        }
             ..
             WarnUser(GetStringValue());
        
        • 위의 예제에서 WarnUse는 reultSting로부터 할당된 값을 가지게되고 포인터는 버려버린다. 결론적으로 결코 free되지 않는다. 이렇게 하는 대신에 자동적으로 free되도록 다음과 같이 쓰시오

          static void GetStringValue(nsAWritableCString& aResult) {
                  ..
                  aResult.Assign("resulting string");
          }
          
                  ..
                  nsCAutoString warning;
                  GetStringValue(warning);
                  WarnUser(warning.get());
          
          free the string manually:
          
          static char *GetStringValue() {
                  ..
                  return resultString.ToNewCString();
          }
          
                  ..
                  char *warning = GetStringValue();
                  WarnUser(warning);
                  nsMemory::Free(warning);
          
      • 런타임시에 스트링 컨버젼을 사용하지 말고 NS_LITERAL_STRING()를 사용하시오

        • INCORRECT

          nsAutoString warning; warning.AssignWithConversion("danger will robinson!");
          ..
          foo->SetUnicodeValue(warning.get());
          
        • CORRECT

          NS_NAMED_LITERAL_STRING(warning,"danger will robinson!");
          ..
          // if you'll be using the 'warning' string, you can still use it as before:
          foo->SetUnicodeValue(warning.get());
          
          // alternatively, use the wide string directly:
          foo->SetUnicodeValue(NS_LITERAL_STRING("danger will robinson!").get());
          
        • 대부분의 플랫폼은 컴파일러가 강제로 저레벨 유니코드 스트링으로 컴파일한다. 이를 직접적으로 할당해주다.

        • Naming and Formatting code

          • 모듈 소유주가 따르는 규칙을 따라라. 다른이들은 그냥 잔디일 뿐이다.

        • 공백 - 탭이 없도록하고 라인의 끝부분에 공백이 없도록한다.

        • 라인 길이 는 80정도 나 이보다 적게 한다.

        • Control Structures

          if (...) {
          } else if (...) {
          } else {
          }
          
          while (...) {
          }
          
          do {
          } while (...);
          
          for (...; ...; ...) {
          }
          
          switch (...)
          {
            case 1:
             {
              // When you need to declare a variable in a switch, put the block  in braces
             int var;
             } break;
            case 2:
              ...
              break;
            default:
              break;
          }
          
        • 클래스

          class nsMyClass : public X,
                              public Y
          {
          public:
          nsMyClass() : mVar(0) { ... };
          
          private:
            int mVar;
          };
          
          • 작은 함수라면 위의 방법도 좋지만 크다면 아래와 같이 한다.
        • 메소드

          int
          nsMyClass::Method(...)
          {
          ...
          }
          
        • 모드 라인

        • 오퍼레이터 - 만약 오버레이터가 라인 브레이크라면 다음에서 시작하지 마시오.

        • 다음과 같은 이름짓기에 따르시오

          • Variable prefixes:
            • k=constant (e.g. kNC_child)
            • g=global (e.g. gPrefService)
            • m=member (e.g. mLength)
            • a=argument (e.g. aCount)
            • s=static member (e.g. sPrefChecked)
          • Global functions/macros/etc
            • Macros begin with NS_ , and are all caps (e.g. NS_IMPL_ISUPPORTS)
            • Global (exported) functions begin with NS_ and use LeadingCaps (e.g. NS_NewISupportsArray)

        • 출처 : http://emstonebebop.wordpress.com/projects/programming_rules/mozila_coding_style_guide/