Does the ternary operator work in constant definition? - objective-c

Why do I get a compiler error when using a ternary operator in the assignment of a CGSize const like this?
CGSize const ksizeSmall = SOME_BOOLEAN_VARIABLE ? {187, 187} : {206, 206};
it does work like this...
CGSize const ksizeSmall = {187, 187};
however, I want to add a boolean expression to evaluate whether I should use one size vs. the other size. I don't want to use if / else because I have a long list of CGSize to set specifically for different purposes.

{187, 187} and {206, 206} aggregates are valid as an initialization expressions, but not as a general-purpose expressions*. That is why a ternary operator does not allow it.
If you are making an initializer for a local constant, you could use CGSizeMake:
CGSize const ksizeSmall = SOME_BOOLEAN_VARIABLE ? CGSizeMake(187, 187) : CGSizeMake(206, 206);
If SOME_BOOLEAN_VARIABLE is a compile-time constant expression, you could use conditional compilation instead:
#if SOME_BOOLEAN_VARIABLE
CGSize const ksizeSmall = {187, 187};
#else
CGSize const ksizeSmall = {206, 206};
#endif
* gcc compiler has a C language extension that offers a special syntax for doing this. It was available in Objective-C as well. However, this extension is not part of the language.

Related

How to determine if undocumented value for NS_ENUM with Swift 1.2

For instance, following NS_Enum is defined...
typedef NS_ENUM(NSInteger, Type) {
TypeNone = 0,
TypeA = 1,
}
var x = 2
if let type: Type = Type(rawValue: x) {
// Swift 1.2 executes this block.
}
else {
// Previous swift executes this block.
}
How can I determine if x is defined on NS_ENUM or not?
I assume this is a consequence of the following change in Swift 1.2, documented in
the Xcode 6.3 release notes:
Imported NS_ENUM types with undocumented values, such as
UIViewAnimationCurve, can now be converted from their raw integer
values using the init(rawValue:) initializer without being reset to
nil. Code that used unsafeBitCast as a workaround for this issue can
be written to use the raw value initializer. For example:
let animationCurve =
unsafeBitCast(userInfo[UIKeyboardAnimationCurveUserInfoKey].integerValue,
UIViewAnimationCurve.self)
can now be written instead as:
let animationCurve = UIViewAnimationCurve(rawValue:
userInfo[UIKeyboardAnimationCurveUserInfoKey].integerValue)!
The problem (if I understand it correctly) was that
typedef NS_ENUM(NSInteger, UIViewAnimationCurve) { ... }
defined only 4 possible enumeration values, but could in fact take other
(undocumented) values as well. This made some nasty workarounds necessary, see for example
How to force a raw value of 7 into a UIViewAnimationCurve enum?
To solve this problem, Swift 1.2 does now allow the creation of
enumeration variables with arbitrary raw values (of the underlying
integer type), if the enumeration is imported from an NS_ENUM
definition.
As a consequence, it is not possible to check programmatically if
a "raw value" is one of the defined values in the NS_ENUM definition.
Try this:
typedef NS_ENUM(NSInteger, Type) {
TypeZero = 0,
TypeOne = 1,
TypeTwo = 2,
TypeUnexpected = INT_MAX
};
switch Type(rawValue: 3) ?? .unexpected {
case .zero:
// Handle type zero
...
case .one:
// Handle type one
...
case .two:
// Handle type two
...
default:
// Handle unexpected types
...
}

#define macro redefined

Can you help me avoid this warning: 'kFontFromFontSize' macro redefined
in Gameconfig.h
#ifndef __GAME_CONFIG_H
#define __GAME_CONFIG_H
//
// Supported Autorotations:
// None,
// UIViewController,
// CCDirector
//
#define kFontFromiPaoneToiPad 2.1
#define kFontFromFontSize 2*kFontFromiPaoneToiPad
Your defines seems to work fine when I test using Visual Studio. Are you sure you are not defining these some other place as well? Perhaps your compiler treats it differently. In that case, you can use const globals instead:
const float kFontFromiPaoneToiPad = 2.1;
const float kFontFromFontSize = 2 * kFontFromiPaoneToiPad;
BTW, it is considered a good practice to use parenthesis around compound expressions in #define to avoid any potential problems when it is substituted in your code.

How to avoid if else or switch case whe dealing with enums?

