사용자 도구

사이트 도구


kb:cppsnippetsetc

차이

문서의 선택한 두 판 사이의 차이를 보여줍니다.

차이 보기로 링크

kb:cppsnippetsetc [2014/11/14 20:45] (현재)
줄 1: 줄 1:
 +{{INLINETOC}}
 +\\
 +
 +====== C++ / Snippets / Etc ======
 +...
 +
 +
 +====== 간단한 컴파일 타임 어서트 ======
 +:!: Visual Studio 2010 부터인가,​ static_assert가 기본으로 들어가면서 별로 쓸모
 +없어졌다. 그냥 참고로 남겨둔다.
 +
 +<code cpp>
 +#if defined (NDEBUG)
 +    #define StaticAssert(expr) ((void)0)
 +#else
 +    template <bool T> struct ASSERT_STRUCT {};
 +    struct ASSERT_STRUCT<​true>​ { static void static_assertion_failed() {} };
 +    #define StaticAssert(expr) { const bool __b = (expr) ? true : false; ASSERT_STRUCT<​__b>::​static_assertion_failed();​ }
 +#endif
 +</​code>​
 +
 +VisualCpp 8.0 경우에는 템플릿 특수화 방식이 약간 변경되었다.
 +
 +<code cpp>
 +template <bool T> struct ASSERT_STRUCT {};
 +template<>​ struct ASSERT_STRUCT<​true>​ { static void static_assertion_failed() {} };
 +</​code>​
 +
 +====== numeric_limits<​float>::​min != numeric_limits<​int>::​min ======
 +numeric_limits<​T>::​min(),​ max() 함수가 반환하는 값은 정수형일 때와 실수형일 때의 의미가 틀리다.
 +
 +  * std::​numeric_limits<​int>::​min() = -2147483648
 +  * std::​numeric_limits<​int>::​max() = 2147483647
 +  * std::​numeric_limits<​float>::​min() = 1.175494E-38
 +  * std::​numeric_limits<​float>::​max() = 3.402823E+38
 +
 +즉 실수형의 최소값은 음수 쪽으로의 최대값이 아니라, 실수형으로 표현할 수 있는 값 중에서 0에 가장 가까운 값이다. 이는 보통 사용하는 FLT_MIN, FLT_MAX 값의 경우에도 마찬가지다. 사실 저넘이 리턴하는 값이 FLT_MIN, FLT_MAX 값이니... ​
 +
 +어쨌든 정수형과 비슷한 의미로 min 값을 찾으려 했다면, std::​numeric_limits<​float>::​min()이 아니라, **-std::​numeric_limits<​float>::​max()**를 호출해야한다.
 +
 +
 +====== 생성자 및 소멸자 내부에서는 가상 함수가 제대로 호출되지 않는다 ======
 +vtable이 완전히 설정된 상태가 아니기 때문이다. 나쁜 프로그래밍 방법이므로 다른 방법을 사용하는 것이 좋다.
 +
 +**샘플**
 +<code cpp>
 +#include <​iostream>​
 +using namespace std;
 +
 +class base
 +{
 +public:
 +    base() { function(); }
 +    virtual void function() { cout << "​i'​m base" << endl; }
 +};
 +
 +class derived : public base
 +{
 +public:
 +    derived() { }
 +    virtual void function() { cout << "​i'​m derived"​ << endl; }
 +};
 +
 +int main()
 +{
 +    derived d;
 +    return 0;
 +}
 +</​code>​
 +
 +**출력**
 +<​code>​
 +i'm base
 +</​code> ​
 +
 +하지만 최종 클래스가 어떤 클래스냐에 따라 베이스 클래스의 동작이 달라져야 하는 경우가 있다면 -- 3D 엔진 같은 곳에서 리소스 초기화와 관련해서 이런 일이 꽤 있는 모양 -- 아래와 같은 모양으로 하면 된다. ​
 +
 +<code cpp>
 +#include <​stdio.h>​
 +
 +class base
 +{
 +public: ​
 +    base() { initialize();​ };
 +
 +protected: ​
 +    virtual void virtual_function() ​
 +    { 
 +        printf("​callling base class virtual function\n"​);  ​
 +    };
 +
 +    base(int i) {}; 
 +
 +    void initialize(){ virtual_function();​ init_more();​ }; 
 +    void init_more() { printf("​some more base specific work here\n"​);​ }; 
 +}; 
 +
 +class derived : public base 
 +
 +public: ​
 +    derived() : base(1) { initialize();​ }; 
 +
 +protected: ​
 +    virtual void virtual_function() ​
 +    { 
 +        printf("​calling derived class virtual function\n"​); ​
 +    };
 +
 +    void initialize() { base::​initialize();​ init_more();​ }; 
 +    void init_more(){ printf("​some more derived specific work here\n"​);​ }; 
 +}; 
 +
 +int main()
 +{
 +    base abc; 
 +    printf("​=========\n"​); ​
 +    derived def;
 +    return 0;
 +}
 +</​code>​
 +
 +<​code>​
 +callling base class virtual function
 +some more base specific work here
 +=========
 +calling derived class virtual function
 +some more base specific work here
 +some more derived specific work here
 +</​code>​
 +
 +====== static_cast vs reinterpret_cast ======
 +  * static_cast와 reinterpret_cast의 모호성에 관한 기술
 +    * [[http://​msdn.microsoft.com/​library/​en-us/​dndeepc/​html/​deep05182000.asp | Operation: static_cast]]
 +    * [[http://​msdn.microsoft.com/​library/​en-us/​dndeepc/​html/​deep06012000.asp | Operation: reinterpret_cast]]
 +
 +====== 배열 안의 아이템 갯수 알아내기 ======
 +  * _countof, ARRAYSIZE
 +  * [[http://​blogs.msdn.com/​the1/​archive/​2004/​05/​07/​128242.aspx | How Would You Get the Count of an Array in C++?]]
 +
 +
 +====== 빠른 atan2 ======
 +뭔가 난잡한데... 좀 정리해야할 듯.
 +<code cpp>
 +#include <​math.h>​
 +#include <​assert.h>​
 +
 +#define ASSERTS_ENABLED 1
 +
 +#ifndef PI
 +    #define PI 3.14159265358979323846
 +#endif
 +
 +static inline float interp( float r, float a, float b )
 +{
 +    return r*(b-a) + a;
 +}
 +
 +enum {NUM_ATAN_ENTRIES ​ =1000};
 +
 +static float pATanTBL[NUM_ATAN_ENTRIES+1];​
 +
 +static void createTrigTBLS(void)
 +{
 +    unsigned int i;
 +
 +    for(i=0; i < NUM_ATAN_ENTRIES+1;​ i++)
 +    {
 +        float y = 10.f;
 +        float x = y / i * NUM_ATAN_ENTRIES;​
 +        pATanTBL[i] = (float)atan2(y,​ x);
 +    }
 +}
 +
 +inline float CheckReturnA(double Angle, double x) 
 +{
 +#ifdef ASSERTS_ENABLED
 +    static double maxerr = 0;
 +    double err = ::fabs( Angle - x );
 +    if ( err>​maxerr ) maxerr = err;
 +#endif
 +/*
 +const double ERROR_VALUE = 1e-4;
 +    if (Angle < (x) - ERROR_VALUE || Angle > (x) + ERROR_VALUE)
 +        throw &​quot;​Incompatible values found at CheckReturn()&​quot;; ​
 +    return float(x);
 +*/
 +    return x;
 +}
 +
 +#define CheckReturn(x) { CheckReturnA(Angle,​(x));​ return float(x); }
 +
 +static double calcAngle( float x, float y )
 +{
 +    float di = (y/​x)*NUM_ATAN_ENTRIES;​
 +    unsigned int i = int( di );
 +    if ( i>​=NUM_ATAN_ENTRIES )
 +        return ::atan2( y, x );
 +    return interp( di-i, pATanTBL[i],​ pATanTBL[i+1] );
 +}
 +
 +float MMATan2(float y, float x)
 +{
 +#if ASSERTS_ENABLED
 +    float Angle = (float)atan2(y,​x);​
 +#endif
 +
 +    if(y == 0.f) // the line is horizontal
 +    {
 +        if( x > 0.f) // towards the right
 +        {
 +            // the angle is 0
 +            CheckReturn(0.f);​
 +        }               
 +        // toward the left
 +        //​CheckReturn(PI);​
 +        return (PI);
 +    }
 +
 +    // we now know that y is not 0 check x
 +    if(x == 0.f) // the line is vertical
 +    {       
 +        if( y > 0.f) 
 +        {
 +            CheckReturn(PI/​2.f);​
 +        }
 +        CheckReturn(-PI/​2.f);​
 +    }
 +
 +    // from here on we know that niether x nor y is 0
 +    if( x > 0.f) 
 +    {
 +        // we are in quadrant 1 or 4
 +        if (y > 0.f) 
 +        {
 +            // we are in quadrant 1
 +            // now figure out which side of the 45 degree line
 +            if(x > y) 
 +            {
 +                CheckReturn(calcAngle(x,​y));​
 +            }
 +            CheckReturn((PI/​2.f) - calcAngle(y,​x));​
 +        }
 +        // we are in quadrant 4
 +        y = -y;
 +        // now figure out which side of the 45 degree line
 +        if( x > y) 
 +        {
 +            CheckReturn(-calcAngle(x,​y));​
 +        }
 +        CheckReturn(-(PI/​2.f) + calcAngle(y,​x));​
 +    }
 +
 +    // we are in quadrant 2 or 3
 +    x = -x; // flip x so we can use it as a positive
 +    if ( y > 0) 
 +    {
 +        // we are in quadrant 2
 +        // now figure out which side of the 45 degree line
 +        if ( x > y) 
 +        {
 +            CheckReturn(PI - calcAngle(x,​y));​
 +        }
 +        CheckReturn(PI/​2.f + calcAngle(y,​x));​
 +    }
 +    // we are in quadrant 3
 +    y = -y; // flip y so we can use it as a positve
 +    ​
 +    // now figure out which side of the 45 degree line
 +    if( x > y) 
 +    {
 +        CheckReturn(-PI + calcAngle(x,​y));​
 +    }
 +
 +    CheckReturn(-(PI/​2.f) - calcAngle(y,​x));​
 +}
 +</​code>​
 +
 +====== 빠른 sqrt ======
 +from [[http://​www.gamedev.net/​community/​forums/​topic.asp?​topic_id=139956]]
 +
 +약간 정확성이 떨어지지만 빠른 sqrt 함수
 +<code cpp>
 +float InvSqrt(float x)
 +{
 +    float xhalf = 0.5f*x;
 +    int i = *(int*)&​x;​
 +    i = 0x5f3759df - (i >> 1);
 +    x = *(float*)&​i;​
 +    x = x*(1.5f - xhalf*x*x);
 +    return x;
 +}
 +
 +float FastSqrt(float x) { return 1.0f / InvSqrt(x); }
 +</​code>​
 +
 +====== 표준 입출력 스트림 리다이렉션하기 ======
 +<code cpp>
 +FILE *fpin, *fpout, *fperr;
 +bool in_redirected,​ out_redirected,​ err_redirected;​
 +
 +inredirected = outredirected = errredirected = false;
 +
 +if ((fpin = freopen("​stdin.txt","​rt",​stdin)) != NULL)
 +    in_redirected = true;
 +
 +if ((fpout = freopen("​stout.txt","​wt",​stdout)) != NULL)
 +    out_redirected = true;
 +
 +if ((fperr = freopen("​stderr.txt","​wt",​stderr)) != NULL)
 +    err_redirected = true;
 +
 +// do stuff with console redirected to files
 +
 +if (in_redirected) freopen("​CON","​rt",​stdin);​
 +if (out_redirected) freopen("​CON","​wt",​stdout);​
 +if (err_redirected) freopen("​CON","​wt",​stderr);​
 +</​code>​
 +
 +====== 미확정 매개 변수를 다른 함수의 인자로 다시 넘기기 ======
 +미확정 매개 변수(variable-length argument list)는 printf 류의 함수에서 "​..."​ 형식으로 쓰이는 변수들을 말한다. 가끔씩 이 변수들을 다른 함수의 인자로 넘겨야할 때가 있는데, 원래 함수 앞에 v가 붙은 버전(printf -> vprintf, sprintf -> vsprintf)을 이용하면 해결할 수 있다. ​
 +<code cpp>
 +#include <​stdio.h>​
 +#include <​stdarg.h>​
 +#include <​conio.h>​
 +
 +void inner(const char* fmt, va_list args)
 +{
 +    vfprintf(stdout,​ fmt, args);
 +    vfprintf(stderr,​ fmt, args);
 +}
 +
 +void outer(const char* fmt, ...)
 +{
 +    va_list args;
 +    va_start(args,​ fmt);
 +    inner(fmt, args);
 +    va_end(args);​
 +}
 +
 +int main()
 +{
 +    outer("​%d%s\n",​ 999, "​gogogo"​);​
 +    _getch();
 +    return 0;
 +}
 +</​code>​
 +스택 정리 등 더 알아두면 좋은 내용이 있는데, 이에 관한 것은 [[http://​www.eskimo.com/​~scs/​cclass/​int/​sx11c.html | Special Issues with Varargs Functions]]를 참고하시라.
 +
 +====== 미확정 매개 변수 처리 ======
 +printf 류의 함수 만들기 샘플 소스. 딱히 이런 함수를 만들 일은 없을 것 같다만 혹시나 싶어 기록해 둔다. Visual C++ 8.0 용으로 만든 소스이며,​ 다른 컴파일러에서 컴파일하기 위해서는 xxx_s 류의 함수를 고쳐줘야한다.
 +<code cpp>
 +void my_sprintf(char* output, size_t length, char* fmt, ...) 
 +
 +    va_list argptr;
 +
 +    int precision ​ = 10;  // 유효 자릿수는 기본 10자리
 +    int decimal ​   = 0;   // 소숫점 위치
 +    int nagative ​  = 0;   // 음수 여부
 +    char temp[256] = {0}; // 임시 버퍼
 +
 +    va_start(argptr,​ fmt);
 +    for (char* cur=fmt; *cur; ++cur)
 +    {
 +        memset(temp,​ 0, sizeof(temp));​
 +        if (*cur != '​%'​)
 +        {
 +            sprintf_s(temp,​ sizeof(temp),​ "​%c",​ *cur);
 +            strcat_s(output,​ length, temp);
 +            continue;
 +        }
 +
 +        switch (*(++cur))
 +        {
 +        case '​d':​
 +            _itoa_s(va_arg(argptr,​ long), temp, sizeof(temp),​ 10);
 +            strcat_s(output,​ length, temp);
 +            break;
 +        case '​f':​
 +            _ecvt_s(temp,​ sizeof(temp),​ va_arg(argptr,​ double), precision, &​decimal,​ &​nagative);​
 +            memmove(temp + decimal + 1, temp + decimal, strlen(temp) - decimal + 1);
 +            temp[decimal] = '​.';​
 +            if (nagative)
 +            {
 +                memmove(temp + 1, temp, strlen(temp) + 1);
 +                temp[0] = '​-';​
 +            }
 +            strcat_s(output,​ length, temp);
 +            break;
 +        case '​s':​
 +            strcat_s(output,​ length, va_arg(argptr,​ char*));
 +            break;
 +        default:
 +            strcat_s(output,​ length, cur);
 +            break;
 +        }
 +    }
 +    va_end(argptr);​
 +}
 +</​code>​
 +
 +====== 파일 로그 함수 ======
 +나름대로 여러번 고쳐서 만든 파일 로그 함수. 매번 파일을 열기 때문에, 대량으로 쓰면 느리다.
 +<code cpp>
 +/// \brief 파일에다 로그하기
 +/// \param szFilename 로그를 출력하고자 하는 파일 이름. NULL일 경우,
 +/// 실행 파일의 이름에다 "​.log"​를 붙여서 출력 파일로 사용한다.
 +/// \param fmt 로그 포맷
 +/// \param ... 로그 내용
 +void filelog(const char* szFilename, char* fmt, ...)
 +{
 +    ofstream file;
 +
 +    if (szFilename != NULL) 
 +    { 
 +        file.open(szFilename,​ ios::out | ios::​app); ​
 +    }
 +    else
 +    {
 +        char szModulePathname[_MAX_PATH*2] = {0,};
 +        ::​GetModuleFileName(NULL,​ szModulePathname,​ _MAX_PATH);
 +        ::​strcat(szModulePathname,​ "​.log"​);​
 +        file.open(szModulePathname,​ ios::out | ios::app);
 +    }
 +
 +    if (file.is_open())
 +    {
 +        time_t now = time(0);
 +        file << ctime(&​now) << " : ";
 +
 +        va_list valist;
 +        va_start(valist,​ fmt);
 +
 +        char stack_buf[512] = {0,};
 +        int nchars = _vsnprintf(stack_buf,​ sizeof(stack_buf)-1,​ fmt, valist);
 +        if (0 < nchars || nchars < sizeof(stack_buf))
 +        {
 +            stack_buf[sizeof(stack_buf)-1] = 0;
 +            file << stack_buf << endl;
 +        }
 +        else
 +        {
 +            int buflen = sizeof(stack_buf);​
 +            char* heap_buf = NULL;
 +
 +            do {
 +                buflen = buflen * 2;
 +                heap_buf = reinterpret_cast<​char*>​(::​realloc(heap_buf,​ buflen));
 +                memset(heap_buf,​ 0, buflen);
 +                nchars = _vsnprintf(heap_buf,​ buflen, fmt, valist);
 +            } while (nchars < 0 || nchars >= buflen);
 +
 +            heap_buf[buflen-1] = 0;
 +            file << heap_buf << endl;
 +            ::​free(heap_buf);​
 +        }
 +
 +        va_end(valist);​
 +    }
 +}
 +</​code>​
 +
 +====== 임시 변수 필요 없는 치환 매크로 ======
 +<code cpp>
 +#define SWAP(A,​B) ​ { (A) ^= (B) ^= (A) ^= (B); }
 +</​code>​
 +보다시피 XOR 연산을 이용하기 때문에, 숫자 타입(int, unsigned int, float 등) 변수에만 적용 가능하다. 차라리 std::​swap()을 쓰는 것이 나을 수도...
 +
 +
 +====== 소수 체크하기 ======
 +<code cpp>
 +bool IsPrimeNumber(int n)
 +{
 +    if (n == 2) return true;
 +    if (n == 1 || n % 2 == 0) return false;
 +
 +    int limit = n / 2;
 +    for (int k = 3; k <= limit; k += 2)
 +        if (n % k == 0)
 +            return false;
 +
 +    return true;
 +}
 +</​code>​
 +
 +====== 2의 승수 체크하기 & 구하기 ======
 +아래의 함수는 주어진 인수가 2^n 형태, 즉 2의 승수일 때 true를 반환한다.
 +<code cpp>
 +bool IsPowerOfTwo(int x)
 +{
 +    return (!(x & (x-1)));
 +}
 +</​code>​
 +
 +아래의 함수는 주어진 수와 같거나 더 큰 2의 승수를 반환한다.
 +<code cpp>
 +unsigned int GetNearestPowerOfTwo(unsigned int value)
 +{
 +    const unsigned MANTISSA_BIT_DEPTH = 23;
 +    const unsigned MANTISSA_MASK = (1 << MANTISSA_BIT_DEPTH) - 1;
 +
 +    (*(float*)&​value) = (float)value;​
 +
 +    value  += MANTISSA_MASK;​
 +    value >>= 23;
 +    value  -= 127;
 +
 +    return 1 << value;
 +}
 +</​code>​
 +
 +
 +====== 최대 N가지의 상태를 표현하기 위해 필요한 비트수 구하기 ======
 +<code cpp>
 +/// \brief 0 ~ N-1 까지의 상태를 표현하기 위해 몇 비트가 필요한가?​
 +template <size_t N>
 +struct NumberOfBits
 +{
 +    template <size_t X>
 +    struct Internal
 +    {
 +        enum
 +        {
 +            Prev = X >> 1, // 왠지 모르지만 컴파일이 안 되서 따로 분리
 +            Value = X < 2 ? X : Internal<​Prev>::​Value + 1
 +        };
 +    };
 +
 +    template <> struct Internal<​0>​ { enum { Value = 0 }; };
 +
 +    enum
 +    {
 +        Value = Internal<​N - 1>::​Value // 이 값을 이용하면 됨
 +    };
 +};
 +</​code>​
 +
 +----
 +  * see also [[CppSnippets|C++ Snippets]]
  
kb/cppsnippetsetc.txt · 마지막으로 수정됨: 2014/11/14 20:45 (바깥 편집)