c ++推导出嵌套异常的类型

Ric*_*ges 10 c++ exception c++11 c++14 c++17

介绍:

给定:

struct X : std::runtime_error {
  using std::runtime_error::runtime_error;
};
Run Code Online (Sandbox Code Playgroud)

当我们打电话时std::throw_with_nested(X("foo")),实际抛出的不是一个X.它是某种类型,源自Xstd::nested_exception.

因此,以下断言将失败:

const std::type_info *a = nullptr, *b = nullptr;
try
{
  throw X("1");
}
catch(X& x) {
  a = std::addressof(typeid(x));
  try {
    std::throw_with_nested(X("2"));
  }
  catch(X& x) {
    b = std::addressof(typeid(x));
  }
}
assert(std::string(a->name()) == std::string(b->name()));
Run Code Online (Sandbox Code Playgroud)

我想做的是推断这两个例外是相关的.

第一次尝试:

        std::type_index
        deduce_exception_type(const std::exception* pe)
        {
            if (auto pnested = dynamic_cast<const std::nested_exception*>(pe))
            {
                try {
                    std::rethrow_exception(pnested->nested_ptr());
                }
                catch(const std::exception& e)
                {
                    return deduce_exception_type(std::addressof(e));
                }
            }
            else {
                return typeid(*pe);
            }
        }
Run Code Online (Sandbox Code Playgroud)

这会失败,因为std::nested_exception::nested_ptr()返回指向该行下一个异常的指针,而不是X当前异常的接口.

我正在寻找(便携式)想法和解决方案,允许我从标准库抛出的'未知名称的异常'中恢复typeid(X)std::rethrow_exception.

c ++ 14和c ++ 1z很好.

为什么?:

因为我希望能够打开一个完整的异常层次结构并在rpc会话中传输它,所以需要使用异常类型名称.

理想情况下,我不希望编写一个包含系统中每个异常类型的catch块,它必须通过派生深度进行弱排序.

预期功能的另一个例子(以及为什么我的方法不起作用的说明):

const std::type_info *b = nullptr;
try
{
  throw std::runtime_error("1");
}
catch(std::exception&) {
  try {
    std::throw_with_nested(X("2"));
  }
  catch(X& x) {
    // PROBLEM HERE <<== X& catches a std::_1::__nested<X>, which 
    //                is derived from X and std::nested_exception
    b = std::addressof(typeid(x));
  }
}
assert(std::string(typeid(X).name()) == std::string(b->name()));
Run Code Online (Sandbox Code Playgroud)

Luc*_*ton 1

一种解决方法是始终使用您自己的throw_with_nested,其中您可以注入所需的功能:

#include <typeinfo>
#include <exception>

struct identifiable_base {
    virtual std::type_info const& type_info() const = 0;
};

template<typename Exception>
struct identifiable_exception: Exception, identifiable_base {
    using Exception::Exception;

    explicit identifiable_exception(Exception base)
        : Exception(std::move(base))
    {}

    std::type_info const& type_info() const override
    {
        // N.B.: this is a static use of typeid
        return typeid(Exception);
    }
};

template<typename Exception>
identifiable_exception<std::decay_t<Exception>> make_identifiable_exception(Exception&& exception)
{ return identifiable_exception<std::decay_t<Exception>> { std::forward<Exception>(exception) }; }

// N.B.: declared with a different name than std::throw_with_nested to avoid ADL mistakes
template<typename Exception>
[[noreturn]] void throw_with_nested_identifiable(Exception&& exception)
{
    std::throw_with_nested(make_identifiable_exception(std::forward<Exception>(exception)));
}
Run Code Online (Sandbox Code Playgroud)

Live On Coliru

任何时候您需要更多功能,都可以进行调整identifiable_baseidentifiable_exception支持您想要的功能。