사용자 도구

사이트 도구


kb:cppsnippetsetc


C++ / Snippets / Etc

간단한 컴파일 타임 어서트

:!: Visual Studio 2010 부터인가, static_assert가 기본으로 들어가면서 별로 쓸모 없어졌다. 그냥 참고로 남겨둔다.

#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

VisualCpp 8.0 경우에는 템플릿 특수화 방식이 약간 변경되었다.

template <bool T> struct ASSERT_STRUCT {};
template<> struct ASSERT_STRUCT<true> { static void static_assertion_failed() {} };

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이 완전히 설정된 상태가 아니기 때문이다. 나쁜 프로그래밍 방법이므로 다른 방법을 사용하는 것이 좋다.

샘플

#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;
}

출력

i'm base

하지만 최종 클래스가 어떤 클래스냐에 따라 베이스 클래스의 동작이 달라져야 하는 경우가 있다면 – 3D 엔진 같은 곳에서 리소스 초기화와 관련해서 이런 일이 꽤 있는 모양 – 아래와 같은 모양으로 하면 된다.

#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;
}
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

static_cast vs reinterpret_cast

배열 안의 아이템 갯수 알아내기

빠른 atan2

뭔가 난잡한데… 좀 정리해야할 듯.

#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));
}

빠른 sqrt

from http://www.gamedev.net/community/forums/topic.asp?topic_id=139956

약간 정확성이 떨어지지만 빠른 sqrt 함수

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); }

표준 입출력 스트림 리다이렉션하기

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);

미확정 매개 변수를 다른 함수의 인자로 다시 넘기기

미확정 매개 변수(variable-length argument list)는 printf 류의 함수에서 ”…” 형식으로 쓰이는 변수들을 말한다. 가끔씩 이 변수들을 다른 함수의 인자로 넘겨야할 때가 있는데, 원래 함수 앞에 v가 붙은 버전(printf → vprintf, sprintf → vsprintf)을 이용하면 해결할 수 있다.

#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;
}

스택 정리 등 더 알아두면 좋은 내용이 있는데, 이에 관한 것은 Special Issues with Varargs Functions를 참고하시라.

미확정 매개 변수 처리

printf 류의 함수 만들기 샘플 소스. 딱히 이런 함수를 만들 일은 없을 것 같다만 혹시나 싶어 기록해 둔다. Visual C++ 8.0 용으로 만든 소스이며, 다른 컴파일러에서 컴파일하기 위해서는 xxx_s 류의 함수를 고쳐줘야한다.

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);
}

파일 로그 함수

나름대로 여러번 고쳐서 만든 파일 로그 함수. 매번 파일을 열기 때문에, 대량으로 쓰면 느리다.

/// \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);
    }
}

임시 변수 필요 없는 치환 매크로

#define SWAP(A,B)  { (A) ^= (B) ^= (A) ^= (B); }

보다시피 XOR 연산을 이용하기 때문에, 숫자 타입(int, unsigned int, float 등) 변수에만 적용 가능하다. 차라리 std::swap()을 쓰는 것이 나을 수도…

소수 체크하기

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;
}

2의 승수 체크하기 & 구하기

아래의 함수는 주어진 인수가 2^n 형태, 즉 2의 승수일 때 true를 반환한다.

bool IsPowerOfTwo(int x)
{
    return (!(x & (x-1)));
}

아래의 함수는 주어진 수와 같거나 더 큰 2의 승수를 반환한다.

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;
}

최대 N가지의 상태를 표현하기 위해 필요한 비트수 구하기

/// \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 // 이 값을 이용하면 됨
    };
};

kb/cppsnippetsetc.txt · 마지막으로 수정됨: 2014/11/14 20:45 (바깥 편집)