std.typecons
このモジュールは、様々な型コンストラクタ -- つまり、 新しい汎用の型を構築するテンプレートを実装しています。Synopsis:
// 値タプル alias Tuple!(float, "x", float, "y", float, "z") Coord; Coord c; c[1] = 1; // インデックスによるアクセス c.z = 1; // 指定された名前によるアクセス alias Tuple!(string, string) DicEntry; // 名前は省略可能 // const/immutableオブジェクトへの、再束縛可能な参照 void bar() { const w1 = new Widget, w2 = new Widget; w1.foo(); // w1 = w2 はダメ; constオブジェクトは再束縛できない auto r = Rebindable!(const Widget)(w1); // r があたかもWidgetオブジェクトであるかのようにメソッド呼び出し r.foo(); // r が他のオブジェクトを指すよう再束縛 r = w2; }Source:
std/typecons.d
License:
Boost License 1.0. Authors:
Andrei Alexandrescu, Bartosz Milewski, Don Clugston Shin Fujishiro
- リソースの唯一の所有権をカプセル化します。
所有権が移動してない限り、型Tを持つリソースが、スコープの終わりと同時に破棄されます。
所有権の移動は release による明示も、関数から
Unique を返す暗黙の移動も可能です。リソースは多相的に振る舞うクラスオブジェクトであってもよく、
その場合は Unique も多相的に振る舞います。
Example:
- this(RefT p);
- 右辺値をとるコンストラクタ。
指定された右辺値が(キャストなどによる)単なる左辺値のビューである場合を除き、
uniqueness を保証します。
典型的な使い方:
Unique!(Foo) f = new Foo;
- this(ref RefT p);
- 左辺値を取るコンストラクタ。コピー元をnullにします。
元の左辺値にさらに別のエイリアスが存在しない限り、
このnull化によって所有権のuniquenessが保証されます。
- ユニークな右辺値を返します。Uniqueオブジェクトの保持する内容はnullになります。
- 保持するオブジェクトへのメンバアクセスを転送します。
- 値のタプル。例えば Tuple!(int, string) は
int と string を格納したレコードです。Tuple は複数の値をまとめて、特に
関数から複数の値を返すのに使うことができます。obj がタプルなら、個々のメンバは先頭要素なら
obj[0]、
次の要素なら obj[1]
、等々の構文でアクセスできます。
1始まりではなく0始まりの記法を選択した理由は、 タプルをコンパイル時のループ対象として (型タプル上のiterationのように) 自然に扱えるようにするためです。
Example:
Tuple!(int, int) point; // 座標を代入 point[0] = 5; point[1] = 6; // 座標の読み取り auto x = point[0]; auto y = point[1];
Tuple のメンバには名前をつけることができます。名前ありと名前無しのメンバを混ぜても構いません。 この場合も、上記の添え字によるアクセスは常に全てのフィールドについて可能です。
Example:
alias Tuple!(int, "index", string, "value") Entry; Entry e; e.index = 4; e.value = "Hello"; assert(e[1] == "Hello"); assert(e[0] == 4);
名前付きタプルと名前なしタプルは異なる型として扱われます。 つまり、フィールドの名前が違うと違うタプル型になります。 フィールド名だけが違うタプルも、構造自体は同じですが、 型としては違う物になります。
Example:
Tuple!(int, "x", int, "y") point1; Tuple!(int, int) point2; assert(!is(typeof(point1) == typeof(point2))); // passes
- タプルの要素型のリスト(型タプル)です。
- フィールドそれぞれにつき1つずつ値を受け取るコンストラクタです。 各々の引数は対応する要素型に代入可能である必要があります。
- 互換性のある別のタプルを受け取る コンストラクタです。 対応する要素それぞれが代入可能である必要があります。
- 同値性判定。
- 同値性判定。
- 順序判定
- 順序判定
- 別のタプルからの代入。変換元のタプルのそれぞれの要素が、
変換先の対応するフィールドへ代入可能である必要があります。
- 別のタプルからの代入。変換元のタプルのそれぞれの要素が、 変換先の対応するフィールドへ代入可能である必要があります。
- タプルのスライスを取得します。
Example:
Tuple!(int, string, float, double) a; a[1] = "abc"; a[2] = 4.5; auto s = a.slice!(1, 3); static assert(is(typeof(s) == Tuple!(string, float))); assert(s[0] == "abc" && s.field[1] == 4.5);
- 文字列への変換。
- 渡された引数でインスタンス化および初期化された Tuple
オブジェクトを返します。
Example:
auto value = tuple(5, 6.7, "hello"); assert(value[0] == 5); assert(value[1] == 6.7); assert(value[2] == "hello");
- T が Tuple 構造体テンプレートのインスタンスであるときのみ true を返します。
- 真の名前付き列挙値を、
parseと文字列化のプリミティブ付きで定義します。
Example:
mixin(defineEnum!("Abc", "A", "B", 5, "C"));
これは以下のコードと同等です:
enum Abc { A, B = 5, C } string enumToString(Abc v) { ... } Abc enumFromString(string s) { ... }
enumToString 関数は、列挙値の被修飾名 "A", "B", "C" などを生成します。enumFromString 関数は "A", "B", "C" を受け取るとその値を返し、それ以外の文字列に対しては 例外を投げます。
基底型は次のように指定します:
mixin(defineEnum!("Abc", ubyte, "A", "B", "C", 255));
この場合、生成された enum は ubyte 表現を持つことになります。
- Rebindable!(T) は、
T 型のオブジェクトと同じように振る舞うシンプルで効率的なラッパです。ただし、Rebindableは途中で別のオブジェクトを指すように
再代入が可能です。完全性を期すために、T が非constオブジェクト型の場合は、
Rebindable!(T) は単なる T へのaliasとなります。
T がそもそもクラス型でない場合は、Rebindable!(T) はコンパイルエラーとなります。
通常の const オブジェクト参照は再代入ができません:class Widget { int x; int y() const { return a; } } const a = new Widget; a.y(); // 大丈夫 a.x = 5; // エラー。const a は変更できない a = new Widget; // エラー。const a は変更できない
しかしここで、Rebindable!(Widget) は最代入可能で、 その他の場面では const Widget とまったく同様に振る舞います:auto a = Rebindable!(const Widget)(new Widget); a.y(); // 大丈夫 a.x = 5; // エラー。const a は変更できない a = new Widget; // 大丈夫
const オブジェクトへの書き換え可能な参照が必要な場合に、Rebindable を使用すると良いでしょう。 たとえば、 inplace でconst参照をsortしたい場合などです。Rebindable が D の型システムの健全性を壊すことはなく、 cast の使用などに見られる類の危険もありません。
- 自動的な型推論で Rebindable を作るための便利関数です
- この関数は単に渡された Rebindable をそのまま返します。 これは、ジェネリックなコードを書いているときに、渡されたオブジェクトが class なのか Rebindable なのか気にせず書ける利点があります。
- 指定されたメンバ宣言リストを、alignmentを保ったままでサイズが最小になるように並び替えます。
mixinできる宣言リストを返します。
Example:
struct Banner { mixin(alignForSize!(byte[6], double)(["name", "height"])); }
80-bit 実数と、align(1) で宣言された構造体に関しては、 必ずしも最適な整列を行うとは限りません。
BUG:
bugzilla 2029 のため、(string[] names...) という引数リスト宣言ができません。 このため、現在のところ醜い配列リテラルを使う必要があります。
- BlackHole!Base は Base の派生クラスで、
Base の abstract 関数を自動的に何もしない空実装で実装した物となります。
各自動実装は、
返値型のデフォルト値をそのまま返すだけで他に何も行いません。
この名前は Sean M. Burke の Perl モジュール
Class::BlackHole
から採っています。
Example:
abstract class C { int m_value; this(int v) { m_value = v; } int value() @property { return m_value; } abstract real realValue() @property; abstract void doSomething(); } void main() { auto c = new BlackHole!C(42); writeln(c.value); // "42" を表示 // abstract 関数はデフォルト実装: writeln(c.realValue); // "NaN"を表示 c.doSomething(); // なにもしない }
See Also:
AutoImplement, generateEmptyFunction - WhiteHole!Base は Base の派生クラスで、
全ての abstract 関数を例外を投げる関数で実装した物となります。
各自動実装は、呼ばれるとすぐに Error を投げ、returnすることはありません。
未実装の関数を埋めるのに役立ちます。
この名前は Michael G Schwern の Perl モジュール
Class::WhiteHole
から採っています。
Example:
class C { abstract void notYetImplemented(); } void main() { auto c = new WhiteHole!C; c.notYetImplemented(); // Error を投げる }
BUGS:
nothrow 関数は release モードではアボートしてしまう問題があります。 これはnothrow関数では実装が assert(0) で行われているためです。 See Also:
AutoImplement, generateAssertTrap - AutoImplement は (デフォルトで) クラスやinterface Base の
全てのabstractメンバを指定された方法で実装します。
Parameters:
Note:how 関数の実装/オーバーライドの仕方を指定するテンプレートです 二つの引数が how に渡されます。型 Base と、 実装したい関数への alias です。これを受け取ると、how は関数実装のボディを文字列で返さなければなりません。 生成する関数本体文字列には、以下のキーワードを使えます: - a0, a1, …: 関数に渡される各引数
- args: 引数のタプル
- self: 関数自身へのalias
- parent: (もしあれば) オーバーライドされる関数へのalias
// オーバーライドする関数全てにログメッセージを付与 string generateLogger(C, alias fun)() @property { enum qname = C.stringof ~ "." ~ __traits(identifier, fun); string stmt; stmt ~= q{ struct Importer { import std.stdio; } }; stmt ~= `Importer.writeln("Log: ` ~ qname ~ `(", args, ")");`; static if (!__traits(isAbstractFunction, fun)) { static if (is(typeof(return) == void)) stmt ~= q{ parent(args); }; else stmt ~= q{ auto r = parent(args); Importer.writeln("--> ", r); return r; }; } return stmt; }
what どの関数を実装/オーバーライドするか決めるテンプレート what に渡される引数は、 Base の非finalメンバ関数へのaliasです。what はbool値を返します。 true を返すと、その関数を implement/オーバーライドしたい、という意味になります。 // 何かreturnする関数かどうか見分ける template hasValue(alias fun) { enum bool hasValue = !is(ReturnType!(fun) == void); }
生成されたコードは std.typecons モジュールのスコープに挿入されます。 したがって、std.typecons の外の有用な関数は生成コードでは使えません。 この問題を回避するには、必要なモジュールをローカルな構造体を作って import します。 例としては上記の generateLogger() テンプレートをご覧下さい。 BUGS:- コンストラクタの可変長引数がsuperに転送されません。
- interface を深く継承すると、以下のようなエラーメッセージと共にコンパイルエラーになります: "Error: function std.typecons.AutoImplement!(Foo).AutoImplement.bar does not override any function". [Bugzilla 2525, Bugzilla 3525]
- parent キーワードは、実際には基底クラスの対応するメンバの delegate です。 [Bugzilla 2540]
- alias テンプレート引数を how や what で使うとおかしなコンパイルエラーが出ることがあります。回避には、タプルテンプレート引数を使用します。 [Bugzilla 4217]
- AutoImplement の how に使える定義済みポリシーです。これらは BlackHole と WhiteHole の実装に用いられています。
- RefCounted オブジェクトの自動初期化に関するオプション (詳しくは下の
RefCounted の定義をご覧下さい)
- 自動初期化しない
- 自動初期化する
- T を内部に保持する参照カウントオブジェクトです。
RefCounted はオブジェクトを指す参照をすべて追跡し、
カウントがゼロになると保持したオブジェクトを解放します。
RefCounted は malloc と free を操作に使用します。
RefCounted は unsafe であり、使う場合は細心の注意が必要です。
内容への参照が RefCounted の管理の外へ漏れ出すことが内容にしてください。
autoInit オプションによって、アクセス時に
オブジェクトが自動的に初期化されることが保証されます。autoInit ==
RefCountedAutoInitialize.yes (デフォルト) は便利ですが、
しかしアクセスの度にコストが発生します。autoInit == RefCountedAutoInitialize.no を指定すると、アクセスする前に
refCountedIsInitialized または refCountedEnsureInitialized
をユーザが呼び出しておく必要があります。これを怠ると null
ポインタ参照になります。
Example:
// int と size_t のペア(後者が参照カウント) // が動的に割り当てられる auto rc1 = RefCounted!int(5); assert(rc1 == 5); // もう割り当てを行わない。参照カウントは1 auto rc2 = rc1; // 参照のセマンティクス rc2 = 42; assert(rc1 == 42); // rc1 と rc2 がスコープを抜けるとペアが解放される
- 中身のオブジェクトを初期化するコンストラクタ
Postcondition:
refCountedIsInitialized - 代入演算子
- 中身のオブジェクトへの参照を返します。(autoInit == RefCountedAutoInitialize.yes) ならば、refCountedEnsureInitialized を呼び出します。そうでなければ,単に assert(refCountedIsInitialized) を確認します。alias refCountedPayload this; という形で使えるようになっているので、呼び出し側は RefCounted オブジェクトを単に T として使うことができます。
- クラスオブジェクトを現在のスコープの内側に構築し、
new のオーバーヘッドを避けます。この機能は unsafe です。
作成したオブジェクトへの参照がスコープの外側に漏れないようにすることは、
ユーザーの責任です。
Example:
unittest { class A { int x; } auto a1 = scoped!A(); auto a2 = scoped!A(); a1.x = 42; a2.x = 53; assert(a1.x == 42); }