【问题标题】:Can g++ check the throw specifiers?g++ 可以检查抛出说明符吗?
【发布时间】:2011-05-18 11:54:42
【问题描述】:

关于这个的两个问题:

  • 有没有办法强制g++ 忽略throw 说明符?
    (例如,我记得,Visual Studio 忽略了 throw 说明符,与 throw() 不同)

  • 是否可以强制 g++ 检查抛出说明符是否正确 - 我的意思是检查(这可以通过 one-pass compilers 完成)如果带有抛出说明符的函数调用函数,这可能只是通过观察他们的抛出说明符来观察执行throw的异常,这会违反说明符吗? (注意: 这不应该在没有 throw 说明符的情况下观看函数,因为这可能会导致大量警告)


编辑:我将为我的第二个问题添加一些示例。

假设我们有:

// sorry for the coding style here, but I don't want it to be unnecessary long
class A { /* .. */ };
class B : public A { /* .. */ };
class C { /* .. */ };
void no_throw_spec() { /* .. */ }
void no_throw_at_all() throw() { /* .. */ }
void throws_A() throw( A ) { /* .. */ }

// this is fine, don't do anything
void f() 
{ no_throw_spec(); no_throw_at_all(); throws_A(); }

void g() throw()
{ 
    no_throw_spec(); no_throw_at_all(); // OK
    throws_A();  // warning here - throws_A() may throw A, but g() has throw()!
}

void h() throw( A )
{
    no_throw_spec(); no_throw_at_all(); throws_A(); // OK
    if( /* .. */ ) 
        throw B(); // OK, B inherits A, it's OK
    /* .. */
    throw C();    // C does not inherit A, so WARNING!
}

【问题讨论】:

  • 我认为 Herb Sutter 关于异常规范的文章仍然适用,即使它已经很老了gotw.ca/publications/mill22.htm
  • 我知道。这有什么关系?如果您的意思是,我不应该编写异常规范,我知道。我只是好奇。
  • 鉴于 throw 说明符不是函数类型的一部分,C++ 编译器肯定无法在任何可计算的传递次数中一般检查它们。例如,void doit(void(*f)()) throws() { f(); }。如果(且仅当)f 抛出任何东西,doit 就会违反其抛出规范。那么编译器是否应该以它可能违反为由拒绝它,因为在编译时没有办法告诉它只会用 nothrow 函数作为参数来调用它? Java 仅通过将 throws 子句作为函数签名的一部分来实现检查异常。
  • 或者你的意思是传递的函数在“没有抛出说明符的被调用函数”的标题下?因此编译器接受该代码,即使没有特别的理由相信它不会违反其抛出规范。还要注意,有些标准函数没有记录要抛出的 throw 说明符,例如 std::vector::at,可能您需要使用制造的说明符对它们进行注释,否则它们永远不会被检查。
  • 嗯,我还没有考虑过函数指针..但它们可能就像没有任何抛出说明符的函数一样。有关更多信息,请参阅我的编辑(:

标签: c++ g++ compiler-options exception-specification


【解决方案1】:
  • gcc 有一个选项 -fno-enforce-eh-specs,请参阅文档并检查它是否符合您的要求。

  • 我不记得使用 gcc 静态检查异常规范的任何方法。

请注意,(动态)异常规范在 C++0X 中已被弃用,它添加了一个 noexcept 异常规范来替换空异常规范案例(它也被动态检查并提供有助于在模板中使用它的规定)。

【讨论】:

  • 不是noexcept吗?但这也是运行时检查的,而不是在编译时静态检查的。
  • @Xeo,两点都是。我的观点是,现在做任何具有非空异常规范的事情都在沿着与委员会选择的路径不同的路径前进。
  • 我知道 C++0x,这让我对当前的 C++03 感到好奇。这个选项很酷,顺便说一句,谢谢!
  • "我不记得用 gcc 静态检查异常规范的任何方法了。" 在大多数程序中,静态检查 ES 是不可行的(大多数有用的程序不会 ES-检查;这就是 Java 不尝试这样做的原因。)它可能在某些完全没有多态性的程序中很有用,我不确定这些好处是否值得。
【解决方案2】:

是的,你可以让 g++ 忽略 throw by:

#define throw(x)

关于您需要更改编译器代码或在构建过程中创建自己的脚本/程序来检查这些内容的其余部分,可以使用正则表达式轻松完成。

编辑:

关于您的评论,查找异常的层次结构非常容易。使用正则表达式:

class ([^ ]*) : ([^ ]*)

并将其输入到哈希中,然后制作分层数据。
要匹配抛出异常的函数中的异常,请使用:

([^\(\s]*)[\s]*([^\)])[\s]*(throw[\s]*\([^\)]*\)){((throw[\s]*[^;])|*)*}

它没有经过测试,可能有一些错误,但是开始的好地方

【讨论】:

  • @Dani - 感谢第一个,+1。关于第二点 - 我不能轻易更改编译器代码:D 另外,正则表达式在这里不起作用,因为对于这个检查,我们需要知道异常的整个层次结构 - 对于一个大项目来说,这并不容易做到,只是使用正则表达式。或者至少我是这么认为的。
  • @Dani,但如果有人使用 throw 0; 会忽略吗?
  • 顺便说一句,x 应该被我替换为所有说明符,或者它是某种变量,它将匹配 () 之间的所有内容?
  • 它不会匹配throw 0,它只会匹配任何类型的1个参数。要匹配任意数量的参数,请使用#define throw(...),它仍然不会匹配throw 0
  • @Dani:不,向编译器添加警告不会改变语言。忽略 throw 规范确实会改变语言(编译器不再符合),但已经涵盖了。
猜你喜欢
  • 1970-01-01
  • 2011-02-10
  • 2023-03-05
  • 2021-04-11
  • 1970-01-01
  • 1970-01-01
  • 2016-05-30
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多