Hacker News new | past | comments | ask | show | jobs | submit login

Unfortunately, although there's no particular reason for it, you're not allowed to write a method that takes only variadic arguments. This doesn't work:

    - (NSString *)format: ...;
It would be oh-so convenient, meaning you could write formatting operations like this:

    newstr = [@"%d %@ %s" format: 42, @"blah", "yep"];
Given the constraints we have to work with, I'd say your best bet would be to simply write a function:

    newstr = fmt(@"%d %@ %s", 42, @"blah", "yep");



Comedy option:

    - (NSString *)format:(id)first, ... {
        NSUInteger argCount = 0;
        BOOL prevPercent = NO;
        for (NSUInteger i = 0; i < [self length]; ++i) {
            unichar c = [self characterAtIndex:i];
            if (prevPercent && c != '%')
                ++argCount;
            prevPercent = c == '%';
        }

        NSMutableArray *argArr = [NSMutableArray new];
        va_list args;
        va_start(args, first);
        for (NSUInteger i = 0; i < argCount; ++i)
            [argArr addObject:va_arg(args, id)];
        va_end(args);

        switch (argCount) {
            case 0: return [NSString stringWithFormat:self, first];
            case 1: return [NSString stringWithFormat:self, first, argArr[0]];
            case 2: return [NSString stringWithFormat:self, first, argArr[0], argArr[1]];
                // ...
        }
    }
Only supports NSObject arguments, obviously. If you actually wanted to do this approach you'd want to do a better job of parsing the format string and use a more cleverer way of storing the args.

Or, for the completely insane approach that probably doesn't actually work (on top of being entirely unportable):

    - (NSString *)format:(id)first, ... {
        NSUInteger argCount = ...;

        va_list args;
        va_start(args, first);
        void *argList = malloc(sizeof(id) * (argCount + 1));
        memcpy(argList, (void*)&first, sizeof(id));
        memcpy(argList + sizeof(id), &va_arg(args, void *), sizeof(id) * argCount);
        va_end(args);

        NSString *ret = [[NSString alloc] initWithFormat:self arguments:argList];
        free(argList);
        return ret;
    }


Why not just have it be a static method like stringWithFormat?

  + (id)stringWithFormat:(NSString *)format, ...;
This of course already exists, but if you wanted to do a Rubyfied version of it, then just follow the same general pattern.


The problem people have with the format implementation is that no matter what you do, it's either obtuse shorthand, or incredibly verbose.

The reality is, that NSString needs native formatting sugar. Anything else would really just be a hack. I'm not particularly even happy with the [@"Hello ":@"World] syntax, either.


Here's a crazy idea:

    newstr = [@"%d %@" format 42, @"blah"];
Implement it as a method that takes a dummy parameter, plus a macro:

    - (NSString *)format:(int)dummy, ...
    #define format format:0,
Awful way to do it, but the resulting syntax is not entirely terrible.


I'm not super comfortable with breaking message syntax like that. Also, very wary of using a #define with a short oft used keyword in a library that will be imported in majority of headers.


Exactly I just want this:

  NSString *newS = @"blah " + @(42) + @"!";


Unfortunately, something like "fmt()" isn't really elegant enough, or "objective-c" enough.




Consider applying for YC's Spring batch! Applications are open till Feb 11.

Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: