Thursday, December 11, 2003


It has long irritated me that #define macros in C did not support variable argument lists -- varargs. GCC has apparently had support for variadic macros for quite some time, but I wasn't aware of it and it would incur a portability issue anyway.

As it turns out, Variadic Macros are now a part of the C99 spec. Very cool. This means we can eliminate such annoying cruft as NSAssert(), NSAssert1(), NSAssert2() ... NSAssert5() and the should() and should1() along with all the rest of the silly 1...N macros that have cropped up in C code so many times over the years.

An example:

Defining the "variadic macro"*:

#define logmethod(...)  NSLog(@"%s:%d [%@ (0x%x) %@%@] %@", 
                  __FILE__, __LINE__, 
                  NSStringFromClass([self class]), self, 
                  [self class] == (void *)self ? @"+" : @"-", 
                  NSStringFromSelector(_cmd), 
                  [NSString stringWithFormat: __VA_ARGS__])

Using the macro:

@interface Foo:NSObject
+ (void) doIt;
- (void) doIt;
@end
@implementation Foo
+ (void) doIt {
    logmethod(@"Doing it in '%@' for '%@'",
              [[NSFileManager defaultManager] currentDirectoryPath],
              NSFullUserName());
}

- (void) doIt { logmethod(@"Doing it in '%@' for '%@'", [[NSFileManager defaultManager] currentDirectoryPath], NSFullUserName()); } @end

.... [Foo doIt]; [[Foo new] doIt]; ....

The output (wrapped for browser sanity):

2003-12-11 23:17:31.827 vargtest[12565] /tmp/vargtest/main.m:17
	[Foo (0x9b054) +doIt] Doing it in
	'/build/bbum-products' for 'Bill Bumgarner'
2003-12-11 23:17:31.830 vargtest[12565] /tmp/vargtest/main.m:23
	[Foo (0x30c760) -doIt] Doing it in
	'/build/bbum-products' for 'Bill Bumgarner'

Excellent.

* They should be called "really obvious and long overdue macros". Why did it take this long? It is just a change to the preprocessor and goodness knows the world has suffered through enough #define Foo[0...N](x [1, [2, [3[, ...]]]) abuses that it was warranted long ago.

hrm

[self class] == (void *)self

The above is the expression I'm using to determine if the currently executing method is a class or instance method. I had originally been using the flags available via the object's isa, but ran into a compiler warning that would not go away.

The above works, but is not exactly the most expressive fragment of code.

Suggestions welcome.

Update:

There is one really annoying situation for which I haven't found a decent workaround. In particular, you generally need to pass at least one argument for the variadic marker for most istuations.

That is, if the __VA_ARGS__ is being used in a context like the following:

#define foo(f, ...) [NSString stringWithFormat: 
    [composedHeader stringByAppendingString: f], __VA_ARGS__]

Then you have to pass in at least one argument in place of the ....

That sucks. There seem to be a bunch of postings that sort of impl

But, lo, the designers of the feature thought of this and provided a solution. Define the above as:

#define foo(f, ...) [NSString stringWithFormat: 
    [composedHeader stringByAppendingString: f], ##__VA_ARGS__]

The ##__VA_ARGS__ tells the preprocessor to delete the comma if there were no variadic args passed; syntax fixed.
11:20:16 PM  pontificate