mikeash.com: just this guy, you know?

Posted at 2011-08-19 14:40 | RSS feed (Full text feed) | Blog Index
Next article: Friday Q&A 2011-09-02: Let's Build NSAutoreleasePool
Previous article: See Me Speak at Voices That Matter in Boston
Tags: c fridayqna objectivec
Friday Q&A 2011-08-19: Namespaced Constants and Functions
by Mike Ash  

The inevitable rotation of the Earth means that it's once again time for another Friday Q&A. For today's edition, Jose Vazquez suggested that I discuss namespaced constants and functions in C.

Traditional Constants
There are three standard techniques for creating constants in C: #define, const global variables, and enum.

The use of #define is straightforward. Simply create a macro with the name and value you want to use:

    #define MAAnswerToTheQuestion 42
    #define MAThingDidChangeNotification @"MAThingDidChangeNotification"

The main downside to this approach is that it doesn't interact nicely with the debugger. Since #define is a preprocessor construct, the debugger doesn't see MAAnswerToTheQuestion, just 42, so trying to get it to print the value of MAAnswerToTheQuestion won't work. It's also a bit ugly, and you can get into some serious trouble if you forget to parenthesize a more complicated expression like #define MAAnswerToTheQuestion 40 + 2.

Global variable constants are a little more complex, but should be familiar since they're the same as a regular global variable, just with the const keyword added. This follows a more typical C pattern of a declaration in the header, then a definition in the implementation file. The header looks like this:

    extern const int MAAnswerToTheQuestion;
    extern NSString * const MAThingDidChangeNotification;

Note the odd position of the const keyword in the second one. Writing the more natural type of const NSString * won't have the intended effect. That declares a pointer to a const NSString, when what we want is a const pointer to a regular NSString.

The definition in the implementation file is much the same, minus the 'extern' and with an initializer:

    const int MAAnswerToTheQuestion = 42;
    NSString * const MAThingDidChangeNotification = @"MAThingDidChangeNotification";

This technique is a little more wordy but works more like you might expect it to. The symbols will be visible to the debugger and won't have strange interactions with the preprocessor.

The enum technique only works for integers, but can be nice for them:

    enum MAConstants
    {
        MAAnswerToTheQuestion = 42,
        MAMusketeers = 3,
        MACircumferenceOfEarth = 199211 // in furlongs
    };

This ends up being reasonably nice, but is somewhat unnatural for constants which aren't part of a single group, as enums are typically used.

Namespaced Constants
Like other global symbols in Objective-C, it's normal to prefix constants with the name of the class that they're associated with or other pertinent info. This not only helps organize the constants and tell the reader where they're from, but also prevents inadvertent naming conflicts between two constants with a similar purpose from different sections of the program.

As an example, let's imagine a class called MANotifyingArray which posts notifications when its contents are altered. The names of these notifications are declared as string constants, along with some userInfo keys for more information:

    const NSString * MANotifyingArrayDidAddObjectNotification;
    const NSString * MANotifyingArrayDidChangeObjectNotification;
    const NSString * MANotifyingArrayDidRemoveObjectNotification;

    // userInfo key pointing to NSNumber
    const NSString * MANotifyingArrayIndexChangedKey;

    // userInfo key containing the changed object
    const NSString * MANotifyingArrayObjectChangedKey;

And then the implementation file would have pretty much the same thing again, slightly different. This is really verbose and ugly, and there's a ton of repetition.

I do a fair bit of Python programming as well, and in Python I can stuff constants into a class to reduce redundancy while preserving organization and preventing conflicts. The above might look like this in Python:

    class MANotifyingArray:
        class notifications:
            didAddObject = 'didAddObject'
            didChangeObject = 'didChangeObject'
            didRemoveObject = 'didRemoveObject'

            class keys:
                indexChanged = 'indexChanged'
                objectChanged = 'objectChanged'

These values could then be accessed by writing code like MANotifyingArray.notifications.didAddObject or MANotifyingArray.notifications.keys.indexChanged. This is much easier to read and write and also gives us a nice hierarchy.

Wouldn't it be nice if we could do this in Objective-C as well? It turns out that we can, or very nearly.

