【问题标题】:Exceptions thrown from specific parts of the code are not being caught未捕获从代码的特定部分抛出的异常
【发布时间】:2013-04-14 13:01:12
【问题描述】:

我刚刚为我的 OpenGL + GLUT 游戏实现了一个基本的异常系统。我抛出退出游戏的异常处理得很好,以及其他一些断言异常,但是我试图通过删除图像文件夹来导致图像加载失败,并且该特定异常似乎没有被捕获。它会导致应用程序关闭并显示消息“此应用程序请求运行时以异常方式终止”。

这是处理所有异常的 try/catch 块,它是应用程序类的内联静态方法(它在头文件中,因为它是一个模板函数):

#pragma once

#include "logging.hpp"
#include "utils.hpp"
#include "throwables.hpp"
#include <cassert>
#include <exception>

namespace core
{
    class application
    {
    public:
        static const char * const tag;

        template <class T>
        static int run(int argc, char *argv[], const int width, const int height,
            int x, int y, const char * const title) throw (const std::exception &)
        {
            int res = 0;
            logging * const log = logging::get();

            try
            {
                assert(log != NULL);
                log->setverbosity(logging::info);
                dbgcode(log->setverbosity(logging::verbose));

                T * const g = T::get();

                if (!g)
                {
                    log->wtf(tag, "run: g is NULL! are we out of memory?");
                    throw core::assertexception();
                }

                T::envinit(argc, argv);
                g->initialize(width, height, x, y, title);
                g->run();
            }
            catch(const core::exitexception &e)
            {
                // hopefully everything is deallocated properly this way
                log->i(tag, "run: exit message recieved");
                res = 0;
            }
            catch(const core::assertexception &e)
            {
                utils::unexpectederror(title);
                res = 1;
            }
            catch(const core::errorexception &e)
            {
                utils::error(title);
                res = 1;
            }

            log->i(tag, strfmt() << "exiting with code " << res);

            return res;
        }
    };
}

这是抛出从未处理过的异常的代码(它在g->run中执行,所以它在try块中):

void sprite::load(const char * const filename) throw (const assertexception &, const errorexception &)
{
    if (!filename) // this exception is handled perfectly
    {
        log->wtf(tag, strfmt() << "load: filename is NULL! what the hell did you pass me?" << img);
            throw assertexception();
    }

    fromtex = false;

    // generate and bind a DevIL image
    ilGenImages(1, &img);
    ilBindImage(img);

    // attempt to load the sprite as a DevIL image
    if (!ilLoadImage(filename)) // this exception is never caught even if I change this to if(true)
    {
        log->e(tag, strfmt() << "load: failed to load " << filename << ", image id = " << img);
        throw errorexception(); // never caught when thrown
    }

    ...

这是我的 throwables.hpp / throwables.cpp,我在其中定义了所有自定义异常:

hpp:

#pragma once

#include <stdexcept>
#include <string>

namespace core
{
    class exception : public std::runtime_error
    {
    public:
        exception(const std::string &info);
    };

    class assertexception : public exception
    {
    public:
        assertexception();
    };

    class errorexception : public exception
    {
    public:
        errorexception();
    };

    class exitexception : public exception
    {
    public:
        exitexception();
    };
}

cpp:

#include "throwables.hpp"

namespace core
{
    exception::exception(const std::string &info)
        : std::runtime_error(info)
    {}

    // assertexception
    assertexception::assertexception()
        : exception("an assertion has occurred - check the log file")
    {}

    // errorexception
    errorexception::errorexception()
        : exception("an error has occurred - check the log file")
    {}

    // exitexception
    exitexception::exitexception()
        : exception("exit message recieved")
    {}
}

编辑:另外,这里是我调用的那些 utils::*error 函数:

void unexpectederror(const wxString &gamename)
{
    // TODO: make everything unicode friendly
    // TODO: get all strings from xmls for easy localization
    wxSafeShowMessage(gamename + wxT(" - Unexpected error"),
        wxT("An unexpected error has occurred. Please report this bug")
        wxT(" and attach your lastsession.log file"));
}

void error(const wxString &gamename)
{
    wxSafeShowMessage(gamename + wxT(" - Error"),
        wxT("An error has occurred. Please check your lastsession.log")
        wxT(" for more info and check for <error> messages. Please report it if you believe it is a bug"));
}

void error(const wxString &title, const wxString &text)
{
    wxSafeShowMessage(title, text);
}

顺便说一句,std::exception 正被 main 捕获:

#include "dungeoncrawler.hpp"
#include "application.hpp"

using namespace core;

int main(int argc, char *argv[])
{
    static const char * const tag = ".main";
    static const char * const title = "DungeonCrawler";

    try
    {
        return application::run<dungeoncrawler::game>(argc, argv, 800, 450, -1, -1, title);
    }
    catch(const std::exception &e)
    {
        utils::unhandledexception(title, e);
        logging::get()->wtf(tag, "unhandled exception, exiting with code 0");
        return 0;
    }

    assert(false); // this code should never be reached
    logging::get()->wtf(tag, "what?! this code should be unreachable! exiting with code 0");
    utils::error(wxString(title), wxT("Unexpected behaviour occurred. Please report this bug to the developer"));

    return 0;
}

【问题讨论】:

  • 可能值得注意的是,您在任何时候都没有捕捉到std::exception。虽然您定义的所有内容都很好并且很好地捕捉到了这些内容,但当某些您没有编写的代码引发异常时,一切都会崩溃。
  • std::exception 在 main 中被捕获

标签: c++ exception-handling


【解决方案1】:

问题在于这个函数是如何声明的。

void sprite::load(const char * const filename) throw (const assertexception &, const errorexception &)

该函数承诺只抛出异常assertexceptionerrorexception

如果该函数抛出任何其他异常,std::unexpected() 将被调用并终止您的程序。例如,如果标准库或任何其他库不知道这些异常类 throw,就会发生这种情况。

删除 throw 子句以修复它。

请参阅this Herb Sutter article 了解更多信息。

【讨论】:

  • 注意:我在这里应用了一些通灵能力,因为该功能的全部细节未知
  • 哇哦。这不完全是问题,但它帮助我解决了这个问题。 sprite::load 是从 dungeoncrawler::game::initialize 调用的,它只被声明为抛出 assertexception。这就是为什么它没有处理其他的!谢谢
  • @FrancescoNoferi 这是个好消息!如果您有时间,请阅读链接的文章。简短的版本是,函数声明中的那些throw 子句已被证明往往比它们的价值更麻烦。
  • @FrancescoNoferi:异常说明符是个坏主意。作为实验,他们在 Java 和 C++ 中都进行了尝试。两种语言都失败了,并且在下一版本的语言中,此语言功能已从 C++ 中删除。
  • @DrewDormann 是的,我已经阅读了这篇文章,我意识到使用异常说明符太麻烦了。此外,当您重构代码、添加新内容或类似内容时,您一定会忘记更新它们。
猜你喜欢
  • 2014-08-18
  • 2018-08-31
  • 1970-01-01
  • 1970-01-01
  • 2011-05-07
  • 2011-08-12
  • 1970-01-01
  • 2012-07-27
相关资源
最近更新 更多