Double braces for start/end blocks

My friend Demitri showed me a coding technique he uses when he has a bunch of lines that need to be preceded by a "start" statement of some sort and followed by an "end" statement of some sort.

A recent example in my own code looks roughly like this:

[self startOutput];  // top-level "start"
 
[self startImageAssets];
[self addImageAssets];
[self endImageAssets];
 
[self startThumbnailAssets];
[self addThumbnailAssets];
[self endThumbnailAssets];
 
[self startAudioAssets];
[self addAudioAssets];
[self endAudioAssets];
 
[self endOutput];  // top-level "end"

The problem is that the "start" and the "end" may be far enough apart that it's not visually apparent that the lines in between are logically nested. In some cases, though not so much in this example, it can be easy to forget to add the "end" statement.

One approach would be to wrap all the interior stuff into a method, so it collapses into one line:

[self startOutput];
[self outputAllAssets];
[self endOutput];

This follows the excellent principle of Keeping Methods Short, and now the beginning/middle/end sequence is clear. But this isn't always convenient, and I don't always prefer it over inlining the intermediate statements.

I've seen code that indents the internal statements like this:

[self startOutput];
 
    [self startImageAssets];
    [self addImageAssets];
    [self endImageAssets];
 
    [self startThumbnailAssets];
    [self addThumbnailAssets];
    [self endThumbnailAssets];
 
    [self startAudioAssets];
    [self addAudioAssets];
    [self endAudioAssets];
 
[self endOutput];

I didn't indent the "add" statements because each of those groups of three lines is short enough that it's obvious what's going on, and I think indenting the middle lines would make the code harder to read, at least to my eye.

The problem with this approach is that when you tell Xcode to Re-Indent, you will lose all your custom indentation. (Side note: I'm not sure, but it seems to me "Re-Indent" in the Xcode menu should be "Re-indent".)

Demitri's solution is to put the internal statements into a C block, like this:

[self startOutput];
{
    [self startImageAssets];
    [self addImageAssets];
    [self endImageAssets];
 
    [self startThumbnailAssets];
    [self addThumbnailAssets];
    [self endThumbnailAssets];
 
    [self startAudioAssets];
    [self addAudioAssets];
    [self endAudioAssets];
}
[self endOutput];

This works nicely, but one teeny-weeny thing about it bothers me. When I see that opening brace it feels like I'm missing a line, like maybe I accidentally deleted an "if" or a "while". I could probably get used to this, especially since the "start" in the line above is a strong clue. But just to help myself see my intentions, I've taken to using double curly-braces:

[self startOutput];
{{
    [self startImageAssets];
    [self addImageAssets];
    [self endImageAssets];
 
    [self startThumbnailAssets];
    [self addThumbnailAssets];
    [self endThumbnailAssets];
 
    [self startAudioAssets];
    [self addAudioAssets];
    [self endAudioAssets];
}}
[self endOutput];

Now when I scan the code I can clearly see that the reason for the block was to indicate logical nesting.

This technique can also be useful for Create/Release pairs in CF code, or alloc/release pairs in Cocoa or iOS code. Or for those times when you create your own autorelease pool:

NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
{{
    // Autoreleased objects here go into the local pool.
}}
[pool drain];  // Less likely you'll forget to drain the pool.

I believe the example Demitri showed me was a begin/commit animation block in his iOS code. With the double braces it would look like this:

[UIView beginAnimations:animationID context:context];
{{
    // Add an animation to the animation block.
    // Add an animation to the animation block.
    // ...
}}
[UIView commitAnimations];

One concern is that someone else reading my code might be confused if they're not familiar with the convention I adopted. Ideally it would turn out I wasn't the first to think of the double-braces approach, and actually lots of people do it this way. But I haven't come across it. Maybe most people take the first approach I mentioned and create a separate method or function.

I think of the double braces sort of like the double parens in "old-style" init methods (which are not how Apple does them any more, but perfectly fine technically):

if ((self = [super init]))
{
    // ...
}

The double parens avoid a warning from the compiler that you might have meant == instead of =. My double braces avoid a "mental compiler warning" that I might be missing an "if" or a "while".

7 thoughts on “Double braces for start/end blocks

  1. I've used this technique for decades, and it's actually pretty common. I'm one of the "brace is on the same line as the thing that opens it", so things look like:

    CGContextSaveGState (context); {
        CGContextSetLineWidth (context, kStrokeWidth);
        CGContextSetStrokeColorWithColor (context,
                                          [[UIColor lightGrayColor] CGColor]);
        CGContextAddPath (context, path);
        CGContextStrokePath (context);
    } CGContextRestoreGState (context);

    (modulo what the commenting system will or won't do to code). Very concise and obvious what's going on.

    For a "brace on its own line" style, I'm not confused by the brace not being nestled to an obvious control structure. It's merely saying "here's a new scope". The double braces would confuse me the first time I saw them in real code until I figured out what was going on.

  2. I actually like the use of blocks for that purpose:

    -(void)performOutput:(void (^)(void))anOutputBlock {
        [self beginOutput];
        anOutputBlock();
        [self endOutput];
    }

    which in turn would make your code look like this:

    [self performOutput:^{
        [self startImageAssets];
        [self addImageAssets];
        [self endImageAssets];
     
        [self startThumbnailAssets];
        [self addThumbnailAssets];
        [self endThumbnailAssets];
     
        [self startAudioAssets];
        [self addAudioAssets];
        [self endAudioAssets];
    }];>

    I did so in my TCMXMLWriter https://github.com/monkeydom/TCMXMLWriter

  3. Belated thanks for the comments. Dominik, I cleaned up the mess that WordPress made of your code. (Also I did see your pull request, I just haven't had a chance to get to it.)

  4. Short note, I prefer the following to double-braces:

    do_this_to_start() ;{
     
    // … internals
     
    }; do_this_to_end()

    This comes from my history with JavaScript, and a tendancy towards comma-first and dependance upon semicolon-first to circumvent semicolon-insertion rules (see Michaeljohn Clement’s article: http://inimino.org/~inimino/blog/javascript_semicolons)

  5. I have actually been using the Demitri's solution for a long time, as it seemed a natural way to define and read a block of code. While in some cases a separate method or function makes sense, breaking a single-use code chuck out into a new method seemed really over-weight.

    One thing that has made it easy for myself is that I tend to follow a coding style that puts all starting braces on the next line, which does not feel common these days but I find easier to read.

    I do like the look of the double-quote, easy to see a difference between logical (if) or iterated (for/while) and a simple code chunk. Good ideas! Thanks for posting.

  6. I, too have been using this technique to 'bracket' a section of code that has a common purpose, but doesn't warrant its own method.

    An extra benefit of this is that you can declare variables inside the brackets that are scoped to it, so not available outside.

    This helps in case/switch statements as well where you need to declare a variable inside a particular case.

  7. Well I'll happily say I've never used this technique before but it looks really, really intriguing…. I'm sitting here wondering why something like this never occurred to me before.

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.