代码之家  ›  专栏  ›  技术社区  ›  Evan Teran

在变量中存储异常的正确方法

  •  7
  • Evan Teran  · 技术社区  · 16 年前

    std::exception

    因为捕捉在一个库或线程中抛出的异常并在另一个库或线程中捕捉它可能会导致未定义的行为(至少Qt会抱怨它,并且在许多上下文中不允许它)。我想将库调用包装在函数中,这些函数将返回一个状态码,如果发生异常,则返回异常对象的副本。

    最好的方法是什么 商店 一个例外(它的多态行为)供以后使用?我相信c++0x-future API使用了这样的东西。那么最好的方法是什么呢?

    我能想到的最好的办法就是 clone() 方法,该方法将返回指向同一类型异常的指针。但这不是很一般,根本不涉及标准例外。

    有什么想法吗?

    编辑 :似乎c++0x将 a mechanism for this . 它被描述为“图书馆的魔力”。这是否意味着is不需要c++0x的任何语言特性?如果没有,是否有与c++03兼容的实现?

    编辑 implementation of exception copying . 我会把这个问题留给任何不愿意回答的人 boost::copy_exception 答案。

    编辑 :解决j琰u random琰u hacker对异常的根本原因是内存不足错误的担忧。对于这个特定的库和一组异常,情况并非如此。从根异常对象派生的所有异常表示由无效用户输入引起的不同类型的分析错误。与内存相关的异常只会导致 std::bad_alloc 单独处理的。

    5 回复  |  直到 11 年前
        1
  •  5
  •   Edward Strange    16 年前

    你有什么会是我认为你最好的,唯一的答案。不能保留对原始异常的引用,因为它将离开作用域。您只需复制它,唯一通用的方法是使用类似clone()的原型函数。

        2
  •  9
  •   Steven W. Klassen    7 年前

    在C++ 11中,可以使用STD:ExabutyPPTR来完成。

    (如果底层线程实现是POSIX线程,我在使std::thread可中断的类中使用它。为了处理可能在用户代码中抛出的异常(如果在我的实现的某个关键部分中抛出,则会导致问题),我使用std::exception_ptr存储异常,然后在关键部分完成后再抛出。)

    要存储异常,需要捕获它并将其存储在ptr变量中。

    std::exception_ptr eptr;
    try {
        ... do whatever ...
    } catch (...) {
        eptr = std::current_exception();
    }
    

    然后,您可以将eptr传递到任何您喜欢的地方,甚至传递到其他线程(根据文档-我自己还没有尝试过)。当需要再次使用(即抛出)它时,您将执行以下操作:

    if (eptr) {
        std::rethrow_exception(eptr);
    }
    

    如果要检查异常,只需捕获它。

    try {
        if (eptr) {
            std::rethrow_exception(eptr);
        }
    } catch (const std::exception& e) {
        ... examine e ...
    } catch (...) {
        ... handle any non-standard exceptions ...
    }
    
        3
  •  1
  •   Tyler McHenry    16 年前

    你可以扔任何东西,包括指针。你可以这样做:

    throw new MyException(args);
    

    然后在异常处理程序中存储捕获的指针,它将是完全多态的(下面假设 MyException std::exception ):

    try {
    
       doSomething(); // Might throw MyException*
    
    } catch (std::exception* pEx) {
    
       // store pEx pointer
    }
    

    这样做时,只需小心内存泄漏,这就是通常使用按值抛出和按引用捕获的原因。

    http://www.parashift.com/c++-faq-lite/exceptions.html#faq-17.8

        4
  •  0
  •   Kirill V. Lyadvinsky    16 年前

    捕获在一个库中抛出的异常并在另一个库中捕获它会导致未定义行为的原因是这些库可以与不同的运行时库链接。如果从函数返回异常而不是抛出异常,则无法避免该问题。

        5
  •  0
  •   Dennis Zickefoose    14 年前

    我的实用程序库有一个 AnyException 基本上和 boost::any 没有铸造支架。相反,它有一个 Throw() 抛出存储的原始对象的成员。

    struct AnyException {
      template<typename E>
      AnyException(const E& e) 
        : instance(new Exception<E>(e))
      { }
    
      void Throw() const {
        instance->Throw();
      }
    
    private:
      struct ExceptionBase {
        virtual void Throw() const =0;
        virtual ~ExceptionBase() { }
      };
    
      template<typename E>
      struct Exception : ExceptionBase {
        Exception(const E& e)
          : instance(e)
        { }
    
        void Throw() const {
          throw std::move(instance);
        }
    
      private:
        E instance;
      };
      ExceptionBase* instance;
    };
    

    这是一个简化,但这是基本框架。我的实际代码禁用了复制,取而代之的是移动语义。如果需要,可以添加虚拟 Clone ExceptionBase 很容易。。。自从 Exception 克隆 方法。

    std::bad_alloc 并直接存储在这些情况下。

    struct AnyException {
       template<typename E>
       AnyException(const E& e) {
          try {
              instance.excep = new Exception<E>(e);
              has_exception = true;
          } catch(std::bad_alloc& bad) {
              instance.bad_alloc = bad;
              bas_exception = false;
          }
       }
    
       //for the case where we are given a bad_alloc to begin with... no point in even trying
       AnyException(const std::bad_alloc& bad) {
         instance.bad_alloc = bad;
         has_exception = false;
       }
    
       void Throw() const {
         if(has_exception)
             instance.excep->Throw();
         throw instance.bad_alloc;
       }
    
     private:
       union {
         ExceptionBase* excep;
         std::bad_alloc bad_alloc;
       } instance;
       bool has_exception;
     };
    

    实际上我根本没试过第二点。。。我可能错过了一些显而易见的东西,这将阻止它的工作。

    推荐文章