草庐IT

c++ - 实现 C++ 异常链的正确/优雅方式?

coder 2023-06-03 原文

我想在 C++ 中实现一个 Exception 类,它模仿 .NET 框架中的类(Java 也有类似的东西),用于以下目的:

  • 异常链:我想实现“异常翻译”的概念,当在更高级别捕获的异常包装和“翻译”较低级别的异常时,还以某种方式保留这些情人级别的异常(在 InnerException 成员中,在这个案例)。为此,应该有一些机制来存储内部异常以及在上层抛出的每个异常。 InnerException成员在下面的实现中提供了这一点。
  • 异常继承:应该可以派生IoException来自 Exception , 和 SerialPortException来自 IoException , 例如。虽然这看起来微不足道,但应该能够动态识别捕获异常的类型(例如用于日志记录或向用户显示),最好没有 RTTI 和 typeid 的开销。 .

  • 这是我想要实现的示例异常处理逻辑:
    try
    {
        try
        {
            try
            {
                throw ThirdException(L"this should be ThirdException");
            }
            catch(Exception &ex)
            {
                throw SubException(L"this should be SubException", ex);
            }
        }
        catch(Exception &ex)
        {
            throw SubException(L"this should be SubException again", ex);
        }
    }
    catch(Exception &ex)
    {
        throw Exception(L"and this should be Exception", ex);
    }
    

    在最上层捕获“最外层”异常时,我希望能够通过 InnerException 解析和格式化整个异常链成员,显示如下内容:



    到目前为止,我已经提出了以下实现:

    小记:CString是 Microsoft 特定的字符串类(仅适用于不熟悉 Visual C++ 内容的人)。
    class Exception
    {
    protected:
    
        Exception(const Exception&) {};
        Exception& operator= (const Exception&) {};
    
    public:
    
        Exception(const CString &message) : InnerException(0), Message(message) {}
        Exception(const CString &message, const Exception &innerException) : InnerException(innerException.Clone()), Message(message) {}
    
        virtual CString GetExceptionName() const { return L"Exception"; }
    
        virtual Exception *Clone() const
        {
            Exception *ex = new Exception(this->Message);
            ex->InnerException = this->InnerException ? this->InnerException->Clone() : 0;
            return ex;
        }
    
    public:
    
        virtual ~Exception() { if (InnerException) delete InnerException; }
    
        CString Message;
        const Exception *InnerException;
    };
    

    现在我们这里有什么。复制构造函数和赋值运算符 protected以防止复制。每个对象都将“拥有”其内部异常对象(并在析构函数中将其删除),因此默认的浅拷贝将是 Not Acceptable 。然后我们有两个看起来很标准的构造函数和删除 InnerException 的虚拟析构函数。目的。 Clone()虚方法负责对对象进行深度复制,主要用于存储内部异常对象(参见第二个构造函数)。最后 GetExceptionName()虚拟方法为识别异常类名提供了 RTTI 的廉价替代方案(我认为这看起来不酷,但我想不出更好的解决方案;为了比较:在 .NET 中,可以简单地使用 someException.GetType().Name )。

    现在这就完成了。但是...我不喜欢这个解决方案有一个特殊的原因:每个派生类所需的编码量。考虑我必须推导出 SubException类,它为基类功能提供绝对零添加,它只提供自定义名称(“SubException”,可能是“IoException”、“ProjectException”等)以区分其使用场景。我必须为每个这样的异常类提供几乎相同数量的代码。这里是:
    class SubException : public Exception
    {
    protected:
    
        SubException(const SubException& source) : Exception(source) {};
        SubException& operator= (const SubException&) {};
    
    public:
    
        SubException(const CString &message) : Exception(message) {};
        SubException(const CString &message, const Exception &innerException) : Exception(message, innerException) {};
    
        virtual CString GetExceptionName() const { return L"SubException"; }
    
        virtual Exception *Clone() const
        {
            SubException *ex = new SubException(this->Message);
            ex->InnerException = this->InnerException ? this->InnerException->Clone() : 0;
            return ex;
        }
    };
    

    我不喜欢我必须提供的事实 protected每次都复制构造函数和赋值运算符,我不喜欢我必须克隆 Clone 的事实。每次使用方法,甚至复制复制基本成员的代码(InnerException ...),只是...我认为这不是优雅的解决方案。但我想不出更好的方法。您对如何“正确”实现这个概念有什么想法吗?或者这可能是 C++ 中这个概念的最佳实现?或者也许我这样做完全错误?

    P.S.:我知道 C++11(也在 Boost 中)存在一些用于此目的的机制(异常链)和一些新的异常类,但我主要对自定义的“旧 C++ 兼容”方式感兴趣。但是,此外,如果有人可以提供 C++11 中的任何代码来完成相同的工作,那就太好了。

    最佳答案

    C++11 已经有了 nested_exception .在 Boostcon/C++Next 2012 上讨论了 C++03 和 C++11 中的异常。视频在 youtube 上:

  • http://www.youtube.com/watch?v=N9bR0ztmmEQ&feature=plcp
  • http://www.youtube.com/watch?v=UiZfODgB-Oc&feature=plcp
  • 关于c++ - 实现 C++ 异常链的正确/优雅方式?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/13357178/

    有关c++ - 实现 C++ 异常链的正确/优雅方式?的更多相关文章

    1. ruby - 如何以所有可能的方式将字符串拆分为长度最多为 3 的连续子字符串? - 2

      我试图获取一个长度在1到10之间的字符串,并输出将字符串分解为大小为1、2或3的连续子字符串的所有可能方式。例如:输入:123456将整数分割成单个字符,然后继续查找组合。该代码将返回以下所有数组。[1,2,3,4,5,6][12,3,4,5,6][1,23,4,5,6][1,2,34,5,6][1,2,3,45,6][1,2,3,4,56][12,34,5,6][12,3,45,6][12,3,4,56][1,23,45,6][1,2,34,56][1,23,4,56][12,34,56][123,4,5,6][1,234,5,6][1,2,345,6][1,2,3,456][123

    2. ruby - 解析 RDFa、微数据等的最佳方式是什么,使用统一的模式/词汇(例如 schema.org)存储和显示信息 - 2

      我主要使用Ruby来执行此操作,但到目前为止我的攻击计划如下:使用gemsrdf、rdf-rdfa和rdf-microdata或mida来解析给定任何URI的数据。我认为最好映射到像schema.org这样的统一模式,例如使用这个yaml文件,它试图描述数据词汇表和opengraph到schema.org之间的转换:#SchemaXtoschema.orgconversion#data-vocabularyDV:name:namestreet-address:streetAddressregion:addressRegionlocality:addressLocalityphoto:i

    3. ruby-on-rails - 如何优雅地重启 thin + nginx? - 2

      我的瘦服务器配置了nginx,我的ROR应用程序正在它们上运行。在我发布代码更新时运行thinrestart会给我的应用程序带来一些停机时间。我试图弄清楚如何优雅地重启正在运行的Thin实例,但找不到好的解决方案。有没有人能做到这一点? 最佳答案 #Restartjustthethinserverdescribedbythatconfigsudothin-C/etc/thin/mysite.ymlrestartNginx将继续运行并代理请求。如果您将Nginx设置为使用多个上游服务器,例如server{listen80;server

    4. ruby-on-rails - 如何使用 instance_variable_set 正确设置实例变量? - 2

      我正在查看instance_variable_set的文档并看到给出的示例代码是这样做的:obj.instance_variable_set(:@instnc_var,"valuefortheinstancevariable")然后允许您在类的任何实例方法中以@instnc_var的形式访问该变量。我想知道为什么在@instnc_var之前需要一个冒号:。冒号有什么作用? 最佳答案 我的第一直觉是告诉你不要使用instance_variable_set除非你真的知道你用它做什么。它本质上是一种元编程工具或绕过实例变量可见性的黑客攻击

    5. ruby - 如何根据特征实现 FactoryGirl 的条件行为 - 2

      我有一个用户工厂。我希望默认情况下确认用户。但是鉴于unconfirmed特征,我不希望它们被确认。虽然我有一个基于实现细节而不是抽象的工作实现,但我想知道如何正确地做到这一点。factory:userdoafter(:create)do|user,evaluator|#unwantedimplementationdetailshereunlessFactoryGirl.factories[:user].defined_traits.map(&:name).include?(:unconfirmed)user.confirm!endendtrait:unconfirmeddoenden

    6. ruby-on-rails - Rails - 乐观锁定总是触发 StaleObjectError 异常 - 2

      我正在学习Rails,并阅读了关于乐观锁的内容。我已将类型为integer的lock_version列添加到我的articles表中。但现在每当我第一次尝试更新记录时,我都会收到StaleObjectError异常。这是我的迁移:classAddLockVersionToArticle当我尝试通过Rails控制台更新文章时:article=Article.first=>#我这样做:article.title="newtitle"article.save我明白了:(0.3ms)begintransaction(0.3ms)UPDATE"articles"SET"title"='dwdwd

    7. ruby - #之间? Cooper 的 *Beginning Ruby* 中的错误或异常 - 2

      在Cooper的书BeginningRuby中,第166页有一个我无法重现的示例。classSongincludeComparableattr_accessor:lengthdef(other)@lengthother.lengthenddefinitialize(song_name,length)@song_name=song_name@length=lengthendenda=Song.new('Rockaroundtheclock',143)b=Song.new('BohemianRhapsody',544)c=Song.new('MinuteWaltz',60)a.betwee

    8. ruby-on-rails - 正确的 Rails 2.1 做事方式 - 2

      question的一些答案关于redirect_to让我想到了其他一些问题。基本上,我正在使用Rails2.1编写博客应用程序。我一直在尝试自己完成大部分工作(因为我对Rails有所了解),但在需要时会引用Internet上的教程和引用资料。我设法让一个简单的博客正常运行,然后我尝试添加评论。靠我自己,我设法让它进入了可以从script/console添加评论的阶段,但我无法让表单正常工作。我遵循的其中一个教程建议在帖子Controller中创建一个“评论”操作,以添加评论。我的问题是:这是“标准”方式吗?我的另一个问题的答案之一似乎暗示应该有一个CommentsController参

    9. ruby - 我可以将我的 README.textile 以正确的格式放入我的 RDoc 中吗? - 2

      我喜欢使用Textile或Markdown为我的项目编写自述文件,但是当我生成RDoc时,自述文件被解释为RDoc并且看起来非常糟糕。有没有办法让RDoc通过RedCloth或BlueCloth而不是它自己的格式化程序运行文件?它可以配置为自动检测文件后缀的格式吗?(例如README.textile通过RedCloth运行,但README.mdown通过BlueCloth运行) 最佳答案 使用YARD直接代替RDoc将允许您包含Textile或Markdown文件,只要它们的文件后缀是合理的。我经常使用类似于以下Rake任务的东西:

    10. ruby - 在 Ruby 中重新分配常量时抛出异常? - 2

      我早就知道Ruby中的“常量”(即大写的变量名)不是真正常量。与其他编程语言一样,对对象的引用是唯一存储在变量/常量中的东西。(侧边栏:Ruby确实具有“卡住”引用对象不被修改的功能,据我所知,许多其他语言都没有提供这种功能。)所以这是我的问题:当您将一个值重新分配给常量时,您会收到如下警告:>>FOO='bar'=>"bar">>FOO='baz'(irb):2:warning:alreadyinitializedconstantFOO=>"baz"有没有办法强制Ruby抛出异常而不是打印警告?很难弄清楚为什么有时会发生重新分配。 最佳答案

    随机推荐