2009年3月2日星期一

C++中的程序跟踪方法

在应用开发构成中,我们经常在程序中加入一些打印语句,来对程序的执行流进行跟踪.在C或C++中可以利用下列语句来实现:
(1)
printf("enter %s\n",(char *)funcName);

cout<<"enter "<< s_funcName << endl;

但这样处理有点不足,就是该语句只输出到标准输出上,我有时希望这些输出被定向到特定文件,输出成日志.为此,我们可以把这些函数进行包装,把输出流ostream(标准输出或文件输出)作为包装函数的一个参数:
(2)
printWrap(ostream out,format, args);注:此处的args, format表示要输出的参数和相应的参数格式.
当然我们还可以对它进行进一步的改进:在该函数中,加入预定以的符号常量__LINE__(当前源代码行的行号,为整数常量),__FILE__(假定的源文件名,某个字符串).这样我们可以知道程序运行到了那个源文件,并且那一行.

现在(2)中的处理方式比(1)中处理方式已经有明显的改善了.但这种方式还稍微有点不足.当我们想要跟踪一个函数的执行,即知到执行流进入某函数,何时离开某函数时,这种处理方式有点不足.每个函数都有一个入口,但可能有多个出口,这样就需要在每个入口和出口处加上printWrap(ostream out,args)语句,并且在C++中,当执行流遇到异常退出该函数时,可能有些printWrap语句并没有被执行,从而没有输出记录.

为此,我们可以对(2)进行进一步改进.我们可以设计一个类,在该类对象的构造函数,析构函数中进行输出.在函数的入口处,调用对象的构造函数进行输出;在函数的出口处,或异常退出时,调用对象的析构函数进行输出.
我们可以把该类简单总结如下:
(3)
class Trace{
public:
Trace(int iDebugLevel,ostream out, format,args) { cout <<"Hello\n";}
~Trace() { cout << " Goodby\n";}
int getDebugLevel();
private:
...
int iDebugLevel;
ostream m_out;
};

注: 我们可以用printWrap(..)替换cout << ....。printWrap中的输出流在Trace的构造函数中传到Trace实例中,并被保存。

我们还可以对它进行一点改进,以提高它的性能。因为采用上面的对象。则每次都会进行输出或进行日志记录.我们可以通过构造函数在Trace的实例中,设置一个iDebugLevel变量和ostream。并在系统中设置一个统一的debugLevel.在每次进行输出时进行iDebugLevel, debugLevel比较,如果iDebugLevel <= debugLevel, 则进行输出,否则则不进行输出.

C++中的程序跟踪方法

在应用开发构成中,我们经常在程序中加入一些打印语句,来对程序的执行流进行跟踪.在C或C++中可以利用下列语句来实现:
(1)
printf("enter %s\n",(char *)funcName);

cout<<"enter "<< s_funcName << endl;

但这样处理有点不足,就是该语句只输出到标准输出上,我有时希望这些输出被定向到特定文件,输出成日志.为此,我们可以把这些函数进行包装,把输出流ostream(标准输出或文件输出)作为包装函数的一个参数:
(2)
printWrap(ostream out,format, args);注:此处的args, format表示要输出的参数和相应的参数格式.
当然我们还可以对它进行进一步的改进:在该函数中,加入预定以的符号常量__LINE__(当前源代码行的行号,为整数常量),__FILE__(假定的源文件名,某个字符串).这样我们可以知道程序运行到了那个源文件,并且那一行.

现在(2)中的处理方式比(1)中处理方式已经有明显的改善了.但这种方式还稍微有点不足.当我们想要跟踪一个函数的执行,即知到执行流进入某函数,何时离开某函数时,这种处理方式有点不足.每个函数都有一个入口,但可能有多个出口,这样就需要在每个入口和出口处加上printWrap(ostream out,args)语句,并且在C++中,当执行流遇到异常退出该函数时,可能有些printWrap语句并没有被执行,从而没有输出记录.

为此,我们可以对(2)进行进一步改进.我们可以设计一个类,在该类对象的构造函数,析构函数中进行输出.在函数的入口处,调用对象的构造函数进行输出;在函数的出口处,或异常退出时,调用对象的析构函数进行输出.
我们可以把该类简单总结如下:
(3)
class Trace{
public:
Trace(int iDebugLevel,ostream out, format,args) { cout <<"Hello\n";}
~Trace() { cout << " Goodby\n";}
int getDebugLevel();
private:
...
int iDebugLevel;
ostream m_out;
};

注: 我们可以用printWrap(..)替换cout << ....。printWrap中的输出流在Trace的构造函数中传到Trace实例中,并被保存。

我们还可以对它进行一点改进,以提高它的性能。因为采用上面的对象。则每次都会进行输出或进行日志记录.我们可以通过构造函数在Trace的实例中,设置一个iDebugLevel变量和ostream。并在系统中设置一个统一的debugLevel.在每次进行输出时进行iDebugLevel, debugLevel比较,如果iDebugLevel <= debugLevel, 则进行输出,否则则不进行输出.

2009年3月1日星期日

C++中的对象同步方法

在C++中,通过提供构造函数、析构函数来对处理资源的获取、释放。
通过C++的这种机制,我们可以很方便地处理C++中的加锁同步机制。把锁对象作为Guard对象的一个成员(m_lock),然后在Guard对象的构造中对m_lock进行加锁:m_lock.acquire(),在Guard对象的析构函数中进行解锁:m_lock.release()。先给出代码实例如下:
template class Guard{public : Guard(const T & lock); virtual ~Guard();
private: const T & m_lock;};
template Guard::Guard(const T & lock) : m_lock(lock){ m_lock.acquire();}
template Guard::~Guard(){ m_lock.release();}
我们可以在应用程序中这样使用它:
void testFunc(.....)
{
Guard guard(mutex);
...
}
在刚进入函数testFun(...),创建guard对象,并自动对mutex进行加锁,对特定数据(resource)进行保护。当应用离开testFunc函数模块时,根据guard对象的作用域和生命周期,此时guard对象的析构函数将被调用,因此将自动对mutex进行解锁。在此之后应用的其他线程将可以访问以前被mutex进行保护起来的资源。

利用上面的方法,我们可以包对资源的同步访问和访问控制交给C++的编译器,而不需要进行人工干预,从而减轻应用开发人员的工作负担。