Declaring constants

From D Wiki
Revision as of 09:15, 2 February 2015 by Sinkuu (talk | contribs) (enum of string doesn't allocate a new copy each time.)
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 two usage of a manifest constant aliases the same one string(especially since 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);
}