'c++'에 해당되는 글 4건

  1. 2009.05.29 베지어 커브 (Bezier curve) 3
  2. 2007.11.26 enum 의 Type과 표현 범위 3
  3. 2007.11.13 C++ enum
  4. 2007.10.31 c++ type casting - const_cast 2

베지어 커브 (Bézier curve)

베지어 커브는 1962년 프랑스 엔지니어인 Pierre Bézier에 의해 만들어 졌는데,
이 커브는 그가 자동차 차체를 디자인 하기 위한 목적으로 사용한 커브였다.

이 커브는 몇 개의 조절점(Control points)들로 구성되며,
조절점들은 a parametric mathematical function에 의해 커브 모양을 결정한다.
커브의 차수는 "조절점의 개수-1"차로 결정되는데, 다음 공식에 의해
커브를 구성하는 점들의 위치가 결정된다

사용자 삽입 이미지








조절점 중 처음과 끝 점은 곡선을 지나가고, 나머지는 어느 곡선과 만나지 않는다.
곡선은 조절점으로 이루어진 다각형을 벗어나지 않는다.
조절점이 하나 뿐이면 그냥 점이다.
조절점이 두 개이면 직선이다.

조절점이 3 개인 경우는 다음과 같이 2차식이 된다.
B(t) = P0 * ( 1 - t ) 2 + P1 * 2 * t ( 1 - t ) + P2 t2

조절점이 4 개인 경우,
B(t) = P0 * ( 1 - t )3 + P1 * 3 * t * ( 1 - t )2 + P2 * 3 * t2 * ( 1 - t ) + P3 * t3


references
http://www.moshplant.com/direct-or/bezier/
http://en.wikipedia.org/wiki/Bernstein_blending_function
http://cagd.cs.byu.edu/~557/
 

:
http://baboc.tistory.com/49

위 글에서 enum 이 #define 을 대신하여 상수 정의 용도로 사용할 수 있다는 것을 썼다.
물론 #define 도 미리 정해진 타입이 없긴 하지만
사람들은 암묵적으로 4바이트 정수 타입으로 인식하고 있는 듯 하다.

enum 으로 선언된 상수들은 일반적으로 signed int 형을 갖게 되는데,
엄밀히 말하자면, 4바이트 정수형 이하의 타입 중에 항당 된 값을 가장 optimal 하게 표현하는
자료형을 갖게 된다.
다만, 4바이트 정수 이하 타입들의 기본 연산자가 모두 일반 int 형 연산자를 사용하므로
그것이 그렇게 중요한 사항은 아니다.

문제는 다음과 같이 signed int 의 범위를 벗어난 값을 할당하려고 할 때이다.
enum{
    TWO_GIGA_BYTE = 2*1024*1024*1024,
}

위와 같이 선언하면 의도한 대로 값이 할당되지 않는다.
다음과 같이 캐스팅을 하면,
#ifdef _WIN32
typedef unsigned __int64  uint64;
#elif defined(__linux__)
typedef unsigned long long uint64;
#endif

enum{
    TWO_GIGA_BYTE = uint64(2)*1024*1024*1024,
}

타입이 변환되는 것처럼 보인다.
나의 테스트 결과 linux 에서는 아무런 문제 없이 의도한 대로 값이 할당된다.
하지만 windows 위 VC++ 6.0, VC++ 7.0 에서는 위와 같이 하여도 제대로 된 값이 할당 되지 않는다.
온갖 테스트를 다 해 보았지만, 결국 성공하지 못 했다.
아마도 이쪽 컴파일러가 64비트 정수의 타입 변환에 대해 별 다른 처리를 해 주지 않은 게 아닐까 생각된다.
결국, signed int 범위를 벗어난 범위에 대해서는 linux 환경에서만 쓸 수 있다는 것이다.
나는 linux 와 win32 환경에서 모두 돌아가는 코드를 작성해야 하므로,
이 경우에는 결국 다른 선택 없이 const 변수를 사용해야만 한다.

아 ... -_-;;
그동안 enum 으로 정의한 상수를 대부분 사용하여 왔는데,
이렇게 되면 코드의 일관성이 떨어지는 결과를 낳게 된다.
물론 const 정수를 사용한 코드도 많지만 ...
일부는 시즈모드 일부는 퉁퉁퉁퉁퉁 !!!

====

<추가 테스트>

typedef unsigned int  uint32;

enum{
    TWO_GIGA_BYTE = uint32(2)*1024*1024*1024,
}

void function()
{
    uint32 i = TWO_GIGA_BYTE;
    print(i);
}

위와 같이 32비트 unsigned int 타입으로 선언하고 값을 받으면 잘 된다.
결국 win32 환경에서 32비트 타입까지는 잘 지원한다는 얘기 !
:

C++ enum

프로그래밍 2007. 11. 13. 22:02 |

enum 은 정수형 type 정의와 상수 정의에 사용된다.
보통 unnamed enum 을 사용하여 #define 을 대신하여 상수 정의에 사용되는데
예를 들면,
// define
#define     TYPE_A      0
#define     TYPE_B      1
#define     TYPE_C      2
#define     TYPE_D      3

// unnamed enum
enum {
    TYPE_A,
    TYPE_B,
    TYPE_C,
    TYPE_D,
};

얼핏 위 두 가지는 비슷해 보이긴 하지만 조금(혹은 많이?) 다르다.
물론 당연히 C++ 에서는 후자를 권장한다.
(reference : Effective C++, Scott Meyers)

내가 일하는 곳에서도 모든 상수 정의는 enum을 사용하고 있다.
가장 큰 장점은 디버깅 시 symbol name으로 표시된다는 점이다.
(이건 디버깅을 해 보면 쉽게 알 수 있다)

