가독성(readability)에 대한 결정

지금 진행하는 프로젝트의 coding idiom에는 가독성(readability)에 대한 부분이 정의되어 있다.


많은 사람들이 관리해야 할 코드에는 가독성이 필요하다는 것에는 의문이 없다. 하지만 무엇이 더 가독성이 좋은가에 대한 의견에는 의문이 많다.


 


1. if 문 뒤에 딱 한 라인이 올 때의 처리


(1) if (cond) 

       return false;


(2) if (cond) 

    {

        return false;

    }


위의 둘 중에 무엇을 택해야 하는 가에는 항상 논란이 많았다. (1)안을 택하게 되면 2개의 라인으로 내용을 모두 볼 수 있다는 장점이 있고 (2)안의 경우에는 indentation에 대한 예외를 두지 않는 규칙이라는 장점이 있다. (코딩에 실수를 할 우려가 있다 등등은 하수를 위한 것이므로 논외) 


현재 내가 진행하는 과제에서는 (2)안이 채택되어 있지만 나는 (1)안을 주장했다. 가독성의 가장 큰 장점이 빨리 코드를 파악해야 한다는 것인데 (1)안처럼 쓰면 한 눈에 코드 2-(2)안을 쓰면 화면 아래에 있을-을 더 볼 수 있다는 장점이 있기 때문이다 

 

 


2. 가로로 긴 줄을 잘라서 개행하기


(1) 가로로 라인이 길어도 개행 하지 않기. 


(2) 128자 등의 기준을 두고 기준 라인이 넘을 경우 개행 권고 


이것도 논란이 많은 이슈 중에 하나다. Linux 쪽의 유명한 사람은 아예 길게 코드를 짤 수 없게 하라는 말도 하였으나 현실과는 좀 맞지 않고, 최근에는 변수 이름을 길게 쓰거나 template을 파라미터로 쓰고 있기 때문에 가로줄이 계속 길어지고 있다.


현재는 가로로 스크롤을 하지 않고도 모든 코드를 다 볼 수 있는 (2)안이 채택되어 있으나 나는 (1)안을 주장하였다. (2)안을 쓰면 코드가 바뀔 때마다 개행 해야 하는 위치를 바꿔야 하는 것이 불편한데다가, 가로로 긴 코드를 실제로 끝까지 봐야 할 일은 거의 없다는 이유에서다.


가로로 줄이 길게 나오려면 주로 파라미터가 많거나 파라미터를 표시하는 방법이 길다는 것인데, 보통 코드를 분석할 때는 앞의 함수 이름을 보고 내가 관심이 있을 때만 뒤의 파라미터까지 보는 것이 대부분이라 나는 (1)안이어도 문제가 없다는 생각이다. 


 


3. 매크로에 대한 것


(1) 매크로가 유리할 때는 써야 한다.


(2) 매크로는 최대한 쓰지 말자. 


이 주제는 goto를 써야 하나 말아야 하냐의 오랜 주제처럼 의견이 많은 내용이다. 하지만 회사의 프로젝트에는 나름대로의 규칙이 존재해야 하기 때문에 이것에 대한 결론도 내어야 하였다. 결론은(2)안이 채택되었으나 나는 아주 예전부터 이것에 관해서는 (1)안을 주장해 왔다. C++ 책에 보면 매크로를 잘 못 썼을 때의 폐혜를 나열하면서 inline template으로 만들라고 한다. 하지만 그것은 매크로를 잘 못 짜거나 잘 못 사용한 경우에 대한 것이므로 제대로 만들었을 때는 문제가 없다는 것이다. 사실 규칙이 (2)안으로 정해졌기 때문에 나도 매크로를 inline template으로 바꾸는 작업을 하였는데, 주로 한 일이 1줄 짜리 매크로를 동일한 기능을 하는 7줄 짜리로 만드는 일이었다. 


그리고 각 method 마다 객체의 validity를 체크 하는 것이 method 제일 앞에 매크로로 들어가 있었다. 매크로만 신뢰한다면 항상 해당 method는 매크로 이후에는 객체 상태가 valid 하다는 것을 보장하는 코드이다. 게다가 수시로 바뀌는 validity 조건을 딱 한군데에서 제어할 수 있기 때문에 큰 장점이 있는 구조였다. 하지만 매크로를 없애야 하는 일이 생기면서 이 부분도 수정을 해야 하게 되었는데 이 매크로 안에는 return과 같은 제어문이 들어 가기 때문에 inline template으로도 만들 수가 없다. 그래서 결국은 매크로를 모두 풀어서 각 method 제일 앞에 넣는 수 밖에 없는 것이다. (물론 이건 안 하고 있다) 


