POCO C++库学习和分析 -- 异常、错误处理、调试

1. 异常处理

C++同C语言相比,提供了异常机制。通过使用try,catch关键字可以捕获异常,这种机制使得程序员在程序异常发生时,可以通过判断异常类型,来决定程序是否继续执行,并在程序结束之前优雅的释放各类资源。当然对于C++的异常机制也存在着很多的争议。在这里,并不对此展开讨论,只介绍一下Poco中的异常类。

Poco中的异常类:
        1. 所有的异常类都是Poco::Exception的子类。
        2. Poco::Exception继承自std::exception类。
        3. Foundation库中涉及的异常类,包括了下面一些:
                   a) Poco::LogicException类负责处理程序错误,包括了:
                           AssertionViolationException
                          NullPointerException
                           NullValueException
                           BugcheckException
                           InvalidArgumentException
                           NotImplementedException
                           RangeException
                           IllegalStateException
                           InvalidAccessException
                           SignalException
                           UnhandledException
                   b) Poco::ApplicationException类负责处理应用程序相关的错误,即使用Poco库的用户自定义异常。
                   c) Poco::RuntimeException类负责处理程序运行时的错误,包括了:
                           RuntimeException
                           NotFoundException
                           ExistsException
                           TimeoutException
                           SystemException
                           RegularExpressionException
                           LibraryLoadException
                           LibraryAlreadyLoadedException
                           NoThreadAvailableException
                           PropertyNotSupportedException
                           PoolOverflowException
                           NoPermissionException
                           OutOfMemoryException
                           DataException
                           DataFormatException
                           SyntaxException
                           CircularReferenceException
                           PathSyntaxException
                           IOException
                           ProtocolException
                           FileException
                           FileExistsException
                           FileNotFoundException
                           PathNotFoundException
                           FileReadOnlyException
                           FileAccessDeniedException
                           CreateFileException
                           OpenFileException
                           WriteFileException
                           ReadFileException
                           UnknownURISchemeException

        成员函数及数据定义:
        1. Poco::Exception包括了一个名字,这是一个静态的字符串,用来描述异常本身。比如说LogicException名字为"Logic exception",TimeoutException名字为"Timeout"。
        2. Poco::Exception还包含了一个字符串消息,这是用来进一步描述异常的。使用的的人可以在运行时定义它。比如都是LogicException异常,函数一处抛出异常时可定义为"Function1",函数二处抛出时异常时可定义为用"Function2",它可以用来说明异常发生的具体位置和原因。
        3. 一个可选的嵌套异常类
        4. 构造函数:
                   a) 可以使用0个,1个或2个字符串参数来构造异常。在Poco::Exception内部存储的时候,第二个字符串会使用字符":"和第一个字符串串联。
                   b) 构造时如果使用了字符串和嵌套异常的方式,嵌套异常会被复制一份。
        5. Poco::Exception支持拷贝和赋值运算符
        6. const char* name()
                   返回异常的名称
        7. const std::string& message()
                   返回在构造时传入的消息字符串
        8. std::string displayText() const
                   同时返回异常名字和消息字符串,中间使用": "分隔
        9. const Exception* nested() const
                   如果存在嵌套异常的话,返回之歌指向嵌套异常的指针,否则返回0
        10. Exception* clone() const
                   返回一个异常的拷贝
        11. void rethrow() const
                   重新抛出异常

        定义自己的异常:
        因为从Poco::Exception继承,去定义自己的异常时,工作非常的枯燥且重复(用户需要重载大量的虚函数),在库中提供了两个宏来完成这个工作:
                   POCO_DECLARE_EXCEPTION:用来申明异常宏
                   POCO_IMPLEMENT_EXCEPTION: 用来定义异常宏的执行体

两个宏分别定义如下:

// MyException.h
#include "Poco/Exception.h"
POCO_DECLARE_EXCEPTION(MyLib_API, MyException, Poco::Exception)
// MyException.cpp
#include "MyException.h"POCO_IMPLEMENT_EXCEPTION(MyException, Poco::Exception,"Something really bad happened...")

宏展开分别为:

