`

respot - c++ - A programmatic look at the exception specifications

    博客分类:
  • c++
c++ 
阅读更多

When I looked at the exception handlings, I am quit confused, as it is is not a full-blown exception system, and it does not have very good way to embedded the exception specification to the system. 

 

there is a good discussion on this matter, the original post is here: A programmatic look at the exception specification. 

 


A Pragmatic Look at Exception Specifications

This article appeared in C/C++ Users Journal, 20(7), July 2002.

 

As we consider work now underway on the new C++ standard, C++0x, it’s a good time to take stock of what we’re doing with, and have learned from, our experience with the current C++98 standard. The vast majority of Standard C++’s features are good, and they get the lion’s share of the print because there’s not much point harping on the weaker features. Rather, the weaker and less useful features more often just get ignored and atrophy from disuse until many people forget they’re even there (not always a bad thing). That’s why you’ve seen relatively few articles about obscure features like valarray, bitset, locales, and the legal expression 5[a] — and the same is true, for reasons which we shall see in this column and the next, for exception specifications and export.

This time, let’s take a closer look at the state of our experience with Standard C++ exception specifications.

 

The Story So Far

The idea behind exception specifications is easy to understand: In a C++ program, unless otherwise specified, any function might conceivably emit any type of exception. Consider a function named Func() (because the name f() is so dreadfully overused):

// Example 1(a)
//
int Func();            // can throw anything

By default, in C++, Func() could indeed throw anything, just as the comment says. Now, often we know just what kinds of things a function might throw, and then it’s certainly reasonable to want to supply the compiler and the human programmer with some information limiting what exceptions could come tearing out of a function. For example:

// Example 1(b)
//
int Gunc() throw();    // will throw nothing 

int Hunc() throw(A,B); // can only throw A or B

In these cases, the function’s exception specification exists in order to say something about what the functions Gunc() and Hunc() could emit. The comments document colloquially what the specifications say. We’ll return to that “colloquially” part in a moment, because as it turns out these two comments are deceptively close to being correct.

One might naturally think that making a statement about what the functions might throw would be a good thing, that more information is better. One would not necessarily be right, because the devil is in the details: Although the motivation is noble, the way exception specifications are, well, specified in C++ isn’t always useful and can often be downright detrimental.

Issue the First: A “Shadow Type System”

John Spicer, of Edison Design Group fame [1] and an author of large swathes of the template chapter of the C++ standard, has been known to call C++’s exception specifications a “shadow type system.” One of C++’s strongest features is its strong type system, and that’s well and good. Why would we call exception specifications a “shadow type system” instead of just “part of the type system”?

The reason is simple, and twofold:

a) Exception specifications don’t participate in a function’s type.

b) Except when they do.

Consider first an example of when exception specifications don’t participate in a function’s type. Reflect on the following code:

// Example 2(a): You can’t write an ES
// in a typedef.
//
void f() throw(A,B);

typedef void (*PF)() throw(A,B); // syntax error 

PF pf = f;                       // can’t get here

The throw-specification on the typedef is illegal. C++ doesn’t let you write that, and so the exception specification is not allowed to participate in the type of a function… at least, not in the context of a typedef, it’s not. But in other cases, exception specifications do indeed participate in the function’s type, such as if you wrote the same function declaration without the typedef:

// Example 2(b): But you can if you omit
// the typedef!
//
void f() throw(A,B);
void (*pf)() throw(A,B);   // ok
pf = f;                    // ok

Incidentally, you can do this kind of assignment of a pointer to a function as long as the target’s exception specification is no more restrictive than the source’s:

// Example 2(c): Also kosher, low-carb,
// and fat-free.
//
void f() throw(A,B);
void (*pf)() throw(A,B,C); // ok
pf = f;                    // ok, less restrictive

Exception specifications also participate in a virtual function’s type when you try to override it:

// Example 2(d): And the ES in the signature
// does matter if it’s a virtual function.
//
class C
{
  virtual void f() throw(A,B); // same ES
};

class D : C
{
  void f(); // error, now the ES matters
};

So the first issue with exception specifications as they exist in today’s C++ is that they’re really a “shadow type system” that plays by different rules than the rest of the type system.

Issue the Second: (Mis)understandings

The second issue has to do with knowing what you’re getting. As many notable persons, including the authors of the Boost exception specification rationale [2], have put it, programmers tend to use exception specifications as though they behaved the way the programmer would like, instead of the way they actually do behave. (For a brief mention of this, with longer related discussion about whether exception safety is worth it, see [3].)

Here’s what many people think that exception specifications do:

bullet

Guarantee that functions will only throw listed exceptions (possibly none).

bullet

Enable compiler optimizations based on the knowledge that only listed exceptions (possibly none) will be thrown.

The above expectations are, again, deceptively close to being correct. Consider again the code in Example 1(b):

// Example 1(b) reprise, and two
// potential white lies:
//
int Gunc() throw();    // will throw nothing (?)

int Hunc() throw(A,B); // can only throw A or B (?)

Are the comments correct? Not quite. Gunc() may indeed throw something, and Hunc() may well throw something other than A or B! The compiler just guarantees to beat them senseless if they do… oh, and to beat your program senseless too, most of the time.

Because Gunc() or Hunc() could indeed throw something they promised not to, not only can’t the compiler assume it won’t happen, but the compiler is responsible for being the policeman with the billy club who checks to make sure such a bad thing doesn’t happen undetected. If it does happen, then the compiler must invoke the unexpected() function. Most of the time, that will terminate your program. Why? Because there are only two ways out of unexpected(), neither of which is a normal return. You can pick your poison:

a) Throw instead an exception that the exception specification does allow. If so, the exception propagation continues as it would normally have. But remember that the unexpected() handler is global — there is only one for the whole program. A global handler is highly unlikely to be smart enough to Do the Right Thing for any given particular case, and the result is to go to terminate(), go directly to terminate(), do not pass catch, do not collect $200.

b) Throw instead (or rethrow) an exception that the exception specification (still) doesn’t allow. If the original function allowed a bad_exception type in its exception specification, okay, then it’s a bad_exception that will now get propagated. But if not, then go to terminate(), go directly to terminate()…

Because violated exception specifications end up terminating your program the vast majority of the time, I think it’s legitimate to call that “beat[ing] your program senseless.”

Above, we saw two bullets stating what many people think that exception specifications do. Here is an edited statement that more accurately portrays what they actually do do:

bullet

Guarantee Enforce at runtime that functions will only throw listed exceptions (possibly none).

bullet

Enable or prevent compiler optimizations based on the knowledge that only listed exceptions (possibly none) will be thrown having to check whether listed exceptions are indeed being thrown.

To see what a compiler has to do, consider the following code which provides a body for one of our sample functions, Hunc():

// Example 3(a)
//
int Hunc() throw(A,B)
{
  return Junc();
}

Functionally, the compiler must generate code like the following, and it’s typically just as costly at runtime as if you’d hand-written it yourself (though less typing because the compiler generates it for you):

// Example 3(b): A compiler’s massaged
// version of Example 3(a)
//
int Hunc()
try
{
  return Junc();
}
catch( A )
{
  throw;
}
catch( B )
{
  throw;
}
catch( ... )
{
  std::unexpected(); // will not return! but
}  // might throw an A or a B if you’re lucky

Here we can see more clearly why, rather than letting the compiler make optimizations by assuming only certain exceptions will be thrown, it’s exactly the reverse: the compiler has to do more work to enforce at runtime that only those exceptions are indeed thrown.

The Scoop on Exception Specifications

Besides the overhead for generating the try/catch blocks shown above, which might be minor on efficient compilers, there are at least two other ways that exception specifications can commonly cost you in runtime performance. First, some compilers will automatically refuse to inline a function having an exception specification, just as they can apply other heuristics such as refusing to inline functions that have more than a certain number of nested statements or that contain any kind of loop construct. Second, some compilers don’t optimize exception-related knowledge well at all, and will add the above-shown try/catch blocks even when the function body provably can’t throw.

Moving beyond runtime performance, exception specifications can cost you programmer time because they increase coupling. For example, removing a type from the base class virtual function’s exception specification is a quick and easy way to break lots of derived classes in one swell foop (if you’re looking for a way). Try it on a Friday afternoon checkin, and start a pool to guess the number of angry emails that will be waiting for you in your inbox on Monday morning.

So here’s what seems to be the best advice we as a community have learned as of today:

Moral #1: Never write an exception specification.

Moral #2: Except possibly an empty one, but if I were you I’d avoid even that.

Boost’s experience is that a throws-nothing specification on a non-inline function is the only place where an exception specification “may have some benefit with some compilers” [emphasis mine]. That’s a rather underwhelming statement in its own right, but a useful consideration if you have to write portable code that will be used on more than one compiler platform.

It’s actually even a bit worse than that in practice, because it turns out that popular implementations vary in how they actually handle exception specifications. At least one popular C++ compiler (Microsoft’s, up to version 7.x) parses exception specifications but does not actually enforce them, reducing the exception specifications to glorified comments. But, on the other hand, there are legal optimizations a compiler can perform outside a function, and which the Microsoft 7.x compiler does perform, that rely on the ES enforcement being done inside each function -- the idea is that if the function did try to throw something it shouldn’t the internal handler would stop the program and control would never return to the caller, so since control did return to the caller the calling code can assume nothing was thrown and do things like eliminate external try/catch blocks. So on that compiler, because the checking is not done but the legal optimization that relies on it is done, the meaning of “throw()” changes from the standard “check me on this, stop me if I inadvertently throw” to a “trust me on this, assume I’ll never throw and optimize away.” So beware: If you do choose to use even an empty throw-specification, read your compiler’s documentation and check to see what it will really do with it. You might just be surprised. Be aware, drive with care.

Summary

While mentioning this material as part of a broader talk at the ACCU conference this past spring, I asked how many of the about 100 people in the room each time had used exception specifications. About half put up their hands. Then a wag at the back said (quite correctly) that I should also ask how many of those people later took the exception specifications back out again afterwards, so I asked; about the same number of hands went up. This is telling. The world-class library designers at Boost went through the same experience, and that’s why their coding policy on writing exception specifications pretty much boils down to “don’t do that.” [2]

True, many well-intentioned people wanted exception specifications in the language, and so that’s why we have them. This reminds me of a cute poem that I first encountered about 15 years ago as it circulated in midwinter holiday emails. Set to the cadence of “‘Twas the Night Before Christmas,” these days it’s variously titled “‘Twas the Night Before Implementation” or “‘Twas the Night Before Crisis.” It tells of a master programmer who slaves away late at night in the holiday season to meet user deadlines, and performs multiple miracles to pull out a functioning system that perfectly implements the requirements… only to experience a final metaphorical kick in the teeth as the last four lines of the ditty report:

The system was finished, the tests were concluded,
The users’ last changes were even included.
And the users exclaimed, with a snarl and a taunt,
“It’s just what we asked for, but not what we want!” 
[4]

The thought resonates as we finish considering our current experience with exception specifications. The feature seemed like a good idea at the time, and it is just what some asked for.

But wait, there’s more: Might the same be said about export? More on that one next time, when we return…

Notes

[1] See www.edg.com.

[2] Available via www.gotw.ca/publications/xc++s/boost_es.htm.

[3] Herb Sutter. “Exception Safety and Exception Specifications – Are They Worth It?” (Guru of the Week #82).

[4] A web search for “a snarl and a taunt” will get you several variations on this poem. Enjoy! Alas, the original author of the poem is unknown (to me). If anyone has information about the original, or at least earliest known, source of this poem, please send me mail.

分享到:
评论

相关推荐

    ios-module-project-photo-collection-programmatic-constraints

    PhotoCollection-程序限制介绍您可能曾经创建过此应用程序,但是将使用此模块项目的NSLayoutConstraint和NSLayoutAnchor API以编程方式布置UI元素。 该应用程序允许用户将其照片库中的照片添加到UICollectionView的...

    前端开源库-npm-programmatic

    在`npm-programmatic-master`这个压缩包中,可能包含了示例代码或者一个完整的项目,演示了如何在Node.js环境中实现上述操作。解压并研究这些文件可以帮助你深入理解编程式使用npm的实践方法。 编程式使用npm不仅...

    nw-programmatic-folder-select:以编程方式打开本机“文件夹选择”对话框

    nw-程序化文件夹选择 以编程方式在NW.js中打开本机“文件夹选择”对话框。 ...// The window object, to have access to the browser context, and a callback function with the user's choice o

    jam3-lesson-programmatic-animation:这是关于程序化动画的课程。 它涵盖了使用代码为网络制作动画的不同方法

    程序化动画目录下载课程文件打开终端并输入npm i jam3/jam3-lesson-programmatic-animation -g 。 这将安装课程模块并使其全球可用。 创建一个名为“jam3-lesson-programmatic-animation”的文件夹。 cd 到此目录并...

    开源项目-TimTosi-fishfinger.zip

    开源项目-TimTosi-fishfinger.zip,FishFinger - Docker-Compose lightweight programmatic library written in Go.

    CParallelPort

    port using low level port IO while at the same time making sure that the code does not cause problems for other apps using the parallel ports using normal Win32 calls. Features Simple and clean...

    helloworld-programmatic-2.0-m11.zip

    【标题】"helloworld-programmatic-2.0-m11.zip" 暗示这是一个开源项目,名为 "HelloWorld" 的编程示例,版本为2.0的第11个里程碑(M11)。通常,这样的项目会包含一个简单的应用程序,用于演示如何使用特定的技术或...

    iBlue - Forex Programmatic Proposal - 0412.pptx

    【iBlue - 外汇程序化提案】 在深入解析这份外汇程序化提案时,我们可以看出其核心目标是通过精准营销策略提升品牌知名度,增加官方网站流量,并招募注册用户。以下是该计划的关键要点: **策略与执行** ...

    Kinect v2 Examples with MS-SDK v2.13 with SDK2.unitypackage

    The avatar-demo scenes show how to utilize Kinect-controlled avatars in your scenes, gesture-demos – how to use the programmatic or VGB gestures, fitting room demos – how to create your own dressing...

    NSX-T Data Center API Guide.html

    NSX-T Data Center provides a programmatic API to automatemanagement activities. The API follows a resource-orientedRepresentational State Transfer (REST) architecture, using JSONobject encoding. ...

    rr-init:研究项目的初始化和组织遵循可重复的研究指南

    概述 project|- doc/ # documentation for the study| +- paper/ # manuscript(s), whether generated or not||- data # raw and primary data, are not changed once created | |- raw/ # raw data, will not be ...

    cute-stack, 在 node 里把你的stack.zip

    cute-stack, 在 node 里把你的stack 可爱的栈可爱的node 堆栈跟踪npm install -g cute-stack # cli usagenpm install cute-stack --save # programmatic usage 用法

    The Java EE 6 Tutorial Basic Concepts 4th Edition

    Using Programmatic Security with Web Applications 469 Examples: Securing Web Applications 474 Chapter 25: Getting Started Securing Enterprise Applications 485 Securing Enterprise Beans 486 ...

    hibernate_reference.pdf

    - **Contextual Sessions**: Discusses the use of contextual sessions, which allow you to manage the lifecycle of a session within a transactional context. #### Configuration Configuration plays a ...

    TeeChart2013_131216_SourceCode

    in a "designtime" development environment, exposes the programmatic interface of the SOFTWARE. You may distribute, on a royalty-free basis, Redistributable Files with Developed Desktop Software only...

    Pro WPF and Silverlight MVVM-English

    Too often there is a reliance on programmatic interaction between controls and not enough trust in the technologies' data-binding capabilities. This leads to a clouding of design values and an ...

    TeeChart2013_130818_SourceCode

    in a "designtime" development environment, exposes the programmatic interface of the SOFTWARE. You may distribute, on a royalty-free basis, Redistributable Files with Developed Desktop Software only...

Global site tag (gtag.js) - Google Analytics