It's not possible to use a class for this purpose as the Python example does, but we can use a struct. The notifications above would look like this:

    // in the header
    extern const struct MANotifyingArrayNotificationsStruct
    {
        NSString *didAddObject;
        NSString *didChangeObject;
        NSString *didRemoveObject;
    } MANotifyingArrayNotifications;

    // in the implementation
    const struct MANotifyingArrayNotificationsStruct MANotifyingArrayNotifications = {
        .didAddObject = @"didAddObject",
        .didChangeObject = @"didChangeObject",
        .didRemoveObject = @"didRemoveObject"
    };

The .didAddObject = ... syntax is a new feature of C99 which allows explicitly initializing individual fields of a struct. It's not strictly necessary here, but it makes this code considerably more clear.

These constants can then be accessed by writing code like MANotifyingArrayNotifications.didAddObject. How nice!

We can go further and even replicate the nested hierarchy of the Python version. Here's what the code looks like with that added in:

    // in the header
    extern const struct MANotifyingArrayNotifications
    {
        NSString *didAddObject;
        NSString *didChangeObject;
        NSString *didRemoveObject;
        struct
        {
            NSString *indexChanged;
            NSString *objectChanged;
        } keys;
    } MANotifyingArrayNotifications;

    // in the implementation
    const struct MANotifyingArrayNotifications MANotifyingArrayNotifications = {
        .didAddObject = @"didAddObject",
        .didChangeObject = @"didChangeObject",
        .didRemoveObject = @"didRemoveObject",
        .keys = {
            .indexChanged = @"indexChanged",
            .objectChanged = @"objectChanged"
        }
    };

The nested constants can then be accessed just as you would expect, with NSNotifyingArrayNotifications.keys.indexChanged and similar.

Overall, this ends up working nicely. A great deal of the redundancy of the standard global variable approach is eliminated, and the constants are placed in a hierarchy that's easy to understand.

It's certainly not perfect. There's substantial redundancy in having to declare a separate struct type in addition to the variable. However, the pros substantially outweigh the cons in my view.

Namespaced Functions
But wait, there's more! The same basic technique can be used to create namespaced functions as well. Plain functions aren't found as often in typical Cocoa/Objective-C code, but they still show up. Cocoa itself contains many helpful functions like NSSelectorFromString, NSPointInRect, and the ubiquitous NSLog.

We can use the same basic struct technique to namespace these functions. The struct will contain function pointers, and then when initializing the struct, we'll just have to implement the function privately and then point the function pointer at the real function.

For an example, let's make a few extra utility functions for NSRect:

    extern const struct MARectUtils
    {
        NSPoint (*topLeft)(NSRect r);
        NSPoint (*topRight)(NSRect r);
        NSPoint (*bottomLeft)(NSRect r);
        NSPoint (*bottomRight)(NSRect r);
    } MARectUtils;

For the implementation, we'll write these as normal, static functions, and then finally initialize the struct with them:

    static NSPoint topLeft(NSRect r)
    {
        return r.origin;
    }

    static NSPoint topRight(NSRect r)
    {
        return NSMakePoint(NSMaxX(r), NSMinY(r));
    }

    static NSPoint bottomLeft(NSRect r)
    {
        return NSMakePoint(NSMinX(r), NSMaxY(r));
    }

    static NSPoint bottomRight(NSRect r)
    {
        return NSMakePoint(NSMaxX(r), NSMaxY(r));
    }

    const struct MARectUtils MARectUtils = {
        .topLeft = topLeft,
        .topRight = topRight,
        .bottomLeft = bottomLeft,
        .bottomRight = bottomRight
    };

Code can call these functions like this:

    NSPoint corner = MARectUtils.bottomRight([self frame]);

Just like with constants, this provides nice grouping and categorization for these functions.

Conclusion
Namespacing constants and functions in C is an unusual technique, and likely to frighten people who aren't comfortable seeing new things. However, I believe it could make code cleaner and easier to understand, and in any case is worth some consideration.

That wraps things up for today. Come back in two weeks for the next one. As always, Friday Q&A is driven by reader ideas, so if you have a topic that you would like to see covered here, send it in!

