1

I have an app that deals with time. I only want to specify how many seconds are in a minute once in my whole project. In fact, as a matter of principle I want my app binary to have as few redundant copies of this datum as possible (so defines are a last resort). Naively I try this:

// appConstants.h
#ifndef appConstants
#define appConstants
extern uint const SecondsInMinute;
#endif

// appConstants.m
#import "appConstants.h"
uint const SecondsInMinute = 60u;

// viewController.m
#import "appConstants.h"
uint const timeout = SecondsInMinute;

This gives me an "initializer element is not a compile-time constant" error on the timeout const definition.

I'll avoid recounting the tales of all the red-herrings I've chased down looking for a solution to this problem (google is full of enough of those, yet no actual answers). Instead I'll just put it simply: is it possible to reference a const in another file's const in objective c?

Update

In order to address the 'it should work' style answers I have created a simple sample project on github with the above code. One small modification needed to be made (timeout const is renamed to timeoutSeconds). The project was created as an empty iOS project in Xcode 5.0 with the appConstants and viewController files added as described above (except for the aforementioned const rename).

Jaysen Marais
  • 3,956
  • 28
  • 44
  • `#define SecondsInMinute 60u` in a header and just import that header wherever you need it; if you do that in your `.pch` file, you don't even need to import it, as it is imported automatically everywhere – matt Feb 19 '14 at 05:44
  • Or just delete the `const` on the troublesome line – matt Feb 19 '14 at 05:51
  • Or use a singleton method that doles out the same value to every client – matt Feb 19 '14 at 05:52
  • @matt I'm trying to avoid `#define` as (in my limited understanding it simply substitutes the value in the source code before compilation). Removing const from the `timeout` or `SecondsInMinute` definitions both cause an `"initializer element is not a compile-time constant"` error. Having a singleton method just to dole out a variable that doesn't change seems wasteful (and from a language design perspective, embarrassing). – Jaysen Marais Feb 19 '14 at 06:42
  • I still don't quite get why #define is bad (esp. since in this case it's just a scalar) – matt Feb 19 '14 at 06:45
  • @matt in this case using a #define instead of const for the `timeout` definition probably isn't bad at all (as it would just substitute a reference to the `SecondsInMinute` const anyway so no redundant storage), but this is a simplified example. I'm trying to get a canonical answer on the 'const referencing const' question. – Jaysen Marais Feb 19 '14 at 07:10
  • There is no error in my test project. Just a new project with code above. – simalone Feb 19 '14 at 09:33
  • @JaysenMarais Obviously it is possible to refer to a `const` that is initialized elsewhere, since that is what e.g. the name of an NSNotification is (e.g. `UIApplicationDidFinishLaunchingNotification`: it is an `extern NSString* const`). And obviously another `NSString* const` can be set to that value. So I guess I don't see what the question is. – matt Feb 19 '14 at 16:43
  • @simalone I also created a test project with the code above but still get the error as described. I have added a link to the github page for the test project in the updated question – Jaysen Marais Feb 20 '14 at 04:01
  • @matt I think the ambiguity in my question is that the `timeout` const is defined in `viewController.m` but outside the class definition. I don't know enough c to know how to describe what that scope is called. Please see https://github.com/jaysenmarais/SOQuestion21872041/blob/master/SOQuestion21872041/viewController.m for an example – Jaysen Marais Feb 20 '14 at 04:04
  • @JaysenMarais oh, I put uint const timeout = SecondsInMinute; in the class method where can use SecondsInMinute directly, why should you define again for equal const value outside the class? – simalone Feb 20 '14 at 04:09
  • @simalone typically I do that because the 'second level' const is itself an extern that it used both within the class whose file it is defined in and in other classes. So in this example imagine that there is a `extern uint const timeout` declaration in the `viewController.m` file and that a third class is trying to use the `timeout` value for something (I know there are many other 'more c' ways to do this, I'm just trying to figure out whether it is possible using compile-time constants referencing compile-time constants defined in other files). – Jaysen Marais Feb 20 '14 at 08:46
  • @JaysenMarais `const` is not a "compile-time constant". (It's merely a hint to the compiler about what to do with this value at runtime; it might not mean very much at all - exactly what it means is implementation-dependent. It might do no more than prevent this value from being set after initialization.) That's my whole point. You are barking up a completely unnecessary tree. `#define` _is_ a compile-time constant. – matt Feb 20 '14 at 15:52

3 Answers3

0
// appConstants.h
#ifndef appConstants
#define appConstants
uint const SecondsInMinute = 60u;
#endif

// viewController.m
#import "appConstants.h"
uint const timeout = SecondsInMinute;
Cy-4AH
  • 4,370
  • 2
  • 15
  • 22
  • that seems like the obvious answer, but when I try it get `"duplicate symbol _SecondsInMinute"` build errors in every file that references the `appConstants.h` file – Jaysen Marais Feb 19 '14 at 06:34
  • That's what the #ifndef-#define guards are for. Are you sure you included them? – Paul-Jan Feb 19 '14 at 06:38
  • @Paul-Jan Yep, definitely included. The question is perhaps over-simplified. The actual project is hundreds of files and each controller may end up #importing the appConstants.h many times via #import statements in intermediate #imports. Added to this the appConstants file actually lives in a sub-project. I debated whether to mention this in the question, but in the end I opted for simplicity and hoping to create a canonical answer to an apparently simple question (referencing const in another file's const). Cy-4AH answer implies it is possible but as I can't get it working I can't accept yet – Jaysen Marais Feb 19 '14 at 06:51
  • @JaysenMarais, I think you haven't removed `uint const SecondsInMinute = 60u;` from appConstants.m – Cy-4AH Feb 19 '14 at 07:34
  • @JaysenMarais and remove everywhere `extern uint const SecondsInMinute;` or use new name instead `SecondsInMinute` – Cy-4AH Feb 19 '14 at 07:40
  • @JaysenMarais and this is odd that you have message `duplicate symbol`. It's constant, not symbol. Check, may be you have write `uint SecondsInMinute = 60u;` instead `uint const SecondsInMinute = 60u;` – Cy-4AH Feb 19 '14 at 07:57
  • @Cy-4AH please see the [sample project on github](https://github.com/jaysenmarais/SOQuestion21872041) (particularly the [appConstants.h](https://github.com/jaysenmarais/SOQuestion21872041/blob/master/SOQuestion21872041/appConstants.h), [appConstants.m](https://github.com/jaysenmarais/SOQuestion21872041/blob/master/SOQuestion21872041/appConstants.m) and [viewController.m](https://github.com/jaysenmarais/SOQuestion21872041/blob/master/SOQuestion21872041/viewController.m) files). – Jaysen Marais Feb 20 '14 at 04:07
  • @JaysenMarais. I see. This is odd. This error appear if build for simmulator. If build for device all is fine. I have also renamed .m-files in .mm and project builded for simmulator. And justin's solution works without renaming files. – Cy-4AH Feb 20 '14 at 10:00
0

Either use a function:

uint timeout() {return SecondsInMinute;}

or an enum (in your header) when you need a compile time constant:

enum { SecondsInMinute = 60 };
justin
  • 104,054
  • 14
  • 179
  • 226
0

It's a non-problem. You should probably take a deep breath and get back to some real work.

You seem to think there is something special about const variables. There isn't. They are variables like any other variables. They are not "compile-time constants" as your comments suggest you think they are. They are ordinary variables, set at runtime in the ordinary way. const is a hint to the compiler that it might like to store this value in a special way because it is guaranteed not to change, but the compiler is not obligated to treat the value specially. So you are not doing yourself any special good worrying about it. It is not even clear why you are using const in your code at all. You seem to be worried about "efficiency", but it is not obvious what you think that means or how const is efficient. #define is certainly efficient in the sense that it is completely inline: it is a text substitution performed before the compiler even comes into play.

Another revealing passage in your comments is "I don't know enough C". That is very clear. I strongly recommend that you stop and read at least the relevant passages of Kernighan and Ritchie before concerning yourself with this sort of thing. I also suggest you read this question:

#define vs const in Objective-C

Community
  • 1
  • 1
matt
  • 515,959
  • 87
  • 875
  • 1,141