Declaring constants

From D Wiki
Jump to: navigation, search

Motivation

The C/C++ way of declaring a constant is to either use a preprocessor macro, which is fraught with peril because preprocessor macros are not subject to syntax restrictions, and therefore full of pitfalls for the unwary:

#define MYCONSTANT  1+2

int y = MYCONSTANT;
/* y==3, as expected */

int x = MYCONSTANT*3;
/* Oops, x==7, not 9 as one might expect */

Or, we can declare a const variable that contains the value:

const int MYCONSTANT = 1+2;

int y = MYCONSTANT;
/* y==3, as expected */

int x = MYCONSTANT*3;
/* Better: x==9. */

But this wastes space to store the constant in a variable, when the constant really only needs to be an integer literal.

Usage

In D, we solve the problem by using manifest constants:

enum MyConstant = 1+2;

int y = MyConstant;   // y==3
int x = MyConstant*3; // x==9

The declaration of MyConstant is fully subject to D's type restrictions and syntax, and so does not suffer from the "leaky operator" present in the C preprocessor macro version of the constant in the previous section. It also does not incur any runtime storage space, because the compiler treats MyConstant as a compile-time value only. This gives us the best of both worlds.

Furthermore, manifest constants in D are not restricted to integral types alone; we can also use them with strings and arrays:

enum MyString = "This is a string";
enum MyIntArray = [1, 2, 3];
enum MyStringArray = ["abc", "def", "ghi"];
enum MyNestedArray = [
  [ 1, 2, 3 ],
  [ 2, 4, 6 ],
  [ 2, 3, 5, 7, 11 ]
];

writeln(MyString ~ "!");       // prints "This is a string!"
writeln(MyIntArray[1]);        // prints 2
writeln(MyNestedArray[2][3]);  // prints 7

Caveats

Be aware, however, that manifest constants of arrays(except for strings) and associative arrays cause the arrays to be duplicated every time you use them. For example:

enum MyArray = [1, 2, 3, 4];

void main()
{
    auto s = MyArray;
    auto t = MyArray;
    assert(s !is t);
}

This causes MyArray to be allocated twice at runtime. This is great if you wish to have multiple copies of it; but if you only need a single copy, you should declare the array as static immutable instead:

static immutable MyArray = [1, 2, 3, 4];

void main()
{
    auto s = MyArray;
    auto t = MyArray;
    assert(s is t);
}

This way, assigning MyArray to a variable merely aliases the same one array, rather than allocate a new copy each time.

Note that this doesn't apply to strings, but usages of same manifest constant of string alias the same one string(strings have immutable content, so it makes little sense to keep duplicating them). If you really want to duplicate them, use .dup or .idup.

enum MyString = "This is a string";

void main()
{
    auto s = MyString;
    auto t = MyString;
    assert(s is t);
}