c++

c++ 공부

이야기prog 2025. 1. 30. 00:30

 

#pragma once
#include <algorithm>
#include "pch.h"

//string 초기화
#define UNDEFINE_NAME						L"Undefine_Name" // wchar_t타입 wide_char

//snprintf 재정의, 안전을 위해 array 사용을 기본으로 합니다.
#define snprintf(dst, format, ...)			_snprinf_s(dst.data(), dst.size(), _TRUNCATE, format, __VA_ARGS__) // __VA_ARGS__ = 가변인자 ...이랑 같이 쓰임 (매크로에서만 쓰임)
#define snwprintf(dst, format, ...)			_snwprintf_s(dst.data(), dst.size(), _TRUNCATE, format, __VA_ARGS__)

//범위 보정 및 체크
#define fixInRange(minumum, x, maximum)		min(maximum, max(x, minimum)) 
#define isInRange(minimum, x, maximum)		(x == fixInRange(minimum, x, maximum)) ? true : false

//overflow 체크
inline bool isOverFlower_uint(unsigned int original, unsigned int add) {
	unsigned int before = original;
	unsigned int after = original + add;
	if ((original & 0x80000000) != (after & 0x80000000)) {
		return false;
	}											
	return true;														//이 코드가 오버플로우를 탐지하는게 맞을까?
}

//컴파일 기본 매크로 회피용 __FUNCTION__ 같은..
#define __W(x)								L##x // 토큰 결합 연산자 = L"x"
#define _W(X)								__W(x)

//-------------------------------------------------------------------------------------------------------//
//문자열 변환
inline void StrConvA2T(CHAR* src, TCHAR* dest, size_t destLen) {
#ifdef UNICODE								// r_winnt
	if (destLen < 1) {
		return;
	}
	MultiByteToWideChar(CP_ACP, 0, src, -1, dest, (int)destLen - 1);
#endif
}

inline void StrConvT2A(TCHAR* src, CHAR* dest, size_t destLen) {
#ifdef UNICODE
	if (destLen < 1) {
		return;
	}
	WideCharToMultiByte(CP_ACP, 0, src, -1, dest, (int)destLen, NULL, FALSE);
#endif
}

inline void StrConvA2W(CHAR* src, WCHAR* dest, size_t destLen) {
	if (destLen < 1) {
		return;
	}
	MultiByteToWideChar(CP_ACP, 0, src, -1, dest, (int)destLen - 1);
}

inline void StrConvT2A(WCHAR* src, CHAR* dest, size_t destLen) {
	if (destLen < 1) {
		return;
	}
	WideCharToMultiByte(CP_ACP, 0, src, -1, dest, (int)destLen, NULL, FALSE);
}
//---------------------------------------------------------------------------------------------------------------------------------//
// delete object
#undef SAFE_DELETE
#define SAFE_DELETE(obj)			\
{									\
	if ((obj)) delete(obj);			\
	(obj) = 0L;						\
}
// 0L == nullptr

#undef SAFE_DELETE_ARRAY
#define SAFE_DELETE_ARRAY(arr)			\
{									\
	if ((arr)) delete[](arr);		\
	(arr) = 0L;						\
}

// delete gameObject
#define SAFE_FREE(obj)					\
{									\
	if ((obj)) obj->free();			\
	(obj) = 0L;						\
}									// obj->free()가 사용자 정의 함수인가?

#define SAFE_RELEASE(obj)			\
{									\
	if (obj) { obj.release(); }		\
}

 

#include "pch.h"
#include "clock.h"

Clock::Clock() {
	serverStartTick_ = this->systemTick();
}

Clock::~Clock() {

}


tick_t Clock::serverStartTick() {
	return serverStartTick_;
}

tick_t Clock::strToTick(wstr_t str, const WCHAR* fmt) {
	int year = 0, month = 0, day = 0, hour = 0, minute = 0, second = 0;
	swscanf_s(str.c_str(), fmt, &year, &month, &day, &hour, &minute, &second); // str.c_str() -> year, month...에 대입
			//  초		분		시	 일		월(0부터 시작) 년(1900년부터시작)
	tm time = {second, minute, hour, day, month - 1, year - 1900};

	return mktime(&time); // time_t
}

tick_t Clock::systemTick() {							// 현재 시간을 time_t 즉 tick_t 타입으로 반환
	return system_clock::to_time_t(system_clock::now()); // std::chrono::system_clock = posix 시간 
														// std::chrono::steady_clock = 프로그램 시작 이후 시간
}

wstr_t Clock::tickToStr(tick_t tick, const WCHAR* fmt) {
	array<WCHAR, SIZE_128> timeStr;

	tm time;
	localtime_s(&time, &tick); // time_t -> tm
	wcsftime(timeStr.data(), timeStr.size(), fmt, &time); //wide char string format time wchar* 버퍼에 time 대입
	return timeStr.data(); // wstr_t는 const wchar_t*로 생성자를 받을 수 있음 
}

wstr_t Clock::nowTime(const WCHAR* fmt) {	// 현재 시간을 wstr_t타입으로 반환
	return this->tickToStr(this->systemTick(), fmt);
}

