设为首页 加入收藏

TOP

写一个Windows上的守护进程(4)日志其余(一)
2017-10-11 17:35:27 】 浏览:819
Tags:一个 Windows 守护 进程 日志 其余

写一个Windows上的守护进程(4)日志其余

这次把和日志相关的其他东西一并说了。

 

一、vaformat

C++日志接口通常有两种形式:流输入形式,printf形式。

我采用printf形式,因为流输入不好控制格式。

printf形式要求日志接口支持不定长参数,我没有直接在日志实现类里边支持不定长参数,而是只接受一个字符串参数,可以参见第一篇。

为什么呢?

如果要成为不定长参数,就是这样

bool log_string(const LOG_LEVEL level, const char* file, const int line, const char *s, ...);

那么在每一个log_xxx的变体里就都要写_vsnprintf_s那一套代码了,而且是完全一样的(我不知道__VA_ARGS__宏是否可以传递),这显然是不好的做法。

我把不定长参数的处理放在了宏定义里,类似:

#define ErrorLog(s, ...) _Log(LOG_ERROR, __FILE__, __LINE__, vaformat(MAX_LOG_BUFFER, s, __VA_ARGS__))

vaformat就是处理不定长参数的:

std::string vaformat(const size_t max_size, const char* msg, ...);
std::wstring vaformat(const size_t max_size, const wchar_t* wmsg, ...);

因为并不知道格式化后有多长,所以要指定最大长度,如果格式化后的长度大于最大长度,则截断。vaformat里还有一个小技巧,当指定的max_size小于1024的时候,使用栈空间,否则申请堆内存,这是从std::string的实现中学来的——SSO短字符串优化。

可以看下vaformat的实现,两个版本的代码基本一样,这样当然是不好的,但是我不知道怎样把他们合并起来,这是一个todo。类似的问题下面还有。

 

二、CLastErrorFormat

这个东西是用来解决第一篇里提到的记录LastErrorCode的问题的。

它的主要功能就是把error code转换成文本描述,合适的构造也可以省去GetLastError的调用:

class CLastErrorFormat : public boost::noncopyable
{
public:
    CLastErrorFormat()
        : m_code(GetLastError())
    {
    }

    CLastErrorFormat(const DWORD code)
        : m_code(code)
    {
    }

    ~CLastErrorFormat()
    {
    }

public:
    const DWORD code() const
    {
        return m_code;
    }

    const std::string& str()
    {
        //...
    }

    const std::wstring& wstr()
    {
        //...
    }

private:
    //...
};

日志实现类里对应的接口:

bool log_last_error(const LOG_LEVEL level, const char* file, const int line, CLastErrorFormat& e, const std::string& prefix);

接受一个CLastErrorFormat的引用,然后在记录日志的时候,把error code和其对应的描述也记录下来:xxx, error code: 999, error msg: yyy

最终的日志接口是有两个版本的:一个接受一个CLastErrorFormat参数;另一个省去,在函数内部自己构造。

 

三、str_encode

我不可能在日志文件里一会记宽字符串,一会记窄字符串,那就没法看了,又考虑到日志文件的大小,我最终决定,按照窄字符串SystemCurrentCodePage(在简体中文版的Windows上,就是GB2312)编码记录日志,所以对于宽字符串我还要转换成窄字符串。

Windows提供了两个API来做编码转换:MultiByteToWideChar和WideCharToMultiByte,而这两个API总是要两次调用才能安全的转换。我将其稍稍封装了一下,做成了两个函数:

std::wstring multistr2widestr(const unsigned int from_code_page, const std::string& s);
std::string widestr2multistr(const unsigned int to_code_page, const std::wstring& ws, const char *default_char = NULL);

注:SystemCurrentCodePage的代码页编码就是CP_ACP。

这样在记宽字符串的时候总是会慢一些,所以我代码中,能用窄字符串的地方我都用窄字符串了。

 

四、any_lexical_cast

代码中总是免不了要做类型转换,特别是把数字转换成字符串,为了简单一点,我使用了boost的lexical_cast,虽然大家都说这货效率低,因为使用了C++的流,但是我坚持“先正确,再优化”的原则,还是使用了它。

然而,这个东西使用起来有两处不便:

1. 转换失败的时候会抛异常

2. 把bool转换成string的时候是0或1,不是true或false

 

为了解决这两个问题,我又做了一下封装:

1. 转换失败的时候,填充为默认值。调用者必须提供默认值

2. 特化对于bool和string之间的转换

 

这就是any_lexical_cast:

template<typename Target, typename Source>
Target any_lexical_cast(const Source& src, const Target& fail_value)
{
    Target value = fail_value;
    try
    {
        value = boost::lexical_cast<Target>(src);
    }
    catch (boost::bad_lexical_cast&)
    {
        value = fail_value;
    }
    return value;
}

template<>
bool any_lexical_cast<bool, std::string>(const std::string& src, const bool& fail_value);

template<>
bool any_lexical_cast<bool, std::wstring>(const std::wstring& src, const bool& fail_value);

template<>
std::st
首页 上一页 1 2 下一页 尾页 1/2/2
】【打印繁体】【投稿】【收藏】 【推荐】【举报】【评论】 【关闭】 【返回顶部
上一篇HDOJ1242 Rescue(营救) 搜索 下一篇复数类(C++练习一)

最新文章

热门文章

Hot 文章

Python

C 语言

C++基础

大数据基础

linux编程基础

C/C++面试题目