코드 리팩토링
Project/Win32API로 던그리드 모작

코드 리팩토링

Woon2World :: Programming & Art Life

 

 

들여쓰기, 간격

  • 한 줄짜리 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 의 출력은 디버깅용이니까... )