그래서 나는 (1)안을 주장한다. 


Posted by 안영기

2012/05/28 23:17 2012/05/28 23:17
Response
0 Trackbacks , 5 Comments
RSS :
http://smgal.ismine.net/tc_191/blog1/rss/response/42

Trackback URL : 이 글에는 트랙백을 보낼 수 없습니다

copy constructor 문제?!

정적 분석 툴에서 다음과 같은 코드의 copy constructor에서 잠재적인 문제점이 있다는 보고를 받았다. (코드는 핵심적인 부분만 남긴 것이다)

 

하지만, 이쪽 코드는 우리가 최근 수정한 적이 없기 때문에 다른 모듈에서 최근 새로운 용법이 생기면서 발생한 문제라고 판단하면서 문제 분석을 하였다.

 

 

class A

{

public:

    A(void);

    A(const A& ref);

 

    int m_val;

};

 

A::A(void)

    : m_val(0)

{

}

 

A::A(const A& ref)

{

    m_val = ref.m_val;

}

 

 

확인 결과, 보통은 문제가 없었지만 다음과 같은 형태의 코드가 추가되면서 잠재적인 문제점이 발생한 것이다. (역시 핵심만 추린 것이라 코드 자체의 로직에는 큰 의미가 없다)

 

enum AType

{

    TYPE_DEFAULT

};

 

A GetA(AType type)

{

    switch (type)

    {

    case TYPE_DEFAULT:

        return A();

    }

    // A를 사용하는 쪽에서 return 구문이 없다.

    // enum의 모든 case에 대해서 switch 문에서 처리하므로

    // 정적 분석툴에서는 문제 없는 것으로 인식 한다.

}

 

int main(void)

