8

Compiling my iOS application's code for arm64 I faced an interesting problem, related to different basic types for custom Foundation types. Say I want to printf (or stringWithFormat) a number declared as NSUInteger

[NSString stringWithFormat:@"%u", _depth,

This will produce a warning compiling for arm64, because NSUInteger declared as unsigned long for arm64. Therefore, I should replace "%u" with "%lu", but now this becomes invalid when compiling for armv7(s) architecture, because for 32-bit architectures NSUInteger declared as unsigned int. I'm aware that warning says "NSUInteger should not be used as format argument", so lets proceed to floats:

typedef CGFLOAT_TYPE CGFloat;

on 64-bit CGFLOAT_TYPE is double, while on 32-bit it is float. Therefore, doing something like this:

- (void)foo:(CGFloat)value;

and then

[self foo:10.0f]; 
[self foo:10.0]; 

Will still produce a warning when compiling for two architectures. On 32-bit architecture second call is not correct (conversion from double to float), on 64-bt architecture, the first one converts float to double (which is ok, but still not good).

Would love to hear your thoughts on this problem.

Serhii
  • 405
  • 1
  • 4
  • 11

1 Answers1

11

One (admittedly awful) approach I've seen used is to use the magic of #define and compile time string literal concatenation. Like this:

// In your prefix header or something
#if __LP64__
#define NSI "ld"
#define NSU "lu"
#else
#define NSI "d"
#define NSU "u"
#endif

// Then later you can use these like this...
NSString* foo = [NSString stringWithFormat:@"%"NSU" things in the array.", array.count]);

Pretty horrible, but it works.

Another, seemingly more common approach is to simply upcast the value to the larger type on every platform, like this:

NSString* foo = [NSString stringWithFormat:@"%lu things in the array.", (unsigned long)array.count]);

More recently (i.e. since the new boxing shorthand syntax came out) I've found myself being lazy and have started just boxing everything, like this:

NSString* foo = [NSString stringWithFormat:@"%@ things in the array.", @(array.count)]);

There may be a better way, but those are the ones I've seen the most.

ipmcc
  • 29,581
  • 5
  • 84
  • 147
  • 4
    It should perhaps be mentioned that the upcast is only formal (to make the compiler happy), but does not really change anything, because `sizeof(unsigned long) == sizeof(NSUInteger)` on both 32- and 64-bit architecture. – Martin R Sep 17 '13 at 11:50
  • Auto-completion suggests exactly upcast (i.e %lu and (unsigned long)value) – Serhii Sep 17 '13 at 11:52
  • I'm confused. How can you define `NSI`/`NSU` at compile time and then magically the right one for each architecture will be chosen at runtime? – Rivera Mar 12 '14 at 01:59
  • The choice isn't happening at runtime. All the choices are happening at compile time. `NSInteger` and `NSUInteger` are also defined at compile time, this is just mimicking/paralleling that behavior. – ipmcc Mar 12 '14 at 12:28