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

Started by
29 comments, last by silvermace 15 years, 4 months ago
Here's the current state of affairs for you guys to dissect. Each macro will expand into the following:

struct OnExit{	int &i;	OnExit(int &i_) : i(i_) {};	~OnExit() { if (!std::uncaught_exception()) { some-code } };} onexit(i);struct OnError{	int &i;	OnError(int &i_) : i(i_) {};	~OnError() { if (std::uncaught_exception()) { some-code } };} onerror(i);struct OnExitOrError{	int &i;	OnExitOrError(int &i_) : i(i_) {};	~OnExitOrError() { some-code };} onexitorerror(i);


This is a local class by the way. The "i" parameter in the constructor is a local variable that you wanna test in the "some-code" part -- there is no other way to access the caller's context. So you'd need to repeat that pattern for a number of variables that you want to use. The storage type (in this case int) must also be specified. It seems that templates aren't legal in local classes, which is a shame.

Anyways, the really neat thing is that as long as the internal references to the local variables have the same names as said local variables, you can just take some code that used to be in the original function, put it inside the local class unchanged and it just works.

The macros are supposed to replicate that up to a certain number of parameters, let you specify the local vars and their types, and also give each instance a different name to prevent conflicts in case you use several of these.

Feel free to punch a whole through it though :)
I'm not so sure about the names (OnExit is where you're supposed to put your assert()'s), and maybe there are some cases I forgot where this will not work.

EDIT
It works now, I just need to find a place to post it up. Example usage:

