[C++] Date Class └ C/C++

목적 : javascript의 Date() 객체와 같은 녀석을 C++ 클래스로 만들어 보고 싶었음

이슈 : Month 값을 0 ~ 11 로 하지 않고 1 ~ 12 로 입력/출력 하도록 했음
       : Format 문자열의 경우, strftime() 의 그것을 사용함  msdn 이나 cpluslpus 참조

Source Code:

<CDate.h>

 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
32
33
34
35
36
37
38
39
40
41
42
43
44
45
#ifndef _DATE_H_
#define _DATE_H_

#include <time.h>

class CDate
{
private:
time_t m_nTime;
struct tm m_tm;

public:
CDate();
CDate( time_t time );
CDate( int year, int month, int day, int hour, int min, int sec );
CDate( const CDate& date );
~CDate();

public:
// Format String
int format( char* buf, int bufSize, const char* fmtString );

public:
int getMEndDay(); // Returns the end of day of the month (from 28-31)
int getMDay(); // Returns the day of the month (from 1-31)
int getWDay(); // Returns the day of the week (from 0-6)
int getFullYear(); // Returns the year (four digits)
int getHours(); // Returns the hour (from 0-23)
int getMinutes(); // Returns the minutes (from 0-59)
int getMonth(); // Returns the month (from 1-12)
int getSeconds(); // Returns the seconds (from 0-59)
time_t getTime(); // Returns the number of seconds since midnight Jan 1, 1970

void setDate( int year, int month, int day );
void setDate( int year, int month, int day, int hour, int min, int sec );
void setDay( int day ); // Sets the day of the month of a date object
void setFullYear( int year ); // Sets the year (four digits) of a date object
void setHours( int hour ); // Sets the hour of a date object
void setMinutes( int min ); // Set the minutes of a date object
void setMonth( int month ); // Sets the month of a date object
void setSeconds( int sec ); // Sets the seconds of a date object
void setTime( time_t time ); // Sets a date and time by adding or subtracting a specified number of milliseconds to/from midnight January 1, 1970
};

#endif

<CDate.cpp>


  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
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
#include "CDate.h"
#include <string.h>

inline static struct tm* _localtime( time_t* time, struct tm* _tm )
{
#ifdef _WIN32
localtime_s( _tm, time );
#else
localtime_r( time, _tm );
#endif
return _tm;
}

CDate::CDate()
{
m_nTime = time(NULL);
_localtime( &m_nTime, &m_tm );
}

CDate::CDate( time_t time )
{
m_nTime = time;
_localtime( &m_nTime, &m_tm );
}

CDate::CDate( int year, int month, int day, int hour, int min, int sec )
{
struct tm tmp_tm;

memset( &tmp_tm, 0, sizeof( tmp_tm ) );
tmp_tm.tm_year = year - 1900;
tmp_tm.tm_mon = month - 1;
tmp_tm.tm_mday = day;
tmp_tm.tm_hour = hour;
tmp_tm.tm_min = min;
tmp_tm.tm_sec = sec;

setTime( mktime( &tmp_tm ) );
}

CDate::CDate( const CDate& date )
{
m_nTime = date.m_nTime;
_localtime( &m_nTime, &m_tm );
}

CDate::~CDate()
{
}

int CDate::format( char* buf, int bufSize, const char* fmtString )
{
return strftime( buf, bufSize, fmtString, &m_tm );
}

int CDate::getMEndDay()
{
static unsigned char month_days[] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};

if( m_tm.tm_mon == 1 ) {
int year = m_tm.tm_year + 1900;
if( year%4==0 && ( year%100!=0 || year%400==0 ) ) {
return 29;
}
}

return (int)month_days[ m_tm.tm_mon ];
}

int CDate::getMDay()
{
return m_tm.tm_mday;
}

int CDate::getWDay()
{
return m_tm.tm_wday;
}

int CDate::getFullYear()
{
return m_tm.tm_year + 1900;
}

int CDate::getHours()
{
return m_tm.tm_hour;
}

int CDate::getMinutes()
{
return m_tm.tm_min;
}

int CDate::getMonth()
{
return m_tm.tm_mon + 1;
}

int CDate::getSeconds()
{
return m_tm.tm_sec;
}

time_t CDate::getTime()
{
return m_nTime;
}

void CDate::setDate( int year, int month, int day )
{
m_tm.tm_year = year - 1900;
m_tm.tm_mon = month - 1;
m_tm.tm_mday = day;
setTime( mktime( &m_tm ) );
}

