…
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<T>::min(), max() 함수가 반환하는 값은 정수형일 때와 실수형일 때의 의미가 틀리다.
즉 실수형의 최소값은 음수 쪽으로의 최대값이 아니라, 실수형으로 표현할 수 있는 값 중에서 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
뭔가 난잡한데… 좀 정리해야할 듯.
#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 "Incompatible values found at CheckReturn()"; 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)); }
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^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; }
/// \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 // 이 값을 이용하면 됨 }; };