The C++ "finally" keyword and its relation to design-by-contract

Started by
29 comments, last by silvermace 15 years, 3 months ago
I've seen a few proposed systems to apply design-by-contract to C++. They're a bit verbose. It's obvious that common assertions at the beginning of a function are enough to express pre-conditions. Post-conditions, however, are much trickier. You can slap some assert()'s at the end of your function body, but you can't guarantee that a previous piece of code won't return earlier. Especially when re-visiting old code, you can just add a new return and not care/not remember that some post-conditions must always be verified. It's tedious work, and hard to maintain all those assertions. So there's a tight relationship between clean-up when exiting scope and asserting post-conditions. It would be cool if we could instruct the compiler to run some code on scope exit, no matter what happens. This would be solved with the proposed keyword "finally", which is available in VS but has to be used with a "try" block, making it very intrusive (ie, try {...} finally {...} ). It's also not available on other compilers (AFAIK). With this in mind, I set out to re-create the "finally" keyword myself. Using it in design-by-contract:

void f() {
	// pre-conditions:
	assert(...);
	// post-conditions:
	finally {
		assert(...);
		assert(...);
	}

	// main body
}
It's also useful for other kinds of clean-up, undoing actions on error, logging, etc. I managed to do it with a macro to create a local class that executes a block of code in its destructor. The main advantage of the macro is that you can access the scope of the original function, which a simple local class usually can't do. Without that feature the method would be rendered obsolete. It's a macro, and I know many people don't like those. But anyways, before posting the code I'd like to gauge interest in this. Would you find it useful? Is there a simpler alternative? Jotaf
Advertisement
Why not use a function ? This way, no matter where you return from, you still assert post-conditions:

private: void do_foo() { /* Code here */ }public: void foo() { assert(pre); do_foo(); assert(post); }


The problem with destructors is that you don't really want to check for post-conditions when you throw an exception: any conditions related to "I will return the right data" are obviously incorrect when an exception is thrown, and any conditions related to the class state are either incorrect (an exception happened because they couldn't become true) or obviously correct (the exception rolled back any modifications to restore the initial state).
Quick response! :)

That's a viable solution, but it suffers from the maintainability problem of other alternatives. You'll need to work hard to have a duplicate of every function in your program -- not to mention name clashes. It's something I didn't think of and I'm glad you mentioned it, attempting to streamline its use might be worth a shot.

I see where you're getting at with the destructors, but it doesn't seem an issue with other systems. To name a few:
Using Design by Contract in C by Charlie Mills
Design by Contract Framework for C++ By Kevin McFarlane
Design by Contract in C++ By Jarl Lindrud

The first one involves a pre-processing perl script, but the other two simply use local classes and destructors like I'm proposing. (Their methods don't allow such freedom to define any assertion you want, though.) They simply ignore the issue you raised. Of course, just because someone else is doing so is no excuse. But from my experience, it doesn't really matter if some code throws, thereby causing another one to throw; it's what happens commonly with a lot of errors anyway.
This could also be useful in C, to do the free-ing of allocated memory at the end of a function!
Or in other words, Digital Mars D has this feature too.

Still, it's a really nifty feature and it's too bad that g++ doesn't have it. ( Or atleast, not that I know of. And if this is the case, then it really lacks documentation on the net in my opinion. )
http://sourceforge.net/projects/pingux/ <-- you know you wanna see my 2D Engine which supports DirectX and OpenGL or insert your renderer here :)
Quote:Original post by ToohrVyk
The problem with destructors is that you don't really want to check for post-conditions when you throw an exception: any conditions related to "I will return the right data" are obviously incorrect when an exception is thrown, and any conditions related to the class state are either incorrect (an exception happened because they couldn't become true) or obviously correct (the exception rolled back any modifications to restore the initial state).
The method of using a destructor isn't totally without merrit though. It can still be used to verify the internal state of the class upon exit, if you're aiming for writing methods with strong exception guarantees.
It will also work fine for methods that cannot throw exceptions.
"In order to understand recursion, you must first understand recursion."
My website dedicated to sorting algorithms
Quote:Original post by Jotaf
With this in mind, I set out to re-create the "finally" keyword myself.


Your "finally" has different semantics than the normal usage -- when execution reaches the finally block, it only registers actions to be done later, rather than occurring immediately. I'd name it something else. Maybe POSTCONDITIONS ? (lowercase #defines are evil! EVIL!)

The dangers of possibly throwing from a destructor have already been noted, yes?
Quote:Original post by MaulingMonkey
It will also work fine for methods that cannot throw exceptions.


That's the idea. I'll probably have two versions. The first one is intended for cleaning-up code (Finally), and as you probably expect behaves exactly like a destructor. As the C++ Standard has it, it will terminate the process if a 2nd exception is found. Here's a nice discussion on the subject (and the reasoning behind throwing destructors).

The second version (PostConditions) only runs when an exception was not already thrown (by checking std::uncaught_exception()). So it's only intended to run in the event of a return statement. Here's where you can have some asserts() for your post-conditions. It's more general than that, can someone suggest a new name?


Quote:Original post by MaulingMonkey
I'd name it something else. Maybe POSTCONDITIONS ? (lowercase #defines are evil! EVIL!)


Weird, I find that UPPERCASE in the middle of well-formed code hurts my eyes so if this is going to be of any general usage, it could be LikeThis. Of course it'd be better to leave it as a choice to the programmer-user. Not enough libraries give you that kind of choice :)


Quote:The dangers of possibly throwing from a destructor have already been noted, yes?


Ah yes, the old problem with throwing destructors. I hope the addition of std::uncaught_exception() mentioned above is enough to avoid that, and the link might provide you with some insight on the whole subject.
Quote:Original post by Lode
This could also be useful in C, to do the free-ing of allocated memory at the end of a function!


Unfortunately this uses local classes so it's not really for C. I see where you're coming from though.

Quote:PinguinDude
Or in other words, Digital Mars D has this feature too.

Still, it's a really nifty feature and it's too bad that g++ doesn't have it. ( Or atleast, not that I know of. And if this is the case, then it really lacks documentation on the net in my opinion. )


It's too bad that it isn't in C++ already! It would allow for much better error handling and come handy in many other situations as well. Unfortunately C++ makes it really difficult to delay code execution of any kind. I've been looking at boost::lambda (and even saw a DBC implementation that used it) but it's not the same thing.
If you need to replicate a finally block as in Java, there's two approaches which can be useful in different circumstances.

1) Using ScopeGuard, which is part of the Loki library.
2) You can replectate try-finally in C++ using the following idiom:

try{   ...}catch (...){   finally_code;   throw;}finally_code;


You can encompass this with a macro like this:

#define MY_FINALLY(X) catch(...) {X; throw; } X; 


You can use it like this. Note the difference of using ( ) instead of { } for the "finally block". This will prevent you from being able to use , as a statement that isn't encompassed by nested parenthesis, but this has rarely been an issue in practice.

try{   ...}MY_FINALLY(   finally_code;)


Although I usually advocate avoiding macros as much as anyone, weighing that against code duplication is a no brainer.

One other thought on your specific question, though. If you think about, post conditions have automatically been violated if you throw an exception out of a function. So, personally, I would either avoid the asserts when an exception is thrown, or at best, simply use the asserts to guarantee one of the exception guarantees in this case, because some other expected post-conditions may not make sense in the presence of an exception.

This topic is closed to new replies.

Advertisement