// MyException.h
#include "Poco/Exception.h"
POCO_DECLARE_EXCEPTION(MyLib_API, MyException, Poco::Exception)
class MyLib_API MyException: public Poco::Exception
{
public:MyException();MyException(const std::string& msg);MyException(const std::string& msg, const std::string& arg);MyException(const std::string& msg, const Poco::Exception& nested);MyException(const MyException& exc);~MyException();MyException& operator = (const MyException& exc);const char* name() const;...
};
// MyException.cpp
#include "MyException.h"
POCO_IMPLEMENT_EXCEPTION(MyException, Poco::Exception,
"Something really bad happened...")
...
const char* MyException::name() const throw()
{return "Something really bad happened...";
}
...

下面是一个例子:

#include "Poco/Exception.h"
#include <iostream>
int main(int argc, char** argv)
{Poco::Exception* pExc = 0;try{throw Poco::ApplicationException("just testing");}catch (Poco::Exception& exc){pExc = exc.clone();}try{pExc->rethrow();}catch (Poco::Exception& exc){std::cerr << exc.displayText() << std::endl;}delete pExc;return 0;
}

2. 断言

POCO库中提供了一些断言的宏来进行运行时检查,这些断言能够提供出错代码的行号和文件信息。
        1. Debugger::_assert(cond)
         如果cond ≠ true时,抛出一个AssertionViolationException异常。
        2. poco_assert_dbg(cond)
           同poco_assert类似,但是只在debug模式下起作用
        3. poco_check_ptr(ptr)
           如果ptr为空,则抛出NullPointerException异常
        4. poco_bugcheck(), poco_bugcheck_msg(string)
           抛出BugcheckException异常

POCO的断言类在debug调试模式下(比如在Visual C++)中时,会触发一个breakpoint。比如:

void foo(Bar* pBar)
{poco_check_ptr (pBar);...
}
void baz(int i)
{poco_assert (i >= 1 && i < 3);switch (i){case 1:...break;case 2:...break;default:poco_bugcheck_msg("i has invalid value");}
}

这主要是因为Poco中的断言类是通过Poco::Debugger去实现的,在Poco::Debugger底层调用了不同操作系统的API,去判断程序是否处于调试状态。如VC下,调用了

BOOL WINAPI IsDebuggerPresent(VOID);
VOID WINAPI DebugBreak(VOID);

3. NDC(Nested Diagnostic Context)

3.1  概述

NestedDiagnosticContext是为了多线程诊断而设计的。我们在写程序时,一般都需要同时处理多个线程。为了更加便捷的处理多线程情况,为每个线程产生各自的日志。Neil Harrison 在他的书中" Patterns for Logging Diagnostic Messages," in Pattern Languages of Program Design 3, edited by R. Martin, D. Riehle, and F. Buschmann (Addison-Wesley, 1997) 中提出了一个方法。独特地标记每个日志请求,用户把上下文信息送入NDC,NDC是 Nested Diagnostic Context的缩写。在这本书里提到了3种日志方法,分别是:
        1. DiagnosticLogger
         分离日志和程序其他模块
        2. TransactionalBuckets
        事务桶,为事务单独建立日志
        3. TypedDiagnostics
        类型化诊断,为所有的诊断信息提供统一的展现

我们还是回到Poco中的NDC上。在Poco中和NDC相关的内容包括了,NestedDiagnosticContext类,NDCScope类,宏poco_ndc和poco_ndc_dbg。其中NestedDiagnosticContext类维护一个NDC对象,其中包括了上下文的栈信息,有函数方法名,源文件代码文件名,行号。宏poco_ndc(func) or poco_ndc_dbg(func)申明了一个NDCScope对象。而NDCScope对象则完成了上下文的入栈工作。下面是一个例子:

#include "Poco/NestedDiagnosticContext.h"
#include <iostream>
void f1()
{poco_ndc(f1);Poco::NDC::current().dump(std::cout);
}
void f2()
{poco_ndc(f2);f1();
}
int main(int argc, char** argv)
{f2();return 0;
}

3.2 实现

3.2.1 线程本地存储

在Poco中实现时,用了一些小技巧,即线程本地存储。我们来看Poco中TLS的类图:

CurrentThreadHolder类是TLS实现的具体类,在每个Thread对象中包含了一个CurrentThreadHolder对象。Thread创建的时候,CurrentThreadHolder会调用不同操作系统的API函数,获取并保存一个固定槽位,用于保存Thread对象的指针。
        每个Thread对象中还包含了一个ThreadLocalStorage对象。ThreadLocalStorage类用于保存具体的线程信息数据,它是一个TLSSlot对象的集合。通过泛型实现TLSSlot后,ThreadLocalStorage可用于保存任何数据的。

使用了TLS技术后,调用Thread的静态函数current可以获取到每个线程对象Thread的指针,然后再通过这个Thread对象的指针,可以获取到ThreadLocalStorage对象,并最终获取或保存数据于TLSSlot中。

通过类的静态函数获取类实例的指针,在C++中是不存在的,这需要操作系统支持,只有Thread对象才能做到这一点。

3.2.2 NDC

在来看一张Poco中NDC类的类图:

使用者通过调用宏poco_ndc和poco_ndc_dbg,来构建一个NDCScope对象。宏定义如下:

#define poco_ndc(func) \Poco::NDCScope _theNdcScope(#func, __LINE__, __FILE__)#if defined(_DEBUG)#define poco_ndc_dbg(func) \Poco::NDCScope _theNdcScope(#func, __LINE__, __FILE__)
#else#define poco_ndc_dbg(func)
#endif

NDCScope实现了诊断信息上下文的入栈出栈工作,它通过调用NestedDiagnosticContext类的静态函数current实现了此功能。其定义如下:

inline NDCScope::NDCScope(const std::string& info)
{NestedDiagnosticContext::current().push(info);
}inline NDCScope::NDCScope(const std::string& info, int line, const char* filename)
{NestedDiagnosticContext::current().push(info, line, filename);
}inline NDCScope::~NDCScope()
{NestedDiagnosticContext::current().pop();
}

NestedDiagnosticContext类的current()是个静态函数,其定义如下:

namespace
{static ThreadLocal<NestedDiagnosticContext> ndc;
}NestedDiagnosticContext& NestedDiagnosticContext::current()
{return ndc.get();
}

而ThreadLocal是一个辅助类,用于获取线程对象的本地存储信息或者是主线程的本地存储信息。

template <class C>
class ThreadLocal/// This template is used to declare type safe thread/// local variables. It can basically be used like/// a smart pointer class with the special feature/// that it references a different object/// in every thread. The underlying object will/// be created when it is referenced for the first/// time./// See the NestedDiagnosticContext class for an/// example how to use this template./// Every thread only has access to its own/// thread local data. There is no way for a thread/// to access another thread's local data.
{typedef TLSSlot<C> Slot;public:ThreadLocal(){}~ThreadLocal(){}C* operator -> (){return &get();}C& operator * ()/// "Dereferences" the smart pointer and returns a reference/// to the underlying data object. The reference can be used/// to modify the object.{return get();}C& get()/// Returns a reference to the underlying data object./// The reference can be used to modify the object.{TLSAbstractSlot*& p = ThreadLocalStorage::current().get(this);if (!p) p = new Slot;return static_cast<Slot*>(p)->value();}private:ThreadLocal(const ThreadLocal&);ThreadLocal& operator = (const ThreadLocal&);
};

到这里Poco中所有的NDC流程都被打通了,用户终于可以实现按线程打印日志信息了。

(版权所有,转载时请注明作者和出处  http://blog.csdn.net/arau_sh/article/details/8698353)

转载于:https://www.cnblogs.com/napu/archive/2013/04/08/5375887.html

POCO C++库学习和分析 -- 异常、错误处理、调试相关推荐

  1. POCO C++库学习和分析

    POCO C++库学习和分析 -- 序 1. POCO库概述: POCO是一个C++的开源库集.同一般的C++库相比,POCO的特点是提供了整一个应用框架.如果要做C++程序应用框架的快速开发,我觉得 ...

  2. POCO C++库学习和分析 -- 序

    POCO C++库学习和分析 -- 序 1. POCO库概述: POCO是一个C++的开源库集.同一般的C++库相比,POCO的特点是提供了整一个应用框架.如果要做C++程序应用框架的快速开发,我觉得 ...

  3. POCO C++库学习和分析 -- 字符编码

    POCO C++库学习和分析 -- 字符编码 1. 字符编码 1.1 字符编码的概念 字符编码可以理解为在计算机上语言符号和二比特数之间的映射.不同的编码方式对应着不同映射方法,对于映射集的双方而言, ...

  4. poco,c++库学习,日期和时间

     POCO C++库学习和分析 -- 日期与时间 在Poco库中,与时间和日期相关的一些类,其内部实现是非常简单的.看相关文档时,比较有意思的倒是历史上的不同时间表示法. 1. 系统时间函数    ...

  5. Python学习笔记__8章错误、调试和测试__8.1章错误处理

    # 这是学习廖雪峰老师python教程的学习笔记 1.概览 我们在运行程序或编写函数时,发生错误后,系统都会返回错误信息.我们可以通过某些机制,让错误信息更加明了 1.1.try try机制的格式就是 ...

  6. 【Python之pymysql库学习】一、分析fetchone()、fetchmany()、fetchall()(保姆级图文+实现代码)

    目录 实现效果 实现思路 实现代码 总结 欢迎关注 『Python之pymysql库学习』 系列,持续更新中 欢迎关注 『Python之pymysql库学习』 系列,持续更新中 实现效果 实现思路 其 ...

  7. 已解决(Python爬虫requests库报错 请求异常SSL错误,证书认证失败问题)requests.exceptions.SSLError: HTTPSConnectionPool

    成功解决(Python爬虫requests库报错 请求异常,SSL错误,证书认证失败问题)requests.exceptions.SSLError: HTTPSConnectionPool(host= ...

  8. PHP - 异常/错误 - 学习/实践

    1.应用场景 主要用于了解熟悉并正确使用PHP异常处理机制. 2.学习/操作 1. 文档阅读 PHP Exception - php完全自学手册 - php中文网手册 深入探讨 PHP 错误异常处理机 ...

  9. muduo网络库学习总结:基本架构及流程分析

    muduo网络库学习:基本架构及流程分析 基本架构 Basic Reactor Mutiple Reactor + ThreadPool muduo库的基本使用 基本结构介绍 EventLoop类 P ...

最新文章

  1. HOG和SIFT图像特征提取简述
  2. .NET中的按需加载/延迟加载 LazyT
  3. matlab确定污染源位置,确定污染源的位置
  4. 理解Flex itemRenderer(3)--通信
  5. VxWorks/tornado环境搭建(Win 7 64位 corei5下试验成功)
  6. Zimbra高级应用之-双向证书认证(一)
  7. 别问,问就是我差点在所有浏览器中注入 JavaScript 代码
  8. Jzoj5662 尺树寸泓
  9. CPU内存管理和linux内存分页机制
  10. Python20行代码爬取搞笑图片—拯救你的不开心
  11. openwrt路由器变网桥设备交换机,还能当服务器部署NAS私有云
  12. GAN之野狼DiscoGAN
  13. 真正的小说 真正的生活 真正的蜕变 真正的品味
  14. 开关电源matlab仿真文件,《MATLAB+在开关电源仿真中的应用.pdf》-支持高清全文免费浏览-max文档...
  15. HTML5中引入字体样式的常用方法-Iconfont(阿里巴巴矢量图库)和IcoMoon-APP
  16. 05.敬业、牺牲与奋斗
  17. Android之主题皮肤实现
  18. CAS: 1260119-01-4, NO2-UIO-66, UIO-66-NO2
  19. Java虚拟机的基本结构
  20. 2042:【例5.10】稀疏矩阵

热门文章

  1. vscode中文配置中文插件
  2. 用正则表达式提取富文本内容(去除html,p,a等标签)
  3. 智能照明控制系统在城市夜景照明工程中的应用
  4. 【FPGA创新设计竞赛——2022紫光同创杯】1、“基于 RISC-V 处理器的软硬件系统设计”赛题介绍
  5. java 向量存储_关于java:使用AES-256和初始化向量进行加密
  6. 微信小程序之五星评分效果
  7. Ubuntu支持LinuxONE大型机:为云而生的强强新组合
  8. 关于MP4视频在浏览器上无法播放的问题(没有找到支持的视频格式和mime类型)
  9. 减少mysql存储列的方法
  10. Odoo16 主题推荐