또한 enum 은 scope 를 가진다.
중괄호 '{}' 안에서도 scope를 가지며,
클래스 내에서 private, public 등 access modifier 에 대해서도 영향을 받는다.

또 이건 장점이자 동시에 단점이 될 수도 있는데,
별도 값을 assign 하지 않으면 값이 알아서 1씩 증가한다는 점이다.
편리함에서 장점이 될 수 있지만, 의도치 않게 중간에 엉뚱한 값이 끼어드는 경우
상수 값 자체가 바뀌어 버리므로 예상치 못한 오류를 일으킬 수도 있다.

또한 중요한 단점인데,
명확하게 type 이 정의되지 않는다는 점이다.

예를 들면,
enum{
    TWO_GIGA = 2*1024*1024*1024,       // 2GB ?
};

이와 같이 선언된 경우,
원래 의도는 TWO_GIGA 를 2GB 대신으로 사용하고자 하였겠지만,
실제로는 일반 4바이트 정수 값으로 0x800000 값이 들어가 있다.
즉, signed int 타입으로 정의되고 그 값은 -2147483648 이다.

제대로 하려면 다음과 같이 하면 된다.
enum{
    TWO_GIGA = 2llu*1024*1024*1024,       // 2GB !
};

명시적으로 long long 타입이라고 알려주는 것이다.
위의 경우와 마찬가지로 다음과 같이 쓴다면,
enum{
    FOUR_GIGA = 4*1024*1024*1024,       // 4GB ?
};

실제로 값은 0 이다.

이런 경우와 같이 명확하게 type 을 표시해 주면 좋은 경우는
enum 대신에 const 변수를 사용하는 것이 더 좋다.
:
내가 학생 때 프로그래밍을 배우면서 C++ 스타일의 캐스팅을 배운 기억이 나지 않는다.
물론 책에서 몇가지 예제가 있었기 때문에 이런 것들이 있다는 것은 알고 있었지만
실제로 내가 코딩을 하면서는 한 번도 사용해 본 적이 없다.

그러나 실전에서 많은 코드을 보면서
C++ 스타일의 캐스팅이 매우 유용하게 사용된다는 사실을 알게 되었다.
그리고 근래에는 나도 몇 프로젝트를 진행하면서 이 유용한 문법을 사용하면서
진작에 왜 이걸 몰랐을까라고 생각이 들었다.
혹시라도 이 내용을 잘 모르는 사람이 이걸 보고 도움이 되길 바란다.

C++ 스타일 캐스팅에서는 다음 4가지가 있다.
const_cast
dynamic_cast
reinterpret_cast
static_cast



우선은 const_cast 부터 ...

이것은 변수의 상수성을 제거한다. 즉, const 로 선언한 변수에서 const 를 무효화 한다는 의미이다.
그리고 중요한 것은 이 캐스팅이 포인터 혹은 레퍼런스 타입에만 사용하여야 한다는 것이다.

예를 들어, 만약 char * 를 파라미터로 받는 라이브러리가 있는데,
로컬 변수가 const char* 로 선언되어 있다면
const_cast 를 사용해서 별도의 처리 없이 함수를 호출 할 수 있다.

void some_function(char* str)
{
     // what this to do ...
}

void caller()
{
    const char* const_variable;
    some_fuction(const_variable); // <- error !!
    some_fuction(const_cast<char *>const_variable); // <- no problem : )
}

만약 const_cast 가 없다면 함수를 호출하기 위해서 별도 변수를 만들고,
그 변수에 값을 받아서 함수 파라미터로 넘겨야 하는 번거로움이 따른다.

다만, 원래 값이 const 로 선언되었다면, 그 의도에 따라 값을 변경하지 않아야 한다.
문법적인 규칙을 따르기 위해 변수의 상수성을 제거 하였지만,
const 변수의 값을 바꾸려는 의도로 이 캐스트를 사용한다면 부작용이 생길 수 있으니
되도록 사용하지 말아야 한다.
혹자는 const_cast 자체를 사용하지 않는 게 좋다고 하긴 하지만,
그래도 편하라고 있는 건데 잘 쓰면 좋지 않나 생각한다.
학생 시절 그렇게 사용하지 말라던 goto 문도 쓸데가 있으니 ... 그것 보단 낫다고 개인적으로 판단된다 -_-;

논리적으로 바르지 않은 const_cast 사용에 관하여. (아래 블로그 참조)
http://blog.naver.com/arcyze?Redirect=Log&logNo=60041966927

#include <iostream.h>

int main()
{
    const int a = 10;
    int *b = const_cast<int *>(&a);
    *b = 100;

    cout << a << "\t" << &a << endl;
    cout << *b << "\t" << b << endl;
   
    return 0;
}

// 결과
10                  0x0013FF7C
100                0x0013FF7C

실제로 VC++ 6.0, gcc 4.0.4 에서 컴파일 해서 실행하면 위와 같이 희한한 결과가 나온다.
아마도 컴파일러가 위와 같은 문장을 비정상적 코드로 판단하고
내부적으로 자체처리 하고 있는 게 아닐까 생각된다.
reinterpret_cast 에서도 이와 비슷한 기능이 있는 것 같다.
(이에 대해서는 나중에 써 보겠다.)

* const_cast 는 volatile 로 선언된 변수에 대해서도 비슷하게 무효화 해 주는 역할을 한다.
다만, volatile 는 잘 사용하지 않으므로 여기서는 생략한다.
(사실 나도 volatile 는 써 본 적이 없다-_-)

참조 : Effective C++ 3rd Edition / by Scott Meyers
: