들여쓰기, 간격
- 한 줄짜리 if문과 for문에도 중괄호 들여쓰기를 적용한다.
- 괄호 내부에 공백을 삽입한다.
- 함수의 매개변수 목록, 함수 호출 시 인자 목록 내부에 공백을 삽입한다.
- 조건문의 조건식에 공백을 삽입한다.
- 괄호로 묶인 식의 내부에 공백을 삽입한다.
- 대괄호 내부에 공백을 삽입한다.
- 이항 연산자의 앞뒤에 공백을 삽입한다.
- 람다의 대괄호 내부에 공백을 삽입한다.
키워드 추가
// keyword.h
#ifndef _keyword
#define _keyword
#define PURE // 대입 연산을 하지 않으며, 결과값이 외부 상태와 무관한 순수 함수
#define NOASSIGNMENT // 대입 연산을 하지 않는 함수
#define HELPER // 사용자 코드에서의 직접 호출되지 않고 다른 클래스나 함수의 도우미로써 이용되는 클래스나 함수
#define NONCREATABLE // 생성이 불가능한 클래스
#define NONCOPYABLE // 복사가 불가능한 클래스
#define MOVABLE // 이동 연산을 지원하여, r-value optimization이 가능한 클래스
#define INTERFACE // 순수 가상함수를 포함하거나, CRTP 패턴에 쓰여 사용자 코드에서 실제 생성할 일이 없고 상속을 위해서만 존재하는 클래스
#endif
|
cs |
빈 #define을 추가하여 키워드처럼 활용한다.
후에 디버깅할 때 함수나 클래스에 붙어있는 키워드를 확인하여 좀 더 편리한 디버깅이 가능하다.
// xyutility.h
#ifndef _xyutility #define _xyutility #include <windows.h>
#include "keyword.h"
// ...
class rect_builder
{
public:
rect_builder& left( const int val ) { /* ... */ }
rect_builder& top( const int val ) { /* ... */ }
rect_builder& right( const int val ) { /* ... */ }
rect_builder& bottom( const int val ) { /* ... */ }
RECT build() { /* ... */ }
// ...
private:
// ...
HELPER const int implicit_left() { /* ... */ }
HELPER const int implicit_top() { /* ... */ }
HELPER const int implicit_right() { /* ... */ }
HELPER const int implicit_bottom() { /* ... */ }
};
#endif |
cs |
implicit_xxx 함수는 build()에서 간접적으로 left, top, right, bottom을 설정하는 데 쓰이는 HELPER 함수이다.
// singleton.h
#ifndef _singleton
#define _singleton
#include "keyword.h"
template < typename T >
class Singleton NONCOPYABLE INTERFACE
{
public:
static auto& getinst() NOASSIGNMENT
{
static T inst = no_constructor_call{};
return inst;
}
Singleton( const Singleton& ) = delete;
void operator=( const Singleton& ) = delete;
protected:
struct no_constructor_call {};
Singleton() {}
};
#endif
|
cs |
Singleton 클래스는 NONCOPYABLE 한 INTERFACE 클래스이다.
또한 Singleton::getinst() 는 NOASSIGNMENT 함수이다.
// randomvalue.h
#ifndef _randomvalue
#define _randomvalue
#include <random>
#include <vector>
#include <array>
#include "keyword.h"
// ...
const auto random_value( const std::uniform_int_distribution<>& uid ) PURE
{ /*...*/ }
const auto random_value( const int lower_bound, const int upper_bound ) PURE
{ /*...*/ }
auto random_value_vector( const std::uniform_int_distribution<>& uid, const size_t cnt )
{ /*...*/ }
auto random_value_vector( const int lower_bound, const int upper_bound, const size_t cnt )
{ /*...*/ }
template <size_t N>
auto random_value_array( const std::uniform_int_distribution<>& uid )
{ /*...*/ }
template <size_t N>
auto random_value_array( const int lower_bound, const int upper_bound )
{ /*...*/ }
const auto random_value( const std::uniform_real_distribution<>& urd ) PURE
{ /*...*/ }
const auto random_value( const double lower_bound, const double upper_bound ) PURE
{ /*...*/ }
auto random_value_vector( const std::uniform_real_distribution<>& urd, const size_t cnt )
{ /*...*/ }
auto random_value_vector( const double lower_bound, const double upper_bound, const size_t cnt )
{ /*...*/ }
template <size_t N>
auto random_value_array( const std::uniform_real_distribution<>& urd )
{ /*...*/ }
template <size_t N>
auto random_value_array( const double lower_bound, const double upper_bound )
{ /*...*/ }
#endif
|
cs |
random_value 함수는 외부 환경인 랜덤 엔진에 영향을 받지만, 사용자가 랜덤 엔진을 건드리는 일이 없다고 확신할 수 있으므로 NOASSIGNMENT 가 아니라 PURE 키워드를 붙였다.
random_value_vector와 random_value_array는 자료구조로의 대입이 있으므로 대입 연산이 있는 함수이다.
// database.h
#ifndef _database
#define _database
#include <fstream>
#include <string>
#include <vector>
#include <sstream>
#include "keyword.h"
namespace _csv
{
HELPER auto csv_read_row( std::istream& in, char delimiter ) { /* ... */ }
template < typename Str_t >
auto csv_read( const Str_t file_path ) { /* ... */ }
}
// ...
#endif
|
cs |
csv_read_row는 csv_read에 쓰이는 HELPER 함수이다.
// timer.h
#ifndef _timer
#define _timer
#include <chrono>
#include <iostream>
#include "keyword.h"
class timer NONCREATABLE
{
public:
static constexpr int ms_per_frame() PURE
{
return 30;
}
static void on_timer() { /* ... */ }
timer() = delete;
// ...
};
#endif
|
cs |
timer는 생성자가 삭제된 NONCREATABLE 클래스이고
timer::ms_per_frame은 항상 30을 반환하는 PURE 함수이다.
경고 제거
- 암묵적 형변환이 일어나고 있는 곳을 명시적 형변환으로 바꾸어 경고 메시지를 제거한다.
- 반복문 조건으로 정수 비교 시 int와 size_t를 비교하는 경우 size_t와 size_t의 비교로 바꾼다.
// xyutility.h
#ifndef _xyutility
#define _xyutility
#include <windows.h>
const auto xytrans( const RECT& destrt, const RECT& srcrt, const POINT& pt )
{
auto xdiff = pt.x - srcrt.left;
auto ydiff = pt.y - srcrt.top;
auto widthratio = ( destrt.right - destrt.left ) / static_cast<double>( srcrt.right - srcrt.left );
auto heightratio = ( destrt.bottom - destrt.top ) / static_cast<double>( srcrt.bottom - srcrt.top );
xdiff = static_cast< LONG >( xdiff * widthratio );
ydiff = static_cast< LONG >( ydiff * heightratio );
return POINT{ destrt.left + xdiff, destrt.top + ydiff };
}
// ...
|
cs |
xdiff *= widthratio;
ydiff *= heightratio; 부분이 수정되었다.
// randomvalue.h
#ifndef _randomvalue
#define _randomvalue
#include <random>
#include <vector>
#include <array>
#include "keyword.h"
namespace _rv
{
std::random_device rd;
std::default_random_engine dre( rd() );
}
// ...
auto random_value_vector( const std::uniform_int_distribution<>& uid, const size_t cnt )
{
std::vector< int > v;
v.reserve( cnt );
for ( size_t i = 0; i < cnt; ++i )
{
v.push_back( random_value( uid ) );
}
return v;
}
// ...
auto random_value_vector( const std::uniform_real_distribution<>& urd, const size_t cnt )
{
std::vector< double > v;
v.reserve( cnt );
for ( size_t i = 0; i < cnt; ++i )
{
v.push_back( random_value( urd ) );
}
return v;
}
// ...
#endif
|
cs |
for ( int i = 0; i < cnt; ++i ) 이
for ( size_t i = 0; i < cnt; ++i ) 와 같이 수정되었다.
timer 리팩토링
// TMP.h
#ifndef _TMP
#define _TMP
// ...
#include <chrono>
// ...
template < typename _Timet = std::chrono::milliseconds, typename Func, typename ... Args >
auto timefunc( Func func, Args... args )
{
using namespace std::chrono;
using _Countt = std::chrono::nanoseconds;
auto tp = system_clock::now();
func( args... );
return duration_cast<_Countt>( system_clock::now() - tp ).count()
/ static_cast<long double>( _Countt::period::den / _Timet::period::den );
}
// ...
#endif
|
cs |
함수의 실행시간을 재는 유틸리티 함수를 추가하였다.
프레임 처리에 걸린 시간은 이 함수를 통해서 재도록 한다.
// timer.h
#ifndef _timer
#define _timer
#undef max // c 매크로 max를 제거하기 위함
#include <chrono>
#include <iostream>
#include "keyword.h"
#include "TMP.h"
#include <algorithm>
class timer NONCREATABLE
{
public:
static constexpr int ms_per_frame() PURE
{
return 30;
}
static void on_timer()
{
static int lag = 0;
std::cout << "\n루틴 시작 당시 lag : " << lag << std::endl;
auto elapsed = static_cast<int>( timefunc( go_routines, lag ) );
lag = std::max( lag + elapsed - ms_per_frame(), 0 );
}
timer() = delete;
private:
HELPER static inline void go_routines( const int current_lag )
{
update();
if ( current_lag < ms_per_frame() )
render();
}
HELPER static void update()
{
std::cout << "업데이트 루틴!\n";
for ( int i = 0; i < 20000000; ++i )
i += 2;
}
HELPER static void render()
{
std::cout << "렌더 루틴!\n";
for ( int i = 0; i < 40000000; ++i )
i += 2;
}
};
#endif
|
cs |
각 루틴들을 HELPER 함수로 분리하였다.
on_timer는 함수를 실행하고 lag을 갱신하는 것으로 더 직관적이게 되었다.
모든 함수가 세 줄 이내가 되었다! ( on_timer 의 출력은 디버깅용이니까... )
'Project > Win32API로 던그리드 모작' 카테고리의 다른 글
std::iostream 으로 std::tuple 입출력하기 (0) | 2021.08.23 |
---|---|
std::stringstream 을 통해 문자열을 값으로 (0) | 2021.08.23 |
csv 문서 파싱 (0) | 2021.08.19 |
게임 프레임 관리 (0) | 2021.08.19 |
빌더 패턴 - 직사각형 만들기 (0) | 2021.08.18 |