通过 QT 的信号/槽系统作为参数传递通用异常

Posted

技术标签:

【中文标题】通过 QT 的信号/槽系统作为参数传递通用异常【英文标题】:Pass generic exception through QT's signal/slot system as argument 【发布时间】:2015-09-05 10:05:57 【问题描述】:

当我捕获一个异常时,我想发出一个信号并将该异常传递给一个槽,以便它可以在不同的线程中处理。信号和槽使用 Qt:QueuedConnection 类型连接。

我第一次尝试这个:

/*in .h*/
signals:
    void exceptionOccured(const std::exception &e);
public slots:
    void exceptionReceive(const std::exception &e);

/*in .cpp*/
    connect(this,SIGNAL(exceptionOccured(const std::exception&)),this,SLOT(exceptionReceive(const std::exception&)),Qt::QueuedConnection);

try 
    throw std::runtime_error("test");
 catch (std::exception &e) 
    emit exceptionOccured(e);

这有点工作,但是当 QT 将引用对象的副本放置在队列中时,exceptionReceived 插槽接收到 std::exception 类型的对象并且所有附加信息都丢失了。

然后我尝试传递一个指向原始异常对象的指针,但这给了我一个问题,即当插槽被调用时,实际对象已经消失了。

如何告诉 QT 制作异常的正确副本(实际应用程序从 std::exception 继承了多个级别)或将捕获的异常转换为某种安全指针类型(即从堆栈到堆左右)。

附: 对象将消息发送给自己,因为 try/catch 语句是使用 QtConcurrent::run() 从 lambda 函数运行的;

【问题讨论】:

您可以将std::exception 包裹在一个类中,该类将对必要数据进行深度复制。 如果您使用的是 C++11,那么 std::exception_ptr 会有所帮助。 @AlanStokes 是的,我使用 C++11 并且使用 std::current_exception() 确实允许我在另一个线程中使用异常。如果你把它作为一个答案,那么我可以标记它。 【参考方案1】:

std::exception_ptr被添加到标准 C++11 中,以解决这个问题。 std::current_exception() 可让您获得当前异常的句柄,然后您可以将其传递并在您想要的任何线程中重新抛出它。

详情请见http://en.cppreference.com/w/cpp/error/exception_ptr。

【讨论】:

你确定它会一直有效吗? ref。 “std::exception_ptr [...] 保存对该异常对象的副本或引用(如果进行了副本,则由实现定义)”。异常通常不需要是可复制的,但这通常要求它们是可复制的,并且它们的复制构造函数动作适当。此外,如果我错了,请纠正我,但有些(如果不是全部)标准异常类型是不可复制的(std::range_error)。是的,base std::exception 是,但这没有帮助。 @Kuda 我相信所有异常类型都必须是可复制的——throw x 复制 x(尽管可以省略复制)。我也相信range_error 是可复制的。我记得这里的副本是实现定义的原因是为了应对 MS 和 GCC 模型。【参考方案2】:

如果你坚持使用基本异常类型,那么这是一个 C++ 问题,而不是 Qt 问题。您需要实现某种类型的虚拟复制构造函数模式。一旦你这样做了,Qt 会自动做正确的事情。

这样一种模式:

class clonable_exception : public std::exception 
protected:
  virtual void clone_to(clonable_exception& dst) const = 0;
public:
  clonable_exception() = default;
  clonable_exception(const clonable_exception& src) 
    src.clone_to(*this);
  
  clonable_exception & operator=(const clonable_exception& src) 
    src.clone_to(*this);
  
;

所有信号和槽都应该使用clonable_exception类型而不是std::exception

【讨论】:

以上是关于通过 QT 的信号/槽系统作为参数传递通用异常的主要内容,如果未能解决你的问题,请参考以下文章

Qt 槽函数怎么传递参数

QT编程中信号与槽遇到的参数传递问题,如下

Qt信号槽如何附带自定义参数

如何通过信号和槽传递参数?

关于Qt信号与槽机制的传递方向性研究(结论其实是错误的,但是可以看看分析过程)

Qt信号槽传递自定义类型参数(qRegisterMetaType)