wstr_t Clock::nowTimeWithMilliSec(const WCHAR* fmt) {
#if 0
	timePoint now = system_clock::now();
	timePoint oldSecond = system_clock::from_time_t(this->systemTick());  //startServerTick()이 더 적절하지 않나?

	std::chrono::duration<double> milliSecond = now - oldSecond;
	array<WCHAR, SIZE_8> milliStr;
	snwprintf(milliStr, L"%03d", (int)(milliSecond.count() * 1000));
#else
	high_resolution_clock::time_point point = high_resolution_clock::now(); // high_resolution_clock = steady_clock
	milliseconds ms = duration_cast<milliseconds>(point.time_since_epoch()); //now().time_since_epoch() epoch는 처음이란 뜻 즉 처음부터 지금까지의 차이값

	seconds s = duration_cast<seconds>(ms);
	tick_t t = s.count(); //개수를 세다. 단위가 초단위임으로 결국 time_t와 같음
	std::size_t fractionalSeconds = ms.count() % 1000;
	array<WCHAR, SIZE_8> milliStr;
	snwprintf(milliStr, L"%03d", (int)(fractionalSeconds)); // std::wstring 또는 wchar_t*의 버퍼에 wchar타입으로 저장
#endif
	wstr_t timeString = this->tickToStr(this->systemTick(), fmt);
	timeString += L".";
	timeString += milliStr.data();
	return timeString;
}

wstr_t Clock::today() {
	return this->tickToStr(this->systemTick(), DATE_FORMAT);
}

wstr_t Clock::tomorrow() {
	return this->tickToStr(this->systemTick() + DAY_TO_TICK(1), DATE_FORMAT);
}

wstr_t Clock::yesterday() {
	return this->tickToStr(this->systemTick() - DAY_TO_TICK(1), DATE_FORMAT);
}

DayOfTheWeek Clock::todayOfTheWeek() {
	tm time;
	tick_t tick = this->systemTick();
	localtime_s(&time, &tick);
	return (DayOfTheWeek)time.tm_wday;
}

오늘은 게임 서버 구축을 따라하면서 학습한 점을 적어보겠다.

<chrono> 헤더파일을 공부했는데,

std::chrono namespace 에서 구현되어있고, std::chrono::system_clock과 std::chrono::steady_clock이 대표적이다.

system_clock은 현 시간을 posix 시간 1970년 1월 1일 00:00:00을 기준으로 시간을 구현하고,

steady_clock은 프로그램 시작 시간을 기준으로 시간을 구현한다. 

자주 쓰이는 now() 함수는 현재 시간을 나타내는 것인데 time_point 클래스타입으로 return해주는데 그냥 특정 시간 구간이란 느낌이다.

 

그리고 time_point끼리의 연산은 std::chrono::nanoseconds타입으로 연산하는 것으로 보인다.

그래서 초단위를 하고싶으면 time_t t = std::chrono::system_clock::to_time_t(std::chrono::system_clock::now())를 하면 time_t = long long타입이며 초단위로 나타낼 수 있다.

 

time_since_epoch란 함수도 있는데, time_point 타입에 객체에 time_since_epoch를 사용하면 그 객체의 시간과 현재 시간사이의 간격을 nanoseconds타입의 값으로 나타낸다.

 

std::chrono::duration<double, ratio<60>> tmp; 뭐 이런식으로도 쓸 수있는데, 이렇게 사용하면 1분이 60초 기준이란 걸 ratio로 알려주고 double이기 때문에 소수점까지 나타낼 수 있다.

 

swscanf_s(): string wide scanf_s로 외웠는데, scanf처럼 사용하는데, wchar_t* 배열에 format형식으로 &주소값에 값을 대입한다. swscanf_s(str.c_str(), format, var_arg);

 

tm타입은 구조체로 {second, minute, hour, day, month, year}로 month는 0부터 시작 year은 1900년기준이다.

 

mktime(&tm)으로 tm타입 구조체를 time_t타입으로 반환해준다.

 

localtime(&tm, &time_t) time_t -> tm이다.

 

wcsftime() tm값을 const wchar_t* 버퍼에 저장해준다. wcsftime(arr.data(), arr.size(), format, &tm);

 

_snwprintf_s(dst.data(), dst.size(), _TRUNCATE, format, __VA_ARGS__): string n wide printf로 외웠는데, wchar_t* dst.data()에 포맷에 맞게 가변인자값을 저장한다.

 

_TRUNCATE는 문자열을 지정된 버퍼 크기 내에서 자동으로 잘라주는 매크로이다.

 

 

게임 서버 프로그래밍 입문 (김동성 지음)책으로 공부함.

 

 

 

 

 

'c++' 카테고리의 다른 글

c++ 공부  (0) 2025.02.27
c++공부  (0) 2025.02.24
c++ 어셈블리어 간단하게 공부  (0) 2025.02.22
c++ 공부  (0) 2025.02.12
c++ 공부  (0) 2025.01.29