Did you enjoy this article? I'm selling a whole book full of them. It's available for iBooks and Kindle, plus a direct download in PDF and ePub format. It's also available in paper for the old-fashioned. Click here for more information.

Comments:

Brian Mastenbrook at 2011-08-19 14:46:32:
Maybe I'm alone in this, but I tend to use static const for most of my constants. It's a good middle ground between preprocessor defines and extern const (which can't be constant folded by the compiler).

Regarding your crazy structs idea, I think it'd be a good idea to fold this kind of thing into the language. Maybe such a language could feature both an explicit namespace feature and an enhanced form of structs with inheritance, visibility control, etc. Someone should get on that!

mikeash at 2011-08-19 15:04:07:
Is there a way to use static const globals in your headers without getting warnings all over the place about them not being used in every single file where they're imported?

Bill at 2011-08-19 15:18:19:
#define constants are only a problem in the debugger if you're stuck with Xcode. I use them quite happily in IntelliJ AppCode.

Bill at 2011-08-19 15:37:51:
But the namespaced constants trick with structs is really cool - thanks!

Brian Mastenbrook at 2011-08-19 15:50:52:
I have a #define for __attribute__ ((unused)) that I use on static consts.

Jamie at 2011-08-19 15:51:03:
Unfortunately you still don't get the best part of namespaces, the resolution of unprefixed symbols.

Why Apple went to all the trouble of adding KVC dot-notation when they could have added namespaces I just don't know. I'm sure they had their reasons, but on Tiger I don't recall ever saying to myself "Gah I hate having to type 'objjctForKey:' over and over!" but I DO still say to myself "Gah what should I prefix my class names with on THIS project?"

Someone at 2011-08-19 15:54:21:
...but the whole point of a namespace is to prevent collisions. While the struct idea prevents collisions within itself, you still end up naming the struct itself. The name of the struct may collide.

mikeash at 2011-08-19 16:30:47:
Someone: I don't understand what you're getting at. Even with true namespaces, those can still collide if they have the same name too.

elliottcable at 2011-08-19 19:16:39:
Yeah, when you demonstrate the idea tentatively, it gets called “really cool.”

When I do it in practice, and have been for years, I get called “insane,” “iditotic,” and other insulting intonations starting with “i.”

But, onto the subject; here’s quite a few examples of this applied in real-life:

My equivalent of a “header”: https://github.com/elliottcable/Paws.c/blob/323628/Source/Types/fork/LL.c#L33-63
The runtime initialization of the struct with the relevant functions: https://github.com/elliottcable/Paws.c/blob/323628/Source/Types/fork/LL.c#L83-127
Finally, the implementation of the functions themselves: https://github.com/elliottcable/Paws.c/blob/323628/Source/Types/fork/LL.c#L128-229

Danny at 2011-08-20 03:16:02:
First up, I love NSBlog. Been with ObjC for 5 months now and learned so much from this site. Thank you so very much for sharing.

As usual, this is another excellent post! Very intriguing. Now I want to refactor all my #defines and consts with namespaced constants! :) (Honestly, NSBlog is one of my main sources where I find inspirations/motivations for refactoring my code. I learn the techniques here and apply them to my code. It's the Friday I most look forward to :)

In regards to Namespaced Functions, I have a question for Mike and all you experts:

Although "namespaced functions" seem cool, I wonder if it's all that practical in the world of Cocoa. If I'm writing a project mostly in C (not taking advantage of the Cocoa framework), I can definitely see the benefit of namespaced functions. But if I'm writing an object-oriented solution, wouldn't it be better to simply write a wrapper class? Even though a class in ObjC doesn't give you the namespace notation, it offers so much more, especially when considering the principle of Separation of Concern? I guess my real question is: is there a scenario where I can use this technique (namespaced functions with struct) even in a project that's entirely composed in classes? Please enlighten me :) Thanks!

mikeash at 2011-08-20 17:19:04:
I'm glad you enjoy the blog!

Regarding namespaced functions, I completely agree that writing classes is generally going to be a better approach. Namespaced functions aren't a replacement for classes, but rather a small improvement on regular C functions when used in this context. As you've no doubt noticed, C functions aren't very commonly used in Cocoa code. Where they *are* used, this technique could come in handy, but it's not going to happen very often.

If you wanted to go completely overboard, you could actually do a sort of namespaced class by putting a Class variable in a struct and then assigning it to a real class. But, probably not a good idea there.

Kentzo at 2011-08-20 22:20:12:
I know you hate c++ Mike, but its namespaces are really good solution.

mikeash at 2011-08-20 23:29:13:
They would be a great solution if I could pull them in without getting any other C++ stuff, or if C++ were a real superset of C. :-)

Lane Roathe at 2011-08-23 14:28:50:
For what it's worth, Mike's solution is, at the most basic level, nearly identical to a namespace. ie:

namespace MANotifyingArrayNotifications
    {
        NSString *didAddObject;
        NSString *didChangeObject;
    }

    MANotifyingArrayNotifications::didAddObject


is equivalent (more or less) to:

struct MANotifyingArrayNotifications
    {
        NSString *didAddObject;
        NSString *didChangeObject;
    };

    MANotifyingArrayNotifications.didAddObject


Of course, namespaces are more flexible, but are not less (or more) prone to collision (ie, in either you can name an object at level X the same as another object at level X), although the flexibility of namespaces does make it easier to contain disparate items vs using a struct (ie, no need for a function to assign the values for instance).

Andrey Mishanin at 2011-08-26 11:27:36:
Mike, your solution is fine except for it won't interplay nicely with ARC since you're declaring objects inside a struct.

Sean M at 2011-08-27 00:55:42:
objects inside structs can be a problem for GC too, depending on wether the struct is allocated in scannable memory.

mikeash at 2011-08-27 02:21:13:
It works fine in ARC, you just need to prefix any ObjC object members with __unsafe_unretained. Regarding GC, constant objects (the only kind you can put inside these structs) are not a problem.

Jean-Daniel at 2011-08-28 08:48:45:
I like this idea, but I see a little drawback.
When you add a new value in the header and you forget to add it in the implementation file, the compiler will no detect the error until runtime. If you use standard globals, the linker will tell you there is a missing symbol.

You may expect a "missing field initialization" warning in the struct definition, but unfortunately, using the c99 syntax inhibits such warning.

alexey at 2011-08-28 14:34:55:
Wrong example, your forgot that notification should have full name to differ with other and a struct doesn't help with it.

    const struct MANotifyingArrayNotifications MANotifyingArrayNotifications = {
        .didAddObject = @"MANotifyingArrayDidAddObject",
        .didChangeObject = @"MANotifyingArrayDidChangeObject",
        .didRemoveObject = @"MANotifyingArrayDidRemoveObject",
        .keys = {
            .indexChanged = @"MANotifyingArrayIndexChanged",
            .objectChanged = @"MANotifyingArrayObjectChanged"
        }
};


And after that I think struct doesn't give any pros.

Clay Bridges at 2011-12-09 20:28:11:
I created a hack version of this for enum, and put the code on github (link below). I use a struct to mirror the enum, and use that to access the enum values using dot syntax. The include side would look like:

// your usual enum
typedef enum StoogeType {
    StoogeTypeMoe = 0,
    StoogeTypeLarry = 1,
    StoogeTypeCurly = 2
} StoogeType;

// a mirroring struct
struct StoogeStruct {
    StoogeType moe;
    StoogeType larry;
    StoogeType curly;
};

// how it gets accessed
extern const struct StoogeStruct StoogeTypes;

Then you could write something like

StoogeType stooge = StoogeTypes.moe;

 Right now, it seems like a lot of work for little benefit, so I doubt the practical use, but ... good to know.

Link: https://github.com/claybridges/EnumDotSyntax


Comments RSS feed for this page

Add your thoughts, post a comment:

Spam and off-topic posts will be deleted without notice. Culprits may be publicly humiliated at my sole discretion.

Name:
Web site:
Comment:
Formatting: <i> <b> <blockquote> <code>. URLs are automatically hyperlinked.
Code syntax highlighting thanks to Pygments.
Hosted at DigitalOcean.