{

    A a = GetA(TYPE_DEFAULT);

    A b = a;

 

물론 위의 코드만으로는 아무런 문제가 없다. 하지만 누군가가 악의적으로 다음과 같이 사용했을 때가 문제가 된다.

 

    A a = GetA(AType(TYPE_DEFAULT+1));

 

이와 같이 강제로 enum에 정의되지 않은 것을 강제로 넣게 되면 GetA() A를 리턴하긴 하지만 생성자가 불리지 않은 A의 임시 객체를 리턴한다. 그렇게 되면 멤버 변수는 초기화 되지 않은 상태이고 A b = a; 에 의해 copy constructor를 통한 b를 생성한다. 이 때 m_val = ref.m_val; 부분을 지나가게 되는데 a는 초기화된 적이 없으므로 알 수 없는 값인 ref.m_valm_val를 초기화 하기 때문에 최종적으로는 A class copy constructor 문제라고 보고가 된다.


Posted by 안영기

2012/05/28 20:16 2012/05/28 20:16
Response
0 Trackbacks , 0 Comments
RSS :
http://smgal.ismine.net/tc_191/blog1/rss/response/41

Trackback URL : 이 글에는 트랙백을 보낼 수 없습니다

new A 와 new A()에 대해

이번에는 new에 대한 이야기 이다.

몇 달 전에, Coding idiom을 정하는 쪽에서 코드의 통일성을 위해 파라미터가 없는 생성자의 생성법을 idiom으로 정리를 하자는 의견을 내었다. 간단하게 이야기 하면 new를 할 때의 코드 표기법을 new A 또는 new A() 중에 하나를 권장하는 쪽으로 가자는 것이다.

나의 경우, 처음에는 stack에서 생성할 때는 A a(); 가 아닌 A a; 를 사용해야 하므로 new 할 때도 new A()가 아닌 new A; 를 해야 한다는 주장을 하였다. 그런데 또 다른 쪽에서는 현재의 C++ 규약에서는 new A()와 new A의 동작을 각각 다르게 정의를 하고 있으므로 그 정의에 따라 결정을 하자고 했다.

그래서 문제의 방향은 new A()와 new A의 차이점을 보고 결정하기로 하였는데, C++1998과 C++2003에서 각각 정의하고 있는 new A()와 new A 의 차이점은 다음 링크에 잘 정리되어 있다.


원래 문제는 좀 더 복잡한 상태에서의 결정이지만, 이 문제를 간단하게 줄이면

- new A 는 메모리만 할당을 하며 사용자가 명세하지 않은 임의의 일을 더 하지 않는다.
- new A() 는 메모리 할당뿐 아니라 멤버의 값을 0으로 초기화 시켜 준다.

이렇게 정리가 된다.

여러 사람들이 각각의 의견을 내었고, 나는 C++1998이냐 C++2003이냐와 상관없이 (1)컴파일러가 임의의 일을 더 하지 않는 new A 방식을 택하여야만 개발자가 항상 명확한 코드를 짤 수 있다는 주장이었고, 반대 쪽의 의견은 (2)항상 0으로 초기화 시켜주어서 개발자가 실수하더라도 문제가 적어지도록 적절히 초기화가 되어야 한다라는 주장이었다.

최종적으로는 나의 의견은 채택되지 않았지만, 나는 여전이 (1)번을 주장하고 싶다. -std 옵션이 무엇이 되든 간에 코드의 consistency을 보장할 수 있다는 것, 즉 예외가 없는 규칙이 가장 좋은 규칙이라는 생각에는 변함이 없다.

사실 이래나 저래나 큰 영향은 없는 규칙이고 개인의 철학과 다양성의 문제이다 보니, 괜히 이런 결정에 브레이크를 걸어 여러 사람의 뒷다리를 잡는 일은 없어야 하기에 그냥 다수의 결정에 따랐다.

Posted by 안영기

2012/04/15 04:46 2012/04/15 04:46
Response
0 Trackbacks , 0 Comments
RSS :
http://smgal.ismine.net/tc_191/blog1/rss/response/39

Trackback URL : 이 글에는 트랙백을 보낼 수 없습니다

POD를 상속한 후손의 assignment operator

최근 정적 분석 툴에 의해 다음의 코드에 문제가 있다는 통보를 받았다.
(실제 코드에서 문제점만 최대한 압축하였다)

struct A
{
int a;
};

class B: public A
{
public:
B& operator=(const B& ref);
};

B& B::operator=(const B& ref)
{
if (this == &ref)
return *this;

this->a = ref.a;

return *this;
}

대부분의 경우는 정적 분석 툴의 설명만으로 문제를 수정할 수 있었지만 이 경우는 도저히 어떤 문제가 있는 것인지 알 수가 없었다. 이 자체로는 문제 있는 것이 아니었기에 컴파일러의 warning level을 높여도 문제가 검출 되지 않았다.

그래서, 결국은 SE팀에게 분석을 요청했고 이 문제에 대한 명료한 대답이 돌아 왔다.




아래처럼 변수를 직접 대입하면 (현재는 문제가 없지만) 잠재적인 문제가 있기 때문에

this->a = ref.a;

다음과 같이 자동으로 만들어질 A의 default assignment operator를 불러야 한다고 한다.

A::operator=(ref);

반론을 찾기 위해 이래 저래 의심 나는 부분에 대해서 어셈블리어로 만들어서 분석해 보았는데, 위 같이 하면, 나중에 A가 어떻게 변하더라도 A만 제대로 구현하였다면 B의 assignment operator는 문제가 없는 것으로 확인되었다. 컴파일러가 알아서 저 부분의 코드를 그때 그때에 맞게 잘 변경 해 주었던 것이다.

Posted by 안영기

2012/03/01 08:35 2012/03/01 08:35
Response
0 Trackbacks , 2 Comments
RSS :
http://smgal.ismine.net/tc_191/blog1/rss/response/38

Trackback URL : 이 글에는 트랙백을 보낼 수 없습니다

OpenGL ES 2.0 샘플 (Android ver.)

원래는 Android용 게임을 만들어 보려고 OpenGL Es 2.0을 이용하여 3D 및 스프라이트 등을 출력하는 기본 라이브러리를 만들고 있었다. 그렇게 시작한 것이 벌써 1년이 지났다.

하지만 시간에 쫓기다 보니 우선 순위에서 계속 밀리게 되었고 자칫하다가는 영원히 Android용 게임은 완성되지 않은 채로 프로젝트가 끝나지 않을까 하는 우려까지 하고 있다. 여전히 Android용 게임 제작은 전체 우선 순위에서 밀리고 있고(Java에 익숙하지 않은 탓과 Java로는 개인 라이브러리가 구축된 것이 없다는 것이 가장 큰 걸림돌이다) 끝까지 성공해낼 확률은 꽤 낮은 편이다.

이것은 가장 초기에 만들었던 출력 테스트 샘플이다. Cube가 제대로 도는지 확인 해본 것이고 shader로 vertex, diffuse color, texture 출력까지 만들어 본 것이다. 간단한 터치도 테스트 해보기 위해 터치를 한 위치에 cube가 돈다.

사용자 삽입 이미지
Android용 OpenGL ES 2.0 출력 샘플 (44K)

프로젝트가 1.5 이상용인데, 너무 구형 프로젝트인지 실제로 import를 해보면 프로젝트 파일 관련 에러가 난다. 에러가 난 메시지로 구글에 검색해보면 프로젝트 파일 고치는 법이 나오는데, 그 방법을 써서 수정하면 된다.

Posted by 안영기

2011/12/01 23:04 2011/12/01 23:04
Response
0 Trackbacks , 7 Comments
RSS :
http://smgal.ismine.net/tc_191/blog1/rss/response/37

Trackback URL : 이 글에는 트랙백을 보낼 수 없습니다

동급생2 맵출력 샘플 (bada ver.)

여느 때처럼 아침에 일찍 일어 났다. 하지만 오늘은 토요일이고 모처럼 회사를 안 가도 되는 토요일이다 보니 갑자기 시간이 너무 많아졌던 것이다.

딱 하루짜리 프로젝트를 해 보자는 생각에 15년 전에 한글화 할 때 끄적였던 리소스들을 꺼내어서 간단한 맵 출력 샘플을 만들기로 하고 아침에 app 개발을 위한 manifest.xml 을 발급 받았다. 그리고 오전과 밤시간을 이용해서 만든 것이 아래의 결과물이다.

사용자 삽입 이미지
십 몇 년 전에, 게제동을 통해서 DOS용으로 pascal과 asm을 이용해서 만들어서 소스를 공개했던 것인데 이것을 bada 용으로 만들어 보았다. 언어도 pascal + asm에서 C++로 바뀌었고 구조도 완전히 다 바뀐 것이라 완전히 새로 만드는 기분이었다.

일단 bada app의 이야기인 app을 만든 방법은,

- 기본 C++ project에서 Form-Base app을 선택
- 처음부터 있는 기본 Form을 타이틀이 없는 Form으로 수정
- 더불어 UI 관련 xml은 삭제
- application.xml 에서 auto scaling 관련 옵션 제거
  (게임에는 사용하지 않는 편이 품질을 보장할 수 있음)
- manifest는 API 1.0 version으로 발급
  (더 많은 기기와 버전에서 동작하도록 하기 위함)
- Application에는 Timer와 관련 listener를 추가
- Application의 OnForeground에서는 timer를 ON
- Application의 OnBackground에서는 timer를 OFF 했습니다.
- Timer 이벤트가 발생할 때마다 10 ms의 간격으로 게임의 메인 루프를 실행

- 게임은 1995~6년도의 원작의 resource를 그대로 사용
  (ELF사에 저작권이 있는 부분입임)
- 당시 4-bit용 게임이었으므로 GetCanvasN()의 포맷인 ARGB8888로 리소스를 변경
- 터치 입력을 추가, 스크린의 4 방향의 가장자리를 누르면 유이가 움직임

bada용 동급생 2 맵 출력 샘플 (257K)

Posted by 안영기

2011/11/06 02:22 2011/11/06 02:22
Response
0 Trackbacks , 2 Comments
RSS :
http://smgal.ismine.net/tc_191/blog1/rss/response/36

Trackback URL : 이 글에는 트랙백을 보낼 수 없습니다

대변 파이터 (bada 이식作)

이번에는 DOS -> Windows -> WIZ -> CANNOO를 거쳐 bada 플랫폼에도 대변 파이터를 이식해 보았다. (앱스토어에 올릴 수가 없으니 풀소스로 첨부)

해상도가 800x480으로 커졌기 때문에 CANNOO에서까지 써왔던 320x240용 리소스는 모두 교체를 했다. 예전의 DOS용 16컬러 데이터를 복원하였고 일부 대사도 현재에 맞게 수정하였다.

제일 어려웠던 것은, 키보드 전용 게임을 풀터치로 만드는 일이었는데 결과적으로는 재미없는 게임이 되고 말았다. 처음부터 풀터치로 기획되지 않은 게임을 풀터치에 올리려는 자체가 잘 못 된 것이다. 게다가 2인용도 불가능 해서 하나의 조작으로 둘 다 동시에 움직이는 키배치로 수정을 하였다.


사용자 삽입 이미지
bada용 대변 파이터 (569K)



Posted by 안영기

2011/10/17 07:40 2011/10/17 07:40
Response
0 Trackbacks , 8 Comments
RSS :
http://smgal.ismine.net/tc_191/blog1/rss/response/35

Trackback URL : 이 글에는 트랙백을 보낼 수 없습니다

나머지 연산자에 의한 실수

아래와 같은 코드를 만들었다.

예로 들기 위해서 만든 것이라 별 의미는 없는 struct인데, time_stamp라는 값이 들어 왔을 때 이 값을 짝수이면 내부 변수에 -1을, 홀수이면 +1을 대입하도록 하였다. 그리고 다음과 같이 짝수는 2로 나눈 나머지가 0이라는 것을 이용해 switch - case 문을 만들었다.

struct Step
{
explicit Step(int time_stamp)
{
// 어떤 값을 2로 나누었을 때,
// 나머지 값은 0 또는 1만 가능하다.
switch (time_stamp % 2)
{
case 0: m_step = -1; break;
case 1: m_step = +1; break;
}
}
int m_step;
private:
Step();
};

이런 방식의 코드를 Test case를 만들기 위한 유틸리티로 집어 넣었고, 곧바로 정적 코드 분석 툴에 의해 위의 코드는 잠재적인 문제가 있다는 통보를 받았다. 지금 보면 당연한 것인데도 보고를 받고 문제를 알아차리는 데는 십 분 정도 걸렸다.


정답은, 특정 조건에서는 생성자에서 m_step에 값을 대입하지 않아서 가비지 값으로 남아 있을 수 있다는 것이고, 그것에 대한 직접 적인 원인은 time_stamp 변수가 음수일 때를 고려 하지 않았기 때문이다.

0 이하일 때 나머지 계산의 값을 보면,

time_stamp = 0, -2, -4, -6, -8, … 일 때는 0
time_stamp = -1, -3, -5, -7, -9, … 일 때는 -1

의 값이 된다. 따라서 이 경우는 위의 코드에서 case -1: 을 추가 해야만 정적 분석 툴에서 문제를 통보하지 않는다.


Posted by 안영기

2011/08/07 21:12 2011/08/07 21:12
Response
0 Trackbacks , 0 Comments
RSS :
http://smgal.ismine.net/tc_191/blog1/rss/response/34

Trackback URL : 이 글에는 트랙백을 보낼 수 없습니다

극한 테스트

Embedded 기기의 양산 제품에 대한 SW 테스트는, 거쳐야 하는 관문이 꽤 많다.

보통은, 개발자에 의한 (1)Unit test, QA 팀의 (2)Integration test / (3)Stress test, 그리고 SW 인력의 손을 떠난 (4)Monkey test나 (5)극한테스트(가칭)가 있다. 그 중에서 극한테스트(가칭)의 경우는 나에게 잊을 수 없는 개발 경험을 준 적이 있다.

때는 5~6년 전 어느 날, 갑자기 양산 검증 쪽에서 급보가 날아 들었다.

고온의 불가마에서 TV가 제대로 나오는지를 테스트 하는 실험이었는데 20일 정도를 계속 켜 놓았더니 TV가 죽었다는 것이다. 시리얼로 덤프를 받아서 개발팀에 넘겼고, 문제를 일으킨 것은 나의 코드였다. 양산 검증 막판에 이런 식의 에러가 발생하면 생산 라인이 가동을 멈추어야 하기에 이것은 꽤 큰 일이었다.

급하게 문제의 소스를 확인 해보니 다음의 위치였다.
(방금 창작한 코드라 당시의 코드와 조금은 다를 수 있다)

if (dst_alpha > 0)
{
unsigned long src_rate = (src_alpha *                                  (MAX_ALPHA_VALUE - dst_alpha)) / MAX_ALPHA_VALUE;
unsigned long last_rate  = src_rate + dst_alpha;

assert(src_rate < 256);
assert(last_rate < 256);
assert(last_rate > 0);
}

양산 제품에 왜 assert()가 빠지지 않았냐는 일단 차치하고(최종 버전을 릴리즈 모드로 하지 않은 듯 하다) 어떤 상황에서 assert()에서 죽을 수 있는 지를 검토해 보았다.

다음과 같은 검증 코드를 만들어서 돌렸고, 모든 유효한 값을 다 넣어 봐도 assert(false)는 일어 날 수 없다는 것을 증명하고서야 나의 혐의는 풀렸다.

void probe(unsigned long src_color, unsigned long dst_color)
{
#define MAX_ALPHA_VALUE 255

unsigned long src_alpha = (src_color >> 24);
unsigned long dst_alpha = (dst_color >> 24);
if (dst_alpha > 0)
{
unsigned long src_rate = (src_alpha *                                  (MAX_ALPHA_VALUE - dst_alpha)) / MAX_ALPHA_VALUE;
unsigned long last_rate  = src_rate + dst_alpha;

assert(src_rate < 256);
assert(last_rate < 256);
assert(last_rate > 0);
}
}

테스트

for (unsigned long s = 0; s <= 0xFF; s++)
for (unsigned long d = 0; d <= 0xFF; d++)
{
unsigned long src_color = (s << 24);
unsigned long dst_color = (d << 24);
probe(src_color, src_color);
}

정상적인 상황에서는 절대 발생할 수 없다는 것이 쉽게 증명이 되는 상황이었기에 다행인 것이지, 만약 이런 것을 증명하기 애매한 경우라면, 며칠을 밤을 새워 가며 시달렸을지도 모른다.

이 경우는 극악한 환경에서의 CPU 오동작과 관계가 있었겠지만, 이런 것 이외에도 memory cache의 타이밍 문제나, LCD controller의 전송 특징과 같이 S/W와는 직접적으로 관련 없어 보이는 것도 S/W의 문제로서 처리해야 하는 경우가 많다. 그리고 우리가 그런 문제점을 통보 받게 되면 S/W에서 처리 가능한 문제가 아니라는 것을 증명하기 위래 아까운 시간을 낭비하게 된다.

그나마 다행인 것은, 이런 문제를 많이 접하면 접할수록 과거의 경험을 통해 S/W의 문제가 아니라는 증명을 그나마 빨리 해낼 수 있게 되었다는 점이다. (물론, H/W device나 compiler를 의심하는 것은 S/W 엔지니어가 가장 마지막에 검토해야 하는 것이다)

Posted by 안영기

2011/06/29 21:51 2011/06/29 21:51
Response
0 Trackbacks , 4 Comments
RSS :
http://smgal.ismine.net/tc_191/blog1/rss/response/33

Trackback URL : 이 글에는 트랙백을 보낼 수 없습니다

switch 문의 최적화

내가 하는 일 중에는 API를 만드는 일도 한다. 그러다 보면 return type이 스펙에 맞게 제대로 되었나를 알아 보기 위해 negative test에 대한 test case를 만든다.

그 중에 enum의 경우는 사용자가 악의를 가지고 범위에서 벗어난 파라미터를 줄 수가 있으므로 그것에 관해서도 체크를 해야 한다. 예를 들어, A와 B만들 가지는 enum에 대해 강제로 음수나 아주 큰 양수를 보내는 행위 등을 막기 위함이다.

그래서 enum에 대해 허용된 값만을 넘겼는지는 체크하기 위한 다음의 매크로를 만들었다.

// 2개의 field를 가지는 enum 타입에 대한 값 체크
#define CHECK_INVALID_ENUM_X2(source, case1, case2) \
switch (source) \
{ \
case case1: \
case case2: \
break; \
default: \
return false; \
}

// 3개의 field를 가지는 enum 타입에 대한 값 체크
#define CHECK_INVALID_ENUM_X3(source, case1, case2, case3) \
switch (source) \
{ \
case case1: \
case case2: \
case case3: \
break; \
default: \
return false; \
}

2개 또는 3개의 값만 가지는 emul에 대해 주어진 파라미터의 값이 허용된 값 안에 있는지를 알아 보고, 그렇지 않을 때는 false를 리턴하게 되어 있다. (실제로는 last error나 error log 등을 세팅하기 때문에 더 복잡하지만...)

사실 이 macro는 특별한 문제가 없다. 다만 앞으로 이야기할 아주 특수한 경우를 빼고 말이다.

문제의 상황을 만들어 보기 위해 간단한 enum 2개와 API를 만들어 보았다.

enum EA { EA_1, EA_2 };
enum EB { EB_1, EB_2, EB_3 };

bool SetSomething(EA a, EB b)
{
CHECK_INVALID_ENUM_X2(a, EA_1, EA_2);
CHECK_INVALID_ENUM_X3(b, EB_1, EB_2, EB_3);

// SetSomethingInternal(a, b);

return true;
}

유효한 값이 2개가 있는 EA라는 enum과 유효한 값이 3개가 있는 EB라는 enum이 있고, SetSomething()이라는 API는 제일 먼저 a와 b라는 이름으로 들어 온 파라미터의 유효성을 체크한다.

그리고 나는 이 매커니즘이 제대로 작동하는 가를 확인 하기 위해 다음과 같은 test case를 만들었다.

#include <stdio.h>

int main()
{
bool r1 = SetSomething(EA(-1), EB_1);
bool r2 = SetSomething(EA_1, EB(-1));

printf("r1 = %d, r2 = %d\n", r1, r2);
return 0;
}

아마도 쉽게 구할 수 있는 유명한 컴파일러에서는 대부분 r1 = 0, r2 = 0 이라는 결과를 돌려 받게 될 것이다. 하지만 조금 이름이 알려진 어떤 회사의 embedded compiler에서는 이 결과가 r1 = 1, r2 = 0 과 같이 나왔다. 다양한 컴파일러에서 작업해야 하는 일이 하나의 업이 된 지금, 이렇게 동작이 다른 컴파일러를 보는 것은 그리 이상한 일은 아니다. 다만 문제를 분석해서 아무런 문제가 없는 코드로 만드는 것이 조금 귀찮은 일일 뿐이다.


Test case가 특정 기기에서는 일부 fail이 나온다는 보고를 받고는  바로 디버깅 작업을 시작하였다. 그리고는 놀라운(?) 사실을 알아 내었다. 일부 컴파일러에서는 최적화 옵션에 따라서는 CHECK_INVALID_ENUM_X2() 매크로 자체가 아예 코드에서 빠진다는 것이다.

유효 값이 2개 밖에 없는 enum에 대해 그것을 switch 문으로 분기를 하면, 내부적으로 if - else 문으로 바꾸게 되고, 이때는 if 쪽이나 else 쪽이나 코드가 break 밖에 없기 때문에 코드를 삭제하는 만행(?)을 저지른 것이다. 다만, emul이 3개가 있는 경우는 if - else 로 바뀌지 않기 때문에 위와 같은 최적화의 대상이 아니라 제대로 동작하는 것이다.

이런 경우는 직접 부딪쳐 경우를 파악할 수 밖에는 없는 것이다. 그리고 최대한 에러 체크를 하기 위한 부분은 최적화에 안 걸리는 쪽의 코드를 짤 수 밖에 없다. 위의 경우도 MACRO를 if (!(A and B)) 문으로 치환하여 해결하였다.

Posted by 안영기

2011/06/04 19:50 2011/06/04 19:50
Response
0 Trackbacks , 1 Comments
RSS :
http://smgal.ismine.net/tc_191/blog1/rss/response/30

Trackback URL : 이 글에는 트랙백을 보낼 수 없습니다

« Previous : 1 : 2 : 3 : 4 : 5 : Next »

블로그 이미지

게임 개발을 기반으로 한, 잡다한 개발 기록 저장소

- 안영기

Notices

Archives

Authors

  1. 안영기

Recent Comments

Recent Trackbacks

Calendar

«   2019/12   »
1 2 3 4 5 6 7
8 9 10 11 12 13 14
15 16 17 18 19 20 21
22 23 24 25 26 27 28
29 30 31        

Site Stats

Total hits:
172940
Today:
6
Yesterday:
11