GeezerVisonThe World Wide Web, restored to 800 pixels of glory. |
| Home|About|Gaming cabinet|Archives|How it works|Starter sites |
GeezerVison is showing dlang.org — Compile-time Sequences - D Programming Language Articles
Report a bug
If you spot a problem with this page, click here to create a GitHub issue.
Improve this page
Quickly fork, edit online, and submit a pull request for this page.
Requires a signed-in GitHub account. This works well for small changes.
If you'd like to make larger changes you may want to consider using
a local clone.
Compile-time Sequences Contents
BackgroundCompile-time sequences are an important metaprogramming concept that comes naturally from D support for variadic templates. They allow a programmer to operate on types, symbols and values, enabling the ability to define compile-time algorithms that operate on types, symbols and values. Note: For historical reasons these sequences can sometimes be called tuples in documentation or compiler internals, but don't get confused - they don't have much in common with tuples that commonly exist in other languages. Sequences of values of different types that can be returned from functions are provided by std.typecons.Tuple. Using the term "tuple" to mean compile-time sequences is discouraged to avoid confusion, and if encountered should result in a documentation bug report. Consider this simple variadic template: template Variadic(T...) { /* ... */ } T here is a TemplateParameterSequence, which is a core language feature. It has its own special semantics, and, from the programmer's point of view, is most similar to an array of compile-time entities - types, symbols (names) and values. One can check the length of this sequence and access any individual element: template Variadic(T...) { static assert(T.length > 1); pragma(msg, T[0]); pragma(msg, T[1]); } alias dummy = Variadic!(int, 42); Output during compilation: int 42 AliasSeqThe language itself does not provide any means to define such sequences outside of a template parameter declaration. Instead, there is a simple template provided by the standard library: alias AliasSeq(T...) = T;
All it does is give a specific variadic argument sequence an externally accessible name so that it can be worked with in any other context: import std.meta; // can alias to some other name alias Name = AliasSeq!(int, 42); pragma(msg, Name[0]); // int pragma(msg, Name[1]); // 42 // or work with a sequence directly pragma(msg, AliasSeq!("aaa", 0, double)[2]); // double Available operationsChecking the lengthimport std.meta; static assert(AliasSeq!(1, 2, 3, 4).length == 4); Indexing and slicingIndexes must be known at compile-time import std.meta; alias nums = AliasSeq!(1, 2, 3, 4); static assert(nums[1] == 2); // slice last 3 elements alias tail = nums[1 .. $]; static assert(tail[0] == 2); static assert(tail.length == 3); Element assignmentWorks only if the sequence element is a symbol that refers to a mutable variable import std.meta; void main() { int x; alias seq = AliasSeq!(10, x); seq[1] = 42; assert(x == 42); // seq[0] = 42; // won't compile, can't assign to a constant } foreachD's foreach statement has special semantics when iterating over compile-time sequences. It repeats the body of the loop for each of the sequence elements, with the loop iteration symbol being an alias for each compile-time sequence element in turn. import std.meta; void main() { foreach (sym; AliasSeq!(int, "literal", main)) { static if (is(sym)) pragma (msg, sym); else pragma (msg, typeof(sym)); } } Output during compilation: int string void() Note: Static foreach should be preferred in new code. Auto-expansionOne less obvious property of compile-time argument sequences is that when used as an argument to a function or template, they are automatically treated as a sequence of comma-separated arguments: import std.meta; template Print0(T...) { pragma(msg, T[0]); } alias Dummy = Print0!(AliasSeq!(int, double)); This will only print int during compilation because the last line gets rewritten as alias Dummy = Print0!(int, double). If auto-expansion didn't happen, AliasSeq!(int, double) would be printed instead. This is an inherent part of the language semantics for variadic sequences, and thus also preserved by AliasSeq. Homogenous sequencesAn AliasSeq that consists of only type or value elements are commonly called "type sequences" or "value sequences" respectively. The concept of a "symbol sequence" is used less frequently but fits the same pattern. Type sequencesIt is possible to use homogenous type sequences in declarations: import std.meta; alias Params = AliasSeq!(int, double, string); void foo(Params); // void foo(int, double, string); foo(7, 6.5, "hi"); Type sequence instantiationD supports a special variable declaration syntax where a type sequence acts as a type: import std.meta; void foo() { AliasSeq!(int, double, string) variables; variables[0] = 42; variables[1] = 42.0; variables[2] = "just a normal variable"; } The compiler will rewrite such a declaration to something like this: int __var1; double __var2; string __var3; alias variables = AliasSeq!(__var1, __var2, __var3); So a type sequence instance is a kind of symbol sequence that only aliases variables, known as an lvalue sequence. Variadic template functions use a type sequence instance to declare function parameters: void foo(T...)(T args) { // 'T' is a type sequence of argument types. // 'args' is an instance of T whose elements are the function parameters static assert(is(typeof(args) == T)); args[0] = T[0].init; } Value sequencesIt is possible to use a sequence of values of the same type to declare an array literal: import std.meta; enum e = 3; enum arr = [ AliasSeq!(1, 2, e) ]; static assert(arr == [ 1, 2, 3 ]); The above sequence is an rvalue sequence, as it is comprised only of literal values and manifest constants. Each element's value is known at compile-time. The following demonstrates use of an lvalue sequence: import std.meta, std.algorithm; auto x = 3, y = 7; alias pair = AliasSeq!(x, y); swap(pair); assert(x == 7 && y == 3); As above, such sequences may use element assignment. .tupleof is a class/struct instance property that provides an lvalue sequence of each field. A function cannot return a value sequence. Instead, std.typecons.Tuple can be used. It wraps an lvalue sequence in a struct, preventing auto-expansion. Symbol sequencesA symbol sequence aliases any named symbol - types, variables, functions and templates - but not literals. Like an alias sequence, the kind of elements can be mixed. import std.meta : AliasSeq; void f(T)(T v) { pragma(msg, T); } alias syms = AliasSeq!(f, f!byte); syms[0](42); // call f!int with IFTI syms[1](42); // call f!byte Output during compilation: byte int Note that function f!byte is instantiated when the sequence is created, not at the call-site. Copyright © 1999-2026 by the D Language Foundation | Page generated by
Ddoc on Mon Nov 9 05:59:21 2026
|
| You are visitor 01402503 — GeezerVison does not store the pages you look up. Best viewed with Netscape Navigator 4.7 or Internet Explorer 6 at 800×600. No cookies required. |