#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는 문자열을 지정된 버퍼 크기 내에서 자동으로 잘라주는 매크로이다.
게임 서버 프로그래밍 입문 (김동성 지음)책으로 공부함.