double my_sqrt(double x) {	double res;	// pre-conditions:	assert( x > 0 );	// post-conditions:	OnExit2(double,x, double,res, {		assert( res * res == x );	});	// main function body	res = sqrt(x);	// uncomment the following line to crash this thing	// (post-condition won't verify)	//res++;	return res;}void main() {	cout<<"Sqrt(30)="<<my_sqrt(30);}


[Edited by - Jotaf on December 22, 2008 7:54:48 PM]
Advertisement
could your solution possibly lay with AspectC++?

If all you want is pre-post assertions then maybe you could write a quick processing tool which could do some trivial source-to-source translation before you run your compilation, it wouldn't significantly change how you would do things so long as the source code was written out to a new location, then debugging wouldn't be affected.

The nice thing about this approach is that you can turn it off when you go to release mode, and the runtime and compile time overhead is minimal, and yes there will be a maintenance burden, but I think it may be less than inline Macros everywhere in your code?

some examples to get your mind working:
struct my_doofer {  int doofer(int x, int y) {    //!pre x<10    //!pre y>10    //!returns x+y        return x+y+1;  }};


and your command line too would process the file and produce something like this:
struct my_doofer {   /**generated method: unprocessed method name was: int doofer(int,int) */   int doofer_checked__(int x, int y) {      return x+y+1; /*original source line: 8*/   }   int doofer(int x, int y) {      /*precondition*/ assert(x<10);      /*precondition*/ assert(y>10);      int returns = doofer_checked__(x, y);      /*postcondition*/ assert(returns == x+y);      return returns;   }}


IMO this would be very simple to write using Python or even C if you feel up to it, and I think it would meet your requirements nicely.

I have thought a few times about implementing a simple "Java Annotations" style system like this, for reasons not dissimilar to yours.

Cheers,
-Danu

[EDIT: also, the original source code doesnt get convoluted and the syntax for the pre and post conditions is nice and tidy and pretty easy to appreciate, even to new commrs to your code.]
"I am a donut! Ask not how many tris/batch, but rather how many batches/frame!" -- Matthias Wloka & Richard Huddy, (GDC, DirectX 9 Performance)

http://www.silvermace.com/ -- My personal website
Quote:"The runtime penalty is also significant"

If there is, then either your compiler has serious problems or you didn't enable optimizations.
Or maybe you didn't realize what a functor was and always put them in boost::function (which is a polymorphic functor container, which of course adds overhead).

Anyway, in the next C++ standard there is support in the language for defining closures inline.

Quote:Yes but at the end of the day, are you really using that for every single aspect of your program? It's impractical.

Yes, I do. (I would even consider code that doesn't to be garbage, but I'm quite the extremist)
It's not impractical if you know how to do it right. It also guarantees at least basic exception-safety throughout your whole program.
Quote:Original post by Jotaf
Yes but at the end of the day, are you really using that for every single aspect of your program? It's impractical.
It's known as RAII, and is generally considered to be the "best practice" in C++ as far as handling resource release is concerned.

The premise of RAII is simple: whenever you have a manipulation that has to be done in two separated steps (be it alloc-dealloc, increment-decrement, lock-unlock, open-close, start-finish or whatever else you can imagine) you wrap the first step in a constructor and the second step in a destructor. Consider that:
  • You don't have that many two-step operations to handle.
  • All of the two-step operations in C++ are already handled for you this way by the standard library.
  • Once the operation is wrapped, the "constructor calls member constructor, destructor calls member destructor" principle in C++ means you don't have to wrap things more than once and so 99% of your code relies on RAII implemented by 1% of your code without additional work.
  • You even have specific wrappers provided by boost to operate on a certain resource with a certain function at end of scope (or when the last shared reference is destroyed), so it's often a matter of boost::shared_ptr<Obj> obj(Frobnicate(), UnFrobnicate);


In the end, the "wrap once, reuse everywhere" approach seems, at least to me, more appealing than the "use 'finally' everywhere", and is quite possibly the reason C# introduced the 'using' keyword to avoid the 'finally' infestations of Java.
Over-use of any concept can be harmful, but I've found that the R in RAII can often be a deterrent to adoption, since it implies there is some physical or external resource that is being managed.

But combined with strong typing, RAII methodology can be surprisingly powerful. A trivial example:
struct rect {  rect(int x1, int y1, int x2, int y2) {    assert(x1 < x2);    assert(y1 < y2);  }private:};
There is no "resource" here, it's just a bunch of ints. But the implications of RAII lead to several benefits.

Constructor asserts the contract. A rect instance cannot be created unless it passes those tests. So by implication, foo(rect) operates on valid rect. Obviously, this may seem like an overkill at glance, or perhaps a complication. But once carefully examined, writing robust code simply requires these assertions, and nothing less. So whether they're present as unit test or as design-by-contract, they cannot be avoided - might as well use the most natural mechanism already provided by language.


Of course, once an instance is constructed, non-const methods may corrupt internal state. Both C# and Java make extensive use of immutable types. In general, immutable types (in C++ created through RAII) make it considerably simpler to maintain. In some ways, they represent invariants.


Obviously, nothing is ever that easy, so eventually one does need non-const member functions, which is where formal testing comes into play. But IMHO, use of RAII coupled with consistent and properly implemented rule-of-three goes a very long way towards eliminating whole classes of bugs present in C++.

However, to get the benefit of such approach, classes should be auto-allocated. Various smart pointers count as such too.


An interesting side-effect of strict RAII/immutable enforcement is that code starts looking more like functional language. And I'm not sure that's a bad thing...
Quote:Original post by silvermace
If all you want is pre-post assertions then maybe you could write a quick processing tool which could do some trivial source-to-source translation before you run your compilation [...].


Yeah I thought about that. It's actually a neat idea. But the only implementation I know of is in C, and it's a much bigger effort to write and set-up a pre-processor than working within the confines of the language itself. If I had more time on my hands, I'd probably do that. Or if it was freely available already.

Quote:Original post by ToohrVyk
It's known as RAII, and is generally considered to be the "best practice" in C++ as far as handling resource release is concerned.
...
In the end, the "wrap once, reuse everywhere" approach seems, at least to me, more appealing than the "use 'finally' everywhere", and is quite possibly the reason C# introduced the 'using' keyword to avoid the 'finally' infestations of Java.


Right, I'm not meaning to replace RAII with 'finally'. You seem to have missed the point. The original idea was just to specify post-conditions for design-by-contract; implementing the 'finally' keyword was just a bonus. I'm not in the school of thought that you should take away some tools from programmers to make them use other tools; they should have everything available and choose according to the task at hand. I didn't automatically cross out using 'finally' for something other than assertions because it can still be useful in some cases (although not many since RAII would be prefered, as you pointed out). And also because removing that functionality artificially is impossible. It's a block of code, you can put assert()'s there, or anything else; I can't stop you.

The primary use would be, as in the code example I gave earlier:

double my_sqrt(double x) {	double res;	// pre-conditions:	assert( x > 0 );	// post-conditions:	OnExit2(double,x, double,res, {		assert( res * res == x );	});	// main function body	res = sqrt(x);	return res;}


So don't get too hung up on this replacing RAII. Its primary function is to enable design-by-contract.

Quote:Original post by Antheus
Of course, once an instance is constructed, non-const methods may corrupt internal state. Both C# and Java make extensive use of immutable types. In general, immutable types (in C++ created through RAII) make it considerably simpler to maintain. In some ways, they represent invariants.


And when implementing those methods, to make sure that the type is immutable, you should use a methodology that catches inconsistent states early such as design-by-contract. See the example above (it's a bit trivial but I hope it gets the point across).
Quote:Original post by Jotaf
Quote:Original post by silvermace
If all you want is pre-post assertions then maybe you could write a quick processing tool which could do some trivial source-to-source translation before you run your compilation [...].


Yeah I thought about that. It's actually a neat idea. But the only implementation I know of is in C, and it's a much bigger effort to write and set-up a pre-processor than working within the confines of the language itself. If I had more time on my hands, I'd probably do that. Or if it was freely available already.
That makes sense. Just FYI, you got me thinking if writing a light-weight annotation system for C++ might be worth it (or even just for some light hearted fun) so I got GCC_XML (emulating gcc-4.2) working on my machine and im shelling out an initial system to do what you want as the first use case, feel free to drop me a line silvermace at gmail dot com and I'd be more than happy to stay in touch and share findings.

More towards solving your problem, You got me thinking about the Pimpl pattern. But you would be faced with 2 implementations of each class, not ideal and personally this wouldn't justify the effort unless you want the other things Pimpl is intended for.

I'm starting to think there's no easy way to do this with C++, I am sitting here with eclipse open trying to see if there is a way to use boost::bind and boost::lambda to try specify the post assertions, which can then be called on destruction (which is exception safe), but there are some real limitations on what you can achieve succinctly with boost::lambda... anyway home time, I will keep fiddling with this because you've struck a chord with me re: the D-B-C and injectability of C++
"I am a donut! Ask not how many tris/batch, but rather how many batches/frame!" -- Matthias Wloka & Richard Huddy, (GDC, DirectX 9 Performance)

http://www.silvermace.com/ -- My personal website
Quote:Original post by silvermace
...


The boost libraries seemed like the obvious approach at first but as I mentioned before (and as reported in a page I linked to) massive use of lambdas really slows down both compilation and execution. Are you sure Pimpl is of any use here? :)

Anyway, GCC-XML could be a good bet for an annotation system. It would be easy to spot relevant code such as the beggining or end of a function (or method), return statements, etc. The annotations would probably reside in comments and I think it strips out those, but the original file can be referred to in this case. So the method could be: locate function headers in the generated XML file, go to the associated source line and see there if there are any relevant comments/annotations; get code from annotations and insert them in the output file.

I'm thinking of annotations such as pre or post-conditions, like in that C implementation. An invariant is just one condition that is duplicated both as pre and as post-condition. The C annotation system has its own mini-language to specify conditions, but I think that converting it to an assert would be more in the spirit of DBC. There are only so many things you can check at compile-time, and to check something at run-time the C++ language is already in place. Could be something like this:

//pre: x > 0//post: res * res == xdouble my_sqrt(double x) {	double res;	res = sqrt(x);	return res;}


Thinking about it, a useful feature would be to convert "return" as a keyword to the value being returned so that instead of having a temporary variable "res" the post-condition could be just "return * return == x".

Any other useful annotations you can think of? I don't mean to replace Doxygen BTW :) It would probably be more useful to think of annotations that can be translated to code, than text or documentation annotations since that's already provided in other programs.
Quote:Original post by Jotaf
Thinking about it, a useful feature would be to convert "return" as a keyword to the value being returned so that instead of having a temporary variable "res" the post-condition could be just "return * return == x".


Except that this particular post-condition would almost never be met ;)
Quote:Original post by Zahlman
Quote:Original post by Jotaf
Thinking about it, a useful feature would be to convert "return" as a keyword to the value being returned so that instead of having a temporary variable "res" the post-condition could be just "return * return == x".


Except that this particular post-condition would almost never be met ;)


Yes of course, it's a bit redundant!
I tested the system by adding a "res++" after the sqrt(), and of course the post-condition failed. It's trivial but it's just a test-case, I could quite easily dig for a more complicated piece of code and add pre- and post-conditions there, but you'd gain nothing except having to read through more code :)

---

A possible issue I just thought of:
Since we're using destructors, wouldn't it be possible for an object we're testing to be destroyed and cleared-out before we make the test? Concerning order of destruction I've seen conflicting sources; some said the order is undefined, while others say it's inverse of the order of construction. Which makes sense, but if it's undefined then not all compilers would guarantee that.

Anyways, this means that the Guard must be constructed before the other objects. But this would make it quite difficult for it to keep references to other objects, since they'd come only after the guard declaration. If I do something like this:

Object x, y, z;Guard guard(&x, &y, &z);


...the objects will be destroyed before the guard tries to make some assertions about them; while if I do this:

Guard guard(&x, &y, &z);Object x, y, z;


...it's not even legal C++ code.

Jotaf

This topic is closed to new replies.

Advertisement