Difference between revisions of "Declaring constants"

From D Wiki
Jump to: navigation, search
(caveat)
m (minor correction for comment in code)
Line 54: Line 54:
 
writeln(MyString ~ "!");      // prints "This is a string!"
 
writeln(MyString ~ "!");      // prints "This is a string!"
 
writeln(MyIntArray[1]);        // prints 2
 
writeln(MyIntArray[1]);        // prints 2
writeln(MyNestedArray[2][3]);  // prints 5
+
writeln(MyNestedArray[2][3]);  // prints 7
 
</syntaxhighlight>
 
</syntaxhighlight>
  

Revision as of 11:06, 25 August 2014

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 (including strings) and associative arrays cause the arrays to be duplicated every time you use them. For example:

enum MyString = "This is a string";

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

This causes MyString 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 (especially since strings have immutable content, so it makes little sense to keep duplicating them), you should declare the string as static immutable instead:

static immutable MyString = "This is a string";

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

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