将设计模式运用于游戏设计:将日志做成单件

来源:互联网 发布:html js页面传值 编辑:程序博客网 时间:2024/05/18 16:38

在游戏程序中日志显得非常重要,例如在调试AI的时候需要连续跟踪AI状态的变化,在非正常的函数返回点记录状态信息等等。但是这一系列的log如果没有统一管理那么将变得很混乱,而且在多线程的情况下有可能会出现同步问题。为此专门用单件的模式制作了一个日志类,希望能派的上用场。不多说了,直接贴代码,原理很简单,关键地方有注释。

log.h

#include <windows.h>
#include 
<fstream>

class CSynObj
{
public:
    CSynObj()
    {
        InitializeCriticalSection(
&m_cs);
    }
   
    
~CSynObj()
    {
        DeleteCriticalSection(
&m_cs);
    }

   
void Lock()
   {
       EnterCriticalSection(
&m_cs);    
   }

   
void UnLock()
   {
       LeaveCriticalSection(
&m_cs);
       
   }

private:
    CRITICAL_SECTION m_cs;
};

class CLock        
{
public:
    CLock(CSynObj
& synchobject)
        :refSynchObject(synchobject)
    {
        refSynchObject.Lock();
    }

    
virtual ~CLock()
    {
        refSynchObject.UnLock();
    }

protected:
    CSynObj
& refSynchObject;
};

enum LOG_TARGET_E
{
    LOG_TARGET_SCREEN    
= 1,
    LOG_TARGET_FILE        
= 2,
};


class CLog
{
private:

    
~CLog();

    CLog();

    CLog(
const CLog& rhs) {}

    CLog
& operator = (const CLog& rhs) {}

    
static CLog*    m_pLog;

    
static CSynObj    m_Lock;

    std::ofstream    m_OutFile;

    
void GetSysTime(SYSTEMTIME* lpSysTime);

    
void FormatAllTime(const SYSTEMTIME& refSysTime, std::string& strOut);

public:

    
void    Write(char* pMsg, int nTarget);

    
static CLog*    GetInstance();

    
// 摧毁singleton的入口
    static void DestroyInstance();
};

__inline 
void DEBUG_MSG(LPSTR filename, int lineno, LPSTR linedesc, DWORD dwErrCode, int nOutPutTarget = LOG_TARGET_SCREEN|LOG_TARGET_FILE)
{
    
char cTemp[256];
    sprintf( cTemp, 
"%lu error on %d line in %s file: %s", dwErrCode, lineno, filename, linedesc );
    CLog
* pLog = CLog::GetInstance();
    pLog
->Write( cTemp, nOutPutTarget );
}

 log.cpp

#include <iostream>
#include 
<log.h>

CLog
* CLog::m_pLog = NULL;
CSynObj    CLog::m_Lock;

CLog::
~CLog()
{
    m_OutFile.close();
}

CLog::CLog()
{
    m_OutFile.open( 
"Log.txt", std::ios::app );
    atexit( CLog::DestroyInstance );
}

void CLog::GetSysTime(SYSTEMTIME* lpSysTime)
{
    
//获得系统时间
    FILETIME CurFileTime;
    ::GetSystemTimeAsFileTime(
&CurFileTime);
    ::FileTimeToLocalFileTime(
&CurFileTime, &CurFileTime);
    ::FileTimeToSystemTime(
&CurFileTime, lpSysTime);
}

void CLog::FormatAllTime(const SYSTEMTIME& refSysTime, std::string& strOut)
{
    
//系统时间转换成字符串
    char cBuffer[MAX_PATH*2];
    sprintf(cBuffer,
"%d-%02d-%02d %02d:%02d:%02d",
        refSysTime.wYear, refSysTime.wMonth, refSysTime.wDay , refSysTime.wHour , refSysTime.wMinute , refSysTime.wSecond);
    strOut 
= cBuffer;
}


void CLog::Write(char* pMsg, int nTarget)
{
    
// 区间锁,在同一时刻只允许一个线程进行写文件或屏幕
    CLock Lock( CLog::m_Lock );
    SYSTEMTIME Systime;
    std::
string strTime;
    GetSysTime( 
&Systime );
    FormatAllTime( Systime, strTime );
    
if( nTarget & LOG_TARGET_FILE )
        m_OutFile 
<< strTime.c_str() << "" << pMsg << std::endl;
    
if( nTarget & LOG_TARGET_SCREEN )
        std::cout 
<< pMsg << std::endl;
}


CLog
* CLog::GetInstance()
{
    
// 双次检测的好处
    
// 1:防止多线程同时进入new CLog;
    
// 2:临界区写在第二次判断,提高了效率
    if!m_pLog )
    {
        CLock Lock( CLog::m_Lock );
        
if!m_pLog )
            m_pLog 
= new CLog;
    }
    
return m_pLog;
}

// 与GetInstance对应,给CLog一个delete的机会
void CLog::DestroyInstance()
{
    delete m_pLog;
    m_pLog 
= NULL;
}





int main()
{
    atexit( CLog::DestroyInstance );
    DEBUG_MSG( __FILE__, __LINE__, 
"main()"0 );
    getchar();
    
return 0;
}
原创粉丝点击