Best way of defining a compile-time constant

16,377

Solution 1

Indeed, this is trickier than it might appear.

Just to explicitly restate the requirements:

  1. There should be no runtime computation.
  2. There should be no static, stack, or heap memory allocation except for the actual result. (Allocation of executable code is impossible to forbid, but ensure that any data storage needed by the CPU is private.)

In C++, expressions can be lvalues or prvalues (before C++11 and in C, rvalues correspond to the relevant concept). Lvalues refer to objects, hence they can appear on the Left-hand side of an assignment expression. Object storage and lvalue-ness is what we want to avoid.

What you want is an identifier, or id-expression, to evaluate to a prvalue.

Currently, only enumerators can do this, but as you observed, they leave something to be desired. Each enumeration declaration introduces a new, distinct type, so enum { Value = 123 }; introduces a constant which is not an integer, but its own unique type which converts to int. This isn't the right tool for the job, although it works in a pinch.

You can use #define, but that's a hack since it avoids the parser completely. You have to name it with all capital letters, and then ensure that the same all-caps name isn't used for anything else in the program. For library interfaces, such a guarantee is especially onerous.

The next best option is a function call:

constexpr int value() { return 123; }

Careful, though, as constexpr functions can still be evaluated at runtime. You need to jump through one more hoop to express this value as a computation:

constexpr int value() {
    /* Computations that do not initialize constexpr variables
       (or otherwise appear in a constant expression context)
       are not guaranteed to happen at compile time, even
       inside a constexpr function. */

    /* It's OK to initialize a local variable because the
       storage is only temporary. There's no more overhead
       here than simply writing the number or using #define. */

    constexpr int ret = 120 + 3;
    return ret;
}

Now, you can't refer to the constant as a name, it must be value(). The function call operator might look less efficient, but it's the only current way to completely eliminate storage overhead.

Solution 2

I think you should consider the C++11 feature of specifying an underlying type for an enum, which applied to your example would be:

enum : unsigned int { Value = 123 };

this removes one of your two objections to using enums, namely, your inability to control which type would actually be used to represent them. It still doesn't allow non-integeral constants, though.

Share:
16,377

Related videos on Youtube

Malvineous
Author by

Malvineous

Updated on November 04, 2020

Comments

  • Malvineous
    Malvineous over 3 years

    What's the best way of defining a simple constant value in C++11, such that there is no runtime penalty? For example: (not valid code)

    // Not ideal, no type, easy to put in wrong spot and get weird errors
    #define VALUE 123
    
    // Ok, but integers only, and is it int, long, uint64_t or what?
    enum {
         Value = 123
    };
    
    // Could be perfect, but does the variable take up memory at runtime?
    constexpr unsigned int Value = 123;
    
    class MyClass {
        // What about a constant that is only used within a class, so
        // as not to pollute the parent namespace?  Does this take up
        // memory every time the class is instantiated?  Does 'static'
        // change anything?
        constexpr unsigned int Value = 123;
    
        // What about a non-integer constant?
        constexpr const char* purpose = "example";
        std::string x;
        std::string metadata() { return this->x + (";purpose=" purpose); }
        // Here, the compiled code should only have one string
        // ";purpose=example" in it, and "example" on its own should not
        // be needed.
    };
    

    Edit

    Since I've been told this is a useless question because there's no background behind it, here's the background.

    I am defining some flags, so that I can do something like this:

    if (value & Opaque) { /* do something */ }
    

    The value of Opaque won't change at runtime, so as it is only needed at compile time it seems silly to have it end up in my compiled code. The values are also used inside a loop that runs multiple times for each pixel in an image, so I want to avoid runtime lookups that will slow it down (e.g. a memory access to retrieve the value of the constant at runtime.) This isn't premature optimisation, as the algorithm currently takes about one second to process an image, and I regularly have over 100 images to process, so I want it to be as fast as possible.

    Since people are saying this is trivial and not to worry about it, I'm guessing #define is as close as you can get to a literal value, so maybe that's the best option to avoid "overthinking" the problem? I guess general consensus is you just hope nobody ever needs to use the word Opaque or the other constants you want to use?

    • Brian Bi
      Brian Bi about 9 years
      Are you sure you're so tight on memory that 4 bytes makes a difference?
    • Malvineous
      Malvineous about 9 years
      Why does everyone always say this? Then you post what you want and all you get are tumbleweeds because nobody can figure out what you want, apart from one person who says you should post a simple example with just the core points...sigh
    • Malvineous
      Malvineous about 9 years
      @Brian: Sure four bytes isn't much, but do you leave unused variables littered around your code? Why bother removing them, compiler warnings can be disabled and a few bytes isn't much :-P Of course one reason is that if you use these values in an algorithm that loops many times, then having the value directly in the code removes one memory lookup on each access, making your algorithm faster (compiler optimisations notwithstanding.)
    • Lightness Races in Orbit
      Lightness Races in Orbit about 9 years
      Yes you should post a simple example with just the core points, but those core points should have some bearing on reality. This question does not indicate any actual problem. It just solicits opinion on something without actually defining any constraints, making it useless!
    • Malvineous
      Malvineous about 9 years
      Well ok, I'm using the fields as flags, for example if (value & SomeFlag) { /* do something */ }. Rather than say if (pixel & 2) I would rather say if (value & Opaque) as it's easier to read, but the value of Opaque won't change at runtime. It's only needed at compile time and seems silly to embed it into my executable just because I want my code to be easy to read. It's also inside a loop that runs multiple times for each pixel, so I want to avoid runtime lookups that will slow it down. So I'm after the most elegant way to define the constant, with a focus on new C++11/14 features.
    • Neil Kirk
      Neil Kirk about 9 years
      My practices haven't been updated for C++11 yet but I just use regular const int a = 0; in a namespace, sometimes static const int a = 0 in a class. These won't use up memory at run-time if not required to exist in memory.
    • einpoklum
      einpoklum over 7 years
      @Malvineous: Ok, done.
  • Malvineous
    Malvineous about 9 years
    Doesn't really answer the question though. Why use static at all? How does this work with const char* values (the code in my question was pseudocode and won't compile)
  • Malvineous
    Malvineous about 9 years
    Yes, it tells me error: expected ‘)’ before ‘purpose’ in the metadata() function.
  • T.C.
    T.C. about 9 years
    @Malvineous string literal concatenation applies to, well, string literals.
  • T.C.
    T.C. about 9 years
    @Malvineous If you want the translation phase 6 string literal concatenation, then a macro is pretty much your only choice.
  • Potatoswatter
    Potatoswatter about 9 years
    You can go very wrong with this. ODR-using such a constant from within an inline function (such as a template) will violate the one-definition rule, because the name refers to a separate, different object in each translation unit.
  • Neil Kirk
    Neil Kirk about 9 years
    It's hard not to care about it when C++ has 53910 ways of defining constants.
  • vsoftco
    vsoftco about 9 years
    @LightnessRacesinOrbit do you need the const? I thought constexpr implies const when declaring an object.
  • Potatoswatter
    Potatoswatter about 9 years
    @vsoftco constexpr implies both const and static. The static part is dangerous, and it's even been deprecated in C.
  • Lightness Races in Orbit
    Lightness Races in Orbit about 9 years
    @vsoftco: True. As of C++14, constexpr member functions are not implicitly const so, for consistency, I prefer to write it out explicitly for object declarations too.
  • Lightness Races in Orbit
    Lightness Races in Orbit about 9 years
    @NeilKirk: It's extremely easy not to care whether a 4-byte constant is baked into your executable or optimised out.
  • Neil Kirk
    Neil Kirk about 9 years
    @LightnessRacesinOrbit It's not if you are a perfectionist with OCD. IT BOTHERS ME.
  • vsoftco
    vsoftco about 9 years
    are you saying that simply return 123 + 3; is not performed at compile time?
  • Potatoswatter
    Potatoswatter about 9 years
    @vsoftco Not quite. But constexpr variables are always guaranteed to be treated as constant expressions whereas constexpr functions are not, and can sometimes be evaluated at runtime. Of course, for such a trivial operation, it doesn't make a difference. But if it were int ret = INT_MAX + 3;, that would be different because there would be signed integer overflow. Using constexpr very liberally helps prevent such gotchas.
  • Potatoswatter
    Potatoswatter about 9 years
    @LightnessRacesinOrbit Yes, internal linkage means there is a different object in each TU. Two definitions of the same inline function will use those two different objects. If it's an ODR-use, such as passing by reference (or perfect forwarding), it violates the ODR rule.
  • Potatoswatter
    Potatoswatter about 9 years
    @LightnessRacesinOrbit If you have hundreds of constants and you're programming an embedded system with only a few kilobytes of program space, then it becomes a problem. To say that C++ isn't used for such embedded programming is circular reasoning: C++ is capable of utmost efficiency as long as we watch the corner cases.
  • Lightness Races in Orbit
    Lightness Races in Orbit about 9 years
    @Potatoswatter: Quote me saying that C++ isn't used for embedded programming. I dare you. I do it myself, habitually. Yet, until such a specific use case is specified as a constraint, we shall discuss the language in the terms of the abstract machine that it describes only and, in those circumstances, caring about this sort of thing is entirely folly.
  • Potatoswatter
    Potatoswatter about 9 years
    @LightnessRacesinOrbit So, when the question is stated in abstract terms, you ask for a use-case, and when you get a use-case, you ask to talk about the abstract machine. ???
  • Lightness Races in Orbit
    Lightness Races in Orbit about 9 years
    @Potatoswatter: Ehm?! I think you've fundamentally misunderstood something. Can you be more specific, so that we can resolve going forwards. Regards
  • Potatoswatter
    Potatoswatter about 9 years
    … In purely abstract-yet-realistic terms, for any arbitrary program size constraint, there is some number of constants that can become burdensome. In the extreme, the storage dedicated to them is some fraction of the executable size, I'd guess 25-50%. Imagine, for example, a parser generator which ODR-uses every value of the state variable. Anyway, I'm not the first or the last to ask you to stop telling question askers what to do…
  • rustyx
    rustyx over 7 years
    @Potatoswatter - integer overflow is UB. The compiler is not required to execute that at run time.
  • Potatoswatter
    Potatoswatter over 7 years
    @RustyX The point is that the compiler is required to refuse to evaluate it at compile time, so if you add constexpr there will be a hard error.
  • Wormer
    Wormer almost 6 years
    Isn't it enough to say "constexpr int ret = (123);" to make 123 become an expression?
  • Potatoswatter
    Potatoswatter almost 6 years
    @Wormer 123 alone is an expression in the first place, and it has the prvalue category that we want. The problem is binding it to a reusable name foo such that foo has the same category as 123.
  • Wormer
    Wormer almost 6 years
    My question is about provided code of function value(). Why do we need 120+3 there? can't we change that to (123)? or why we can't put just 123? Sorry for misunderstanding.
  • Potatoswatter
    Potatoswatter almost 6 years
    @Wormer Yes, it doesn’t matter what goes there. I was trying to insert a calculation into the example but it’s a little confusing.
  • Wormer
    Wormer almost 6 years
    I'm feeling so terrible thinking how I would write something like this to only give a simple name for my compilation time constant. Oh why we have constexpr and all this fancy template stuff in C++ and don't have a thing for such a simple notion.
  • Potatoswatter
    Potatoswatter almost 6 years
    @Wormer Because the difference is usually insignificant. This only matters (1) when the compile-time constant should not be allocated (e.g. very large tables, or systems where the executable needs to be very small, such as low-cost embedded or virus development) and (2) in cases where allocation leads to ODR violation. For (1) an [[attribute]] would be suitable and for (2) the committee would like a better ODR (in the same way that it would like a unicorn; the modules TS occupies its efforts in the design space but only makes the problem worse).