Log的輸出問題是程序設計需要在設計初期就要計劃的一個模塊,其接口和功能的設計,會直接影響整體程序的調試和信息輸出等方面。
wxDeMPQ在初期,計劃通過其StatusBar作為Log輸出,并簡單封裝了一個函數,但在實際使用中,由于急于實現功能和DLL等的引入,加上VC方便的調試,導致完全廢棄了當初的想法,這也是的wxDeMPQ在后期實現上沒有任何的Log輸出,這對于Debug的程序來說,是--災難和活該。
對于類似Log輸出的模塊,在實現時需要考慮的是使用時的易融合性和靈活性。簡單地說,一來此模塊的引入不應影響原代碼的執行;二來要考慮信息輸出的不確定性,就是說你可能無法預知程序會最終通過什么方式輸出,文件?Console?或者一個EditCtrl?這兩點我認為是需要在初期著重考慮的。
下面是一個簡單Log輸出模塊的實現,通過Adapter方式,連接輸入和輸出。
#pragma once
#include <string>
#ifdef OUTPUTAGENTLIBRARY_EXPORTS
#define OUTPUTAGENTLIBRARY_API __declspec(dllexport)
#else
#define OUTPUTAGENTLIBRARY_API __declspec(dllimport)
#endif
namespace OUTPUT
{
class OUTPUTAGENTLIBRARY_API COutputBase
{
public:
COutputBase() {}
virtual ~COutputBase() {}
virtual void Output(const std::wstring& info) = 0;
};
const unsigned int LEVEL_DEBUG = 1;
const unsigned int LEVEL_INFO = 2;
const unsigned int LEVEL_WARN = 4;
const unsigned int LEVEL_ERROR = 8;
const unsigned int LEVEL_ALL = 0xFFFFFFFF;
class OUTPUTAGENTLIBRARY_API COutputAgentObject
{
public:
COutputAgentObject();
virtual ~COutputAgentObject();
void SetLevel(unsigned int level);
unsigned int GetLevel() const;
void SetOutput(COutputBase* output);
COutputBase * GetOutput() const;
void Output(unsigned int level, const std::wstring& info);
protected:
COutputBase* _output;
unsigned int _level;
};
}
//#define OUTPUT(outputptr, level, info) \
//{\
// if(outputptr != NULL) \
// {\
// std::wostringstream ostr; \
// ostr << info; \
// outputptr->Output(level, ostr.c_str()); \
// }\
//}
#include "stdafx.h"
#include "OutputAgentObject.h"
namespace OUTPUT
{
COutputAgentObject::COutputAgentObject()
: _output(NULL), _level(LEVEL_ALL)
{
}
COutputAgentObject::~COutputAgentObject()
{
}
void COutputAgentObject::SetLevel(unsigned int level)
{
_level = level;
}
unsigned int COutputAgentObject::GetLevel() const
{
return _level;
}
void COutputAgentObject::SetOutput(COutputBase* output)
{
_output = output;
}
COutputBase* COutputAgentObject::GetOutput() const
{
return _output;
}
void COutputAgentObject::Output(unsigned int level, const std::wstring& info)
{
if(_output == NULL)
return;
if((level & _level) == 0)
return;
_output->Output(info);
}
}
下面是一段測試代碼,用于展示如何使用。
#pragma once
#include <string>
#include <sstream>
#include "wx/wx.h"
#include "OutputAgentObject.h"
class COutput : public OUTPUT::COutputBase
{
public:
COutput(wxTextCtrl* text)
:_text(text)
{
}
virtual ~COutput() {}
virtual void Output(const std::wstring& info)
{
if(_text != NULL)
{
_text->AppendText(WString2wxString(info));
}
}
protected:
const wxString WString2wxString(const std::wstring& str)
{
return wxString(str.c_str(), wxConvISO8859_1);
}
private:
wxTextCtrl* _text;
};
OUTPUT::COutputAgentObject g_stOutputAgent;
#define OUTPUT(level, info) \
{\
std::wostringstream ostr; \
ostr << info; \
g_stOutputAgent.Output(level, ostr.str());\
}
_output = new COutput(m_textOutput);
g_stOutputAgent.SetOutput(_output);
OUTPUT(OUTPUT::LEVEL_INFO, L"Level:" << g_stOutputAgent.GetLevel() << std::endl);
簡單實現,很多因素并不考慮,如Thread Safe問題等,源碼和測試代碼在
這里。測試程序模樣如下:
