C++ 在C+中调度异常+;
如何调度异常,以便以集中、用户友好的方式处理错误处理和诊断 例如:C++ 在C+中调度异常+;,c++,oop,exception,C++,Oop,Exception,如何调度异常,以便以集中、用户友好的方式处理错误处理和诊断 例如: DataHW类处理与某些数据采集硬件的通信 DataHW类可能会基于一些可能的错误引发异常:间歇性信号、无信号、CRC故障、驱动程序错误。每种类型的错误都有自己的异常类 DataHW类由许多不同的代码段调用,这些代码段执行不同类型的采集和分析 正确的错误处理策略取决于异常类型和尝试的操作。(对于间歇性信号,请重试X次,然后告诉用户;对于驱动程序错误,请记录错误并重新启动驱动程序;等等)应如何调用此错误处理策略 将错误恢复编
- DataHW类处理与某些数据采集硬件的通信
- DataHW类可能会基于一些可能的错误引发异常:间歇性信号、无信号、CRC故障、驱动程序错误。每种类型的错误都有自己的异常类
- DataHW类由许多不同的代码段调用,这些代码段执行不同类型的采集和分析
- 将错误恢复编码到每个异常类中:这将导致异常类非常大,并且包含高级UI和系统管理代码。这似乎很糟糕
- 为每种类型的异常提供一个单独的
块:因为DataHW类是从许多不同的地方调用的,所以每个catch
块必须在每个调用站点复制。这似乎很糟糕catch
- 使用单个
块调用一些catch
函数,并使用基于RTTI的ExceptionDispatch
语句:RTTI和switch
通常表示应用OO设计失败,但这似乎是最不坏的选择switch
f()
{
try
{
// something
}
catch (...)
{
handle();
}
}
void handle()
{
try
{
throw;
}
catch (const Foo& e)
{
// handle Foo
}
catch (const Bar& e)
{
// handle Bar
}
// etc
}
我经常遇到的一个想法是,异常应该由能够处理它们的级别捕获。例如,传输数据的函数可能捕捉到CRC错误,一旦捕捉到该异常,它可能会尝试重新传输,而“无信号”异常可能会在更高级别被捕捉,并放弃或延迟整个操作 但我的猜测是,这些异常中的大多数都将围绕同一个函数捕获。单独捕获和处理它们是一个好主意(如soln#2),但您认为这会导致大量重复代码(导致soln#3) 我的问题是,如果有很多代码需要复制,为什么不把它变成一个函数呢 我的想法是
void SendData(DataHW* data, Destination *dest)
{
try {
data->send(dest);
} catch (CRCError) {
//log error
//retransmit:
data->send(dest);
} catch (UnrecoverableError) {
throw GivingUp;
}
}
我想这就像你的ExceptionDispatch()函数一样,只是它不会对异常类型进行switch
ing操作,而是将异常生成调用本身包装起来,并捕获异常
当然,这个函数过于简化了——您可能需要一个围绕DataHW的完整包装器类;但我的观点是,如果类的不同用户处理异常的方式相似,最好有一个集中的点来处理所有DataHW异常。我认为有三种方法可以解决这个问题 编写包装函数 为每个可以抛出异常的函数编写一个包装函数,该异常将处理异常。然后,所有调用方调用该包装器,而不是原始的抛出函数 使用函数对象 另一个解决方案是采用更通用的方法,编写一个函数来接受函数对象并处理所有异常。以下是一些例子:
class DataHW {
public:
template<typename Function>
bool executeAndHandle(Function f) {
for(int tries = 0; ; tries++) {
try {
f(this);
return true;
}
catch(CrcError & e) {
// handle crc error
}
catch(IntermittentSignalError & e) {
// handle intermittent signal
if(tries < 3) {
continue;
} else {
logError("Signal interruption after 3 tries.");
}
}
catch(DriverError & e) {
// restart
}
return false;
}
}
void sendData(char const *data, std::size_t len);
void readData(char *data, std::size_t len);
};
因为您提供了函数对象,所以也可以管理状态。假设sendData更新len,以便它知道读取了多少字节。然后,您可以编写读取和写入的函数对象,并维护到目前为止读取的字符数
第二种方法的缺点是无法访问抛出函数的结果值,因为它们是从函数对象包装器调用的。没有简单的方法可以获得函数对象绑定器的结果类型。一种解决方法是编写一个结果函数对象,该对象在函数对象执行成功后由executeAndHandle调用。但是,如果我们在第二种方法中投入了太多的工作,只是为了让所有的内务工作顺利进行,那么结果就不值得了
两者结合
还有第三种选择。我们可以结合这两种解决方案(包装器和函数对象)
classdatahw{
公众:
模板
R executeAndHandle(函数f){
for(int尝试=0;尝试++){
试一试{
返回f(本);
}
捕获(CrcError&e){
//处理crc错误
}
捕捉(间歇性信号错误和e){
//处理间歇信号
如果(尝试次数<3){
继续;
}否则{
日志错误(“3次尝试后信号中断”);
}
}
捕获(驱动错误和e){
//重新启动
}
//返回一个合理的默认值。对于bool,这是false。对于其他整数
//类型,它是零。
返回R();
}
}
发送数据(字符常量*数据,标准::大小长度){
返回executeAndHandle(
boost::bind(&DataHW::doSendData,_1,data,len));
}
//假设它在这个例子中返回了一些东西
T doSendData(字符常量*数据,标准::大小长度);
T数据读取数据(字符*数据,标准::大小长度);
};
技巧是
返回f()代码>模式。即使f返回void,我们也可以返回。这最终将是我的最爱,因为它既允许将句柄代码集中在一个地方,也允许在包装器函数中进行特殊处理。您可以决定是否最好将其拆分,并创建一个拥有错误处理函数和包装器的类。这可能是一个更干净的解决方案(我想到这里。一个是基本的DataHW功能,一个是错误处理)。也许您可以编写一个
void doit() {
char buf[] = "hello world";
hw.executeAndHandle(boost::bind(&DataHW::sendData, _1, buf, sizeof buf));
}
class DataHW {
public:
template<typename R, typename Function>
R executeAndHandle(Function f) {
for(int tries = 0; ; tries++) {
try {
return f(this);
}
catch(CrcError & e) {
// handle crc error
}
catch(IntermittentSignalError & e) {
// handle intermittent signal
if(tries < 3) {
continue;
} else {
logError("Signal interruption after 3 tries.");
}
}
catch(DriverError & e) {
// restart
}
// return a sensible default. for bool, that's false. for other integer
// types, it's zero.
return R();
}
}
T sendData(char const *data, std::size_t len) {
return executeAndHandle<T>(
boost::bind(&DataHW::doSendData, _1, data, len));
}
// say it returns something for this example
T doSendData(char const *data, std::size_t len);
T doReadData(char *data, std::size_t len);
};