void CDate::setDate( int year, int month, int day, int hour, int min, int sec )
{
m_tm.tm_year = year - 1900;
m_tm.tm_mon = month - 1;
m_tm.tm_mday = day;
m_tm.tm_hour = hour;
m_tm.tm_min = min;
m_tm.tm_sec = sec;
setTime( mktime( &m_tm ) );
}

void CDate::setDay( int day )
{
m_tm.tm_mday = day;
setTime( mktime( &m_tm ) );
}

void CDate::setFullYear( int year )
{
m_tm.tm_year = year - 1900;
setTime( mktime( &m_tm ) );
}

void CDate::setHours( int hour )
{
m_tm.tm_hour = hour;
setTime( mktime( &m_tm ) );
}

void CDate::setMinutes( int min )
{
m_tm.tm_min = min;
setTime( mktime( &m_tm ) );
}

void CDate::setMonth( int month )
{
m_tm.tm_mon = month - 1;
setTime( mktime( &m_tm ) );
}

void CDate::setSeconds( int sec )
{
m_tm.tm_sec = sec;
setTime( mktime( &m_tm ) );
}

void CDate::setTime( time_t time )
{
m_nTime = time;
_localtime( &m_nTime, &m_tm );
}



[WINAPI] C++ 용 WIN32 Thread 클래스 └ C/C++

MFC없이 Win32 API 만을 이용해 Thread 프로그래밍을 하던 중
자바나 C#의 Thread 클래스처럼 class화 해서 사용할 수 있으면 좋겠다고 생각해서
대충 생각나는대로 디자인을 해봤다
일단 간단한 Thread 프로그래밍에는 그럭저럭 만족스러운 모습을 보여주었다
  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
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
#ifndef _BASIC_THREAD_H_
#define _BASIC_THREAD_H_

#define WIN32_LEAN_AND_MEAN
#include <Windows.h>

/-========================================================================================================================
** File			: BasicThread.h
** Desc			: 기본적인 Thread Class
** Author		: Jin-Gyu Choi
** Create		: 2013.03.28
** Last Modify	: 2013.03.28
========================================================================================================================*-

class BasicThread
{
protected:
	HANDLE m_hThread;		// Window Thread Handle
	DWORD  m_nThreadID;		// Window Thread ID

private:
	BasicThread( const BasicThread& bt ) {}					// 해당 Thread의 복제를 막는다
	BasicThread& operator = ( const BasicThread& bt ) {}	// 해당 Thread의 복제를 막는다
	
	static DWORD WINAPI StaticThreadStart( LPVOID lpParam )	// 실제 Thread의 시작부분
	{
		BasicThread* pThread = (BasicThread*)lpParam;
		return pThread->Run();
	}

protected:
	virtual DWORD Run(void) = 0;	// Protected로 사용자가 직접 호출하는 것을 막음

public:
	BasicThread() : m_hThread( NULL ), m_nThreadID( 0 )
	{
	}
	
	virtual ~BasicThread()
	{
		if( m_hThread ) CloseHandle( m_hThread );
	}

public:
	//----------------------------------------------------------------
	// public functions
	//----------------------------------------------------------------
	bool Start()	// Thread 시작
	{
		if( m_hThread ) // 이미 Thread가 생성되어 있는 경우
		{
			if( WaitForSingleObject( m_hThread, 0 ) == WAIT_TIMEOUT ) {	// Thread Still Running
				return false;	// Start 거부
			}
			CloseHandle( m_hThread );
		}

		// Thread 생성
		m_hThread = CreateThread(
			NULL,			// default security attributes
			0,				// default stack size
			(LPTHREAD_START_ROUTINE)BasicThread::StaticThreadStart,
			this,			// thread function argument = this class
			0,				// default creation flags
			&m_nThreadID	// receive thread identifier
		);

		if( m_hThread != NULL ) return true;

		return false;
	}
	
	void Stop()	// Thread 강제 종료 - 비추!!
	{
		if( this->IsRunning() )	{
			// 강제 Terminate이므로 정상적인 자원 해제를 기대할 수 없음
			// 공유자원 등을 물고 있을 경우 deadlock 등의 위험이 있을 수 있음
			::TerminateThread( m_hThread, -1 );
		}

		if( m_hThread ){
			CloseHandle( m_hThread );
			m_hThread = NULL;
		}
	}

public:
	//----------------------------------------------------------------
	// inline functions
	//----------------------------------------------------------------

	// Getter
	inline HANDLE GetThreadHandle() { return m_hThread; }
	inline DWORD GetThreadID() { return m_nThreadID; }

	// Status
	inline bool IsRunning() 
	{
		if( m_hThread ){
			DWORD dwExitCode = 0;
			::GetExitCodeThread( m_hThread, &dwExitCode );
			if( dwExitCode == STILL_ACTIVE ) return true;
		}
		return false;
	}

	// Join - Wait For Thread Done
	inline void Join()
	{
		::WaitForSingleObject( m_hThread, INFINITE );
	}

	// Yeild - Yeild Execution to Another Thread
	inline BOOL Yeild()
	{
		return ::SwitchToThread();
	}

	// Sleep - Suspends the execution of the current thread until the time-out interval elapses
	inline void Sleep( DWORD dsMilliiseconds )
	{
		::Sleep( dsMilliiseconds );
	}

	// Suspend - Suspend Thread
	inline DWORD Suspend()
	{
		return ::SuspendThread( m_hThread );
	}

	// Resume - Resume Thread
	inline DWORD Resume()
	{
		return ::ResumeThread( m_hThread );
	}

};

#endif
BasicThread 클래스 그 자체로는 Run이 정의되어 있지 않기 때문에 그대로 사용하는 것은 안되고
실제로 사용하기 위해서는 BasicThread 클래스를 상속받아
Run() 함수를 구현해서 사용하면 된다
대략 사용한다면 이런 느낌???
물론 원한다면 맴버변수, static 변수 모두 사용 가능하다
 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
32
33
34
35
36
37
38
39
40
#include <stdio.h>
#include "BasicThread.h"

class TestThread : public BasicThread
{
protected:
	DWORD Run(void)
	{
		printf("Thread %d Start\n", GetThreadID());

		for( int i = 0; i < 5; i++ )
		{
			printf("Thread %d : Count %d\n", GetThreadID(), i);
			this->Sleep( 1000 );
		}

		printf("Thread %d End\n", GetThreadID());

		return 0;
	}
};

int main(void)
{
	TestThread* pThread[5];

	for( int i = 0 ; i < 5; i++ )
	{
		pThread[i] = new TestThread();
		pThread[i]->Start();
	}

	for( int i = 0 ; i < 5; i++ )
	{
		pThread[i]->Join();
		delete pThread[i];
	}

	return 0;
}

[C++] 함수 포인터를 static 맴버 변수로? └ C/C++

함수포인터가 단 한번 선언되는 경우가 아니면

모호성을 줄이기 위해서 typedef를 이용하는 것이 가장 좋은 방법이다

함수 포인터를 static 맴버 변수로 사용할 때도 마찬가지

class CSample
{
typedef void (*MYFP)();

static MYFP m_pFunc;
};

CSample::MYFP CSample::m_pFunc = NULL;

[C/C++] 어쩌다 저지를 수 있는 for문 스코프 런타임 오류 └ C/C++

대게의 경우 변수 이름을 아무 생각없이 막 쓰지 않는 이상 스코프에 의한 런타임 오류는 경험하기 힘들다

가장 많이 찾아볼 수 있는 스코프 오류는 for문을 사용할 때 이다

이 역시 컴파일러의 해석 차이에 의해서 생기는 스코프 오류로

이 이슈가 가장 많이 문제 시 되었던 시기는 VS 6.0 에서 최신 버전으로 넘어가면서 이다

int main()
{
for( int i = 0 ; i < 10 ; i++ ) {
// Do Something
}

printf("%d", i );
return 0;
}

i 의 스코프는 어디까지 유지 되는가?

VS 6.0을 쓰던 사람은 for문 이후 main문 끝까지 유지된다고 할 것이고

그 이외의 컴파일러를 쓰는 사람은 for문 내에서만 유효하다고 할 것이다

이 정도의 이슈가 그나마 현업에서 가장 자주 만날 수 있는 스코프 오류의 '주' 였다



오늘 이야기하고자 하는 내용도 for문과 관련이 있다

어떻게 보면 너무 당연한 내용이지만... 아차 하고 넘어갈 수 있는 부분이다

오늘 문제가 될 코드는 아래와 같다

int main()
{
int tmp = 0;
for( int i = 0, tmp = 0; i < 10 ; i++, tmp++ ) {
// Do Something
}

printf("%d\n", tmp );
return 0;
}

위의 코드의 결과는 무엇일까?

C로 할경우는 확실히 에러일테고, C++의 기준으로 생각해 보자

일단 본인 주변에서는 의외로 위의 문제에 한번에 정답을 말하는 이가 적었다

먼저 정답을 말하자면

VS 6.0에서는 에러
gcc와 최신버전의 VS ( 2008과 2010 만 일단 확인 ) 컴파일러의 경우 0 을 출력한다

10 이라고 생각한 사람들은 코드를 잘 살펴보아라

콤마(,) 때문에 착각할 수도 있는 문제다

for문을 사용할 때, 다중 초기화 및 다중 가감산 연산을 취할 경우

우리는 콤마(,) 를 사용하여 구분한다

위의 경우도 본래의 의도는

i를 생성 0으로 초기화 한 후, 이미 선언된 tmp 를 0으로 초기화하라고 생각하고 작성하기 쉽다

물론 i와 tmp가 for문 이전에 선언되어 있었다면 의도한 대로 될 것이다

하지만 선언문 뒤에 콤마가 붙어 버렸기 때문에 위의 코드는 다음과 같이 해석 된다

int main()
{
int tmp = 0;

{
int i, tmp;
for( i = 0, tmp = 0; i < 10 ; i++, tmp++ ) {
// Do Something
}
}

printf("%d\n", tmp );
return 0;
}

즉, for문 스코프 내에 tmp 변수를 한번 더 선언한게 되기 때문에

for문 내에서 tmp 변수를 아무리 바꿔봤자 for문 내의 로컬 tmp 변수를 바꾼 것일 뿐 

그 밖에 있는 원래 tmp에는 아무런 영향이 없다는 것이다

디버그 모드로 돌려보아도

VS의 Auto나 Watch 탭에서는 for문의 tmp가 잡혀서 값이 정상적으로 들어간 것 처럼 보이지만...

실제 Local 탭에는 tmp 변수가 바뀌지 않음을 확인할 수 있다

VS 6.0의 경우 for문 안과 밖이 같은 스코프로 취급하기 때문에 재정의 오류가 발생한다



위의 코드가 좀 억지스러운 상황을 만들기는 했지만, 실제로 현업에서 어쩌다 한번씩 발견되는 실수다

코드의 모호성을 줄이고, 해당 스코프 오류를 줄이기 위해서 

가능한 for문에 사용 할 변수의 경우 for문 밖에서 미리 선언하고 사용하는 것이 안전하다

[jsp] javascript encodeURI() 한글 인코딩 시 jsp URLDecoder에서 한글 깨짐현상 └ Flash & Web

javascript 에서 제공하는 encodeURI()와 encodeURIComponent() 함수는 기본적으로 UTF-8으로 인코딩을 합니다

이를 Query로 하여 jsp 페이지에 넘겨서

request.getParameter() 함수로 받고서 아무 의심없이 URLDecoder.decode() 함수를 사용했습니다

당연히 UTF-8으로 decode를 했지요

하지만 계속해서 한글이 깨져있습니다;;;

계속 원인을 찾던 중

tomcat이 Query를 미리 서버에 지정된 기본 문자셋으로 디코딩을 해버린다는 사실을 알았습니다

즉, request.getParameter()로 받은 결과가 인코딩 된 문자열이 아닌 이미 디코딩 된 문자열이었지요

즉, 기대한 값은 %EC%95%88%EB%85%95%ED%95%98%EC%84%B8%EC%9A%94 과 같은 모양의 문자열인데

이미 저 값은 URLDeocder.decode( 문자열, 서버 기본 문자셋 ) 함수로 한번 디코딩 된 결과 같이 나온다는 거에요

많은 경우 서버 기본 문자셋이 MS-949 등이기 때문에 UTF-8으로 인코딩 된 값을 잘못 디코딩한 것이죠

잘못 디코딩된 녀석을 다시 디코딩 해봤자 한글이 깨져있는 것은 당연한 것이지요...



1. 해결 방법으로는 서버 기본 문자셋을 UTF-8으로 바꿔버리는 방법과...



2. 데이터의 양이 늘어나지만 encodeURI() 또는 encodeURIComponent() 의 결과를 한번 더 인코딩 해버리는 방법도 있습니다

encodeURIComponent( encodeURIComponent( plainText ) );  // 이렇게요

그리고 jsp에서는 URLDecoder.decode() 함수를 한번만 ( 한번은 자동으로 디코딩을 수행하기 때문에 ) 호출하면 됩니다

이렇게 될 경우 tomcat이 자동으로 디코딩한 결과는 원래 기대 값인 인코딩 된 값

%EC%95%88%EB%85%95%ED%95%98%EC%84%B8%EC%9A%94 과 같은 모양의 문자열

이 되고, 이를 URLDecoder.decode() 함수로 제대로 디코딩 해주기 때문에 원하는 결과 값이 나오게 됩니다






1 2 3 4 5 6 7 8 9 10 다음