csr8615 mcu:C++临时变量的另类应用:基于iostream的类型安全的log接口设计
来源:百度文库 编辑:偶看新闻 时间:2024/04/29 02:54:42
C++临时变量的另类应用:基于iostream的类型安全的log接口设计
几年前做的一个东西,现在专注于系统底层开发,C++的高级功能用的比较少了,写出来回忆回忆。在实际的应用开发中,log模块设计是必 不可少的一部分,log模块设计的好坏直接影响到系统的性能和日后的维护。总的来说,log模块在功能上除了日志级别、时间和消息正文这些必须的信息外, 最好还能记录日志产生时尽可能多的信息,比如线程ID、模块名称、源文件、代码行等。在log接口的设计上应该尽可能简化,以方便调用。一个调用起来很麻 烦的接口,估计是没人想用的。
在基于C/C++开发的应用中,log接口通常设计成带可变参数的
void __cdecl PrintLog(int log_level, const char* format, ...);
对C 程序员这是很合理的选择。但是对于强调强类型安全的C++来说
类似于printf的其他iostream替代者,我们希望log接口
log << "test for iostream log";
log << "fopen failed. errno=" << errno;
但是对于log来说,用iostream的一个问题在于如何界定一条完整的log信息
同时,log level和log time也是log信息的必要组成部分,必须要记录
log << loglvl_info << log_time << "test for iostream log";
log << loglvl_err << log_time << "fopen failed. errno=" << errno;
估计没有谁喜欢用。还有一个要注意的是
这些问题中,最关键的是第一个问题,即如何从多个流操作中界定一条完
class logstream : public std::ostream
{
public:
explicit logstream();
~logstream()
{
write_log(m_szLogText);
}
//...
};
要产生log时这样调用:
logstream() << "fopen failed. errno=" << errno;
在这条语句结束时,logstream()临时变量被析构
#define LL_ERROR 1
#define LL_INFO 2
class logstream : public std::ostream
{
public:
explicit logstream( int level);
~logstream()
{
write_log(m_szLogText);
}
};
#define logerr logstream( LL_ERROR)
#define loginfo logstream( LL_INFO)
这样调用log接口:
logerr << "fopen failed. errno=" << errno;
上面这行其实有一个问题:在C++标准草案中,临时变量是r
我这里的work-around利用了r-value可以调用成员函数,而成员函数可以返回non
class logstream : public std::ostream
{
public:
explicit logstream( int level);
~logstream();
logstream& l_value() { return *this; }
};
#define logerr logstream( LL_ERROR).l_value()
#define loginfo logstream( LL_INFO).l_value()
关于临时变量的一个比较详细的讨论可以看这里。另外一个要注意的问题是要保证析构函数中的所有操作都不能抛出异常
附 录给出了一份比较完整的logstream接口代码。由于logstream基于iostream,其接口非常简单易用,继承了iostream的很多优 点:如对所有数据类型具有一致性的接口,支持iostream library原先支持的所有数据类型,扩展能力很强-新增数据类型对iostream的重载自动适用于logstream。除此之外,该 logstream是线程安全的。对于每条log信息,除了基本的log level和log time之外,logstream还能记录生成每条log 的线程ID、模块名称、源文件代码行、函数名等信息。
附:比较完整的logstream接口部分代码。
#ifndef __LOG_STREAM_H__
#define __LOG_STREAM_H__
#include
#include
#include
#include "logleveldef.h"
using std::ostream; // 为了应付vc6.0的bug
class logstream : public std::ostream
{
logstream( const logstream& );
const logstream& operator=( const logstream& );
public:
explicit logstream( int level, int line, const char* file, const char* function );
~logstream();
logstream& l_value() { return *this; }
// strstreambuf标准接口
std::strstreambuf *rdbuf() const
{return ( (std::strstreambuf *)&streambuf_ ); }
void freeze(bool f = true)
{streambuf_.freeze(f); }
char *str()
{return (streambuf_.str()); }
std::streamsize pcount() const
{return (streambuf_.pcount()); }
private:
std::strstreambuf streambuf_;
LogMessage message;
};
inline
logstream::logstream( int level, int line, const char* file, const char* function )
: ostream(&streambuf_)
, streambuf_( &LogMessage::mem_alloc, &LogMessage::mem_free )
, message(level, line, file, function)
{
}
inline
logstream::~logstream()
{
//----------------------------------------------------------
// logstream.h包含在stdhdrs.h中,因此也包含在所有cpp文件中。
// 如果直接调用Log接口需要包含log.h文件。这样使文件的依赖性
// 大大提高,如果修改Log接口会导致编译所有cpp文件。
// 这里调用全局函数,可以不用包含log.h头文件,
// 对log接口的修改不会导致其他cpp文件的重新编译,从而
// 降低文件的依赖性提高编译速度。
// 在release版本中,可以考虑包含log.h头文件从而直接调用Log接口
// 省去一个函数调用。但是另一方面,插件程序不依赖于log.h,
// 如果要在插件中使用本文件,就必须做条件编译,更加麻烦。
// 因此这里为简明起见,只调用一个全局函数,通过在主程序和
// 插件程序里对该函数的不同实现调用ILog接口
//----------------------------------------------------------
//----------------------------------------------------------
// LogMessage::write()中不能抛出异常,因为在析构函数中抛出
// 异常是不安全的。具体讨论见"More exceptional C++"一书。
//----------------------------------------------------------
const char* ptr = this->str();
this->freeze( false );
message.write( ptr, this->pcount() ); // never throw
}
// __FILE__, __LINE__是ANSI C标准,__FUNCTION__是VC扩展,在vc.net中才有
#ifndef __FUNCTION__
#define __FUNCTION__ NULL
#endif // __FUNCTION__
#ifdef _DEBUG
#define logdbg(message) do{ _logdbg << message; }while(0)
#else // NDEBUG
#define logdbg(message) do{}while(0)
#endif // _DEBUG
//--------------------------------------------------------
// 以下define定义了几个临时变量。这些临时变量在语句结束
// 时析构,导致调用logstream
// 中调用write_log()接口写日志
//--------------------------------------------------------
//----------------------------------------------------------
// 注意以下定义的log接口都是一些临时变量,C++标准草案中
// 临时变量是r-value,不能有non-const引用,
// 因此在VC.NET 7.1中你会看到loginfo << "X"调用的是成员操作符
// ostream::operator<<(const void*),而不是非成员的
// operator<<(ostream&,const char*),这导致loginfo << "X"输出
// 一个内存地址,而非期望的字符串。
// 我这里的解决方法是:由于r-value可以调用成员函数,成员函数
// 可以返回non-const引用,因此这里的log接口是临时变量
// non-const引用。另一种解决方法是这样用:
// const_cast
// VC 6.0和VC .NET 7.0对临时变量缺省选择non-const引用(但不
// 符合C++规范),因此没有这个问题。
// 关于临时变量更详细的讨论参见这里:
// http://groups.google.com/groups?hl=zh-CN&lr=&ie=UTF-8&threadm=77q8ju8cqfg11td4qnn24i9unqp54801in%404ax.com&rnum=1&prev=/groups%3Fselm%3D77q8ju8cqfg11td4qnn24i9unqp54801in%25404ax.com
//----------------------------------------------------------
//--------------------------------------------------------
// 致命错误。程序即将退出
//--------------------------------------------------------
#define logexit logstream( LL_FATALERR, __LINE__, __FILE__, __FUNCTION__ ).l_value()
//--------------------------------------------------------
// 一般错误。某个模块功能错误,但其它部模块不受影响,
// 程序还能继续工作。
//--------------------------------------------------------
#define logerr logstream( LL_ERROR, __LINE__, __FILE__, __FUNCTION__ ).l_value()
//--------------------------------------------------------
// 警告信息。需要提醒用户注意的信息,比如对某个接口传递的
// 调用参数不正确,内存溢出,或设备驱动收到一个不能识别的参数。
// 警告和一般错误的区别在于一般错误是程序的逻辑出现问题,
// 警告则是程序本身的自我保护,是正确的逻辑
//--------------------------------------------------------
#define logwarn logstream( LL_WARNING, __LINE__, __FILE__, __FUNCTION__ ).l_value()
//--------------------------------------------------------
// 一般信息。报告目前的状态
//--------------------------------------------------------
#define loginfo logstream( LL_INFO, __LINE__, __FILE__, __FUNCTION__ ).l_value()
//--------------------------------------------------------
// 调试信息。有助于程序员调试使用的信息。
//--------------------------------------------------------
#define _logdbg logstream( LL_DBGINFO, __LINE__, __FILE__, __FUNCTION__ ).l_value()
#endif // __LOG_STREAM_H__分类: C/C++
基于WEB数据库的应用
c盘的另类减肥
MSComm中Variant变量的应用问题。
C定义变量的问题
c语言的指针变量
变量的变化??c语言
ftp另类整合应用
FTP另类应用
另类智慧的重要性以及伟人应用另类智慧的实例
临时变量空间是什么概念?
javascritp变量应用问题
基于模糊AHP法的水质评价模型及其应用
特尔斐法(专家意见法)基于网络的应用
特尔斐法(即专家意见法)基于网络的应用
基于构件应用开发的优点有哪一些啊
讨论以下问题:基于Web应用的构件
基于SNMP++的阻塞模式收发应用研究
基于网络应用的设计服务都有哪些?
追问:powerbuild一般用于开发基于什么语言的应用?
关于c里面static变量的问题
C:\Program Files的变量是什么?
C语言 有关变量名的问题
( )是C语言合法的变量名?
C语言函数,变量命名的问题