I have a member variable that tells units for a value I have measured like centimeters,kilometers,seconds,hours etc.
Now these are enums,
When I display a corresponding string, I have created a method that returns corresponding string for these enums.
Unlike Java, enums here cant have other properties associated with them.
So I have to explicitly do a if-else-if chain or a switch case to return the correct string.
I am new to Objective C. any good practice that I should be following in such scenarios ?
afaik Objective-C enums are just old-school C enums... so maybe you can use an integer value for them?
I guess if your enum values started at 0 and increased you could use some sort of array access:
const char *distanceUnitToString2(enum DistanceUnit unit)
{
const char *values[] = {
"cm",
"m",
"km"
};
// do some sanity checking here
// ...
return values[unit];
}
But this feels a little flaky to me. What if you have negative values, or you are using bitmask-style enum values like 1 << 8? You are going to end up using a very large array.
You also could use a switch and improve it a little with a macro. Something like this:
const char *distanceUnitToString(enum DistanceUnit unit)
{
#define CASE(UNIT, STRING) case (UNIT): return (STRING)
switch (unit) {
CASE(kCentimeters, "cm");
CASE(kMeters, "m");
CASE(kKiloMeters, "km");
default:
// should not get here
assert(0);
break;
}
#undef CASE
}
But you don't really save that much vs. not using the macro.
Martin James's comment is the right answer. And use a definition of the enum like:
enum units { cm = 0, m, km };
that way you can be sure that your enum translates to the correct index values.

Method for deriving a string from an enum

I've been wondering if there's an elegant way to derive a string from an enum in Objective-C or vanilla C. I'm currently using a switch statement like so:
switch (self.requestType)
{
case MSListRequest:
serverRequestType = #"List";
break;
case MSDetailsRequest:
serverRequestType = #"Details";
break;
case MSPurchaseRequest:
serverRequestType = #"PurchaseVolume";
break;
}
I'm curious if there's a cleaner way to derive strings than this.
-edit:
I'm also using the same enum elsewhere to interface to a different system that needs to map the same enums to a different set of strings.
There's no real nice way to do this. A very simple way is to create an array:
NSString *const ENUM_NAMES[] = {
#"List", #"Details", #"PurchaseVolume", ...
};
There are alternatives that use macros and some simple preprocessor hacks to define both the names and the enum itself from the same source. However, the resulting code is more difficult to read.
// some_enum.def
X(List),
X(Details),
X(PurchaseVolume)
// some_enum.h
enum {
#define X(x) x
#include "some_enum.def"
#undef X
};
// some_enum.c
char const *const ENUM_STRING[] = {
#define X(x) #x
#include "some_enum.def"
#undef X
};
I'm not sure of the best way to generate an NSString from the preprocessor, whether you can just stick an # in it or if it's better to use (NSString *)CFSTR(x).
When I need a bunch of code like this, I write a Python script to generate the code from a text file -- it generates GPerf output for converting strings to enum, and it generates the code for converting enum to string as well. Plain old C doesn't do reflection.

integer constant does 'not reduce to an integer'

I use this code to set my constants
// Constants.h
extern NSInteger const KNameIndex;
// Constants.m
NSInteger const KNameIndex = 0;
And in a switch statement within a file that imports the Constant.h file I have this:
switch (self.sectionFromParentTable) {
case KNameIndex:
self.types = self.facilityTypes;
break;
...
I get error at compile that read this: "error:case label does not reduce to an integer constant"
Any ideas what might be messed up?
For C/C++ and Objective-C must the case statement have fixed values - "reduced to an integer (read value)" at compile time
Your constants is not a real "constant" because it is a variable and I imagine it can be changed through a pointer - ie &KNameIndex
Usually one defines constants as enum
enum {
KNameIndex = 0,
kAnotherConstant = 42
};
If you would use C++, or Objective-C++ (with .mm as file extension) you could use a const statement as
const int KNameIndex = 0;
You can use
#define KNameIndex 0
...
switch (self.sectionFromParentTable) {
case KNameIndex:
self.types = self.facilityTypes;
break;
...
and it should work.
Just had the same problem and I decided to go with #define rather than enum. Works for me™ ;-)
This is a stab in the dark because I haven't used Cocoa / ObjC in a long time now, but is the member variable sectionFromParentTable not of int type?
I have not worked with Objective C, but I'd try chucking the 'extern'. At least if this were C++, the Constants.m file would not be part of the compilation unit of Other.m, so the value of KNameIndex would be unknown to the compiler. Which would explain the error; an unknowable value can't be a constant.
Does putting the definition, not just the declaration, in the Constants.h file help?
I think you are stuck with using a const int instead of a const NSInteger as switch only works with built in integral types. (not sure about your syntax with const flipped around after the type).
Take a look at the related question: Objective-C switch using objects?