D言語といえばデバッグ・検証(と思い込んでます).
ここでは、検証に使えそうな式をまとめました.
main文なしのプログラム
D言語では-mainを指定すればmain文なしでコンパイルができるよう.
$dmd -main hoge.d
契約プログラミング
参考によると, 契約とは常にtrueでないといけない式のことです. つまり, falseになったときにerrorを出力するということですね.
一番基本的なものとしてassert()が挙げられます.
//式がfalseだったらエラーが出力される
assert(式);
//出力文字を設定
assert(A==B,"出力");
//到達不能
//到達した時点でエラーを出力
assert(0);
ちなみに, -releaseでコンパイルするとassert(false)やassert(0)などのコンパイル時に絶対に失敗するとわかるもの以外, assert式は消えるようです.
事前・事後条件
かなり意訳ですが, 自分なりの解釈を
in : 引数が正しいかチェック
out : 実行結果が正しいかチェック
body: 実行内容
...として検証を行います.
//公式サイト丸パクリ
long square_root(long x)
in {
//前提としてxが0以上じゃないと平方根は求まらない
assert(x >= 0);
}
out (result) {
//実行結果が正しいか性質に当てはめて検証してみる
assert((result * result) <= x && (result+1) * (result+1) >= x);
}
body {
//実行内容
return cast(long)std.math.sqrt(cast(real)x);
}
//引数がないときなどにinがない検証も行える
void func()
out {
~~~
}
body {
...
}
ちなみに以下のように書いてはいけません。
エラーとなります。
void func() {
in {
} out {
~~~
}
body {
...
}
}
void main(){ ~~ }
$rdmd error.d
error.d(): Error: found 'in' instead of statement
error.d(): Error: declaration expected, not 'out'
error.d(): Error: unrecognized declaration
参考:
契約プログラミング
D言語assert(failse)について
単体テスト
unittest {
assert(~~);
out {
~~
}
body {
--
}
//処理をつらつら
}
unittestを使うには-unittestを指定します
$dmd -unittest hoge.d
※どうやらunittestはmainの中では使えないようなので気をつけて
エラー処理
実行中エラーになったときの動作を記述するものです.まさかの場合が起きたときの処理ですね.
エラーの例はエラー処理を見てください.
throw(意訳)
これが実行されるとその部分でプログラムが進まなくなり,エラーを返します.そしてエラーを受け取った関数は上位の関数へエラーを返し...
これをmain関数より上位に来るまで繰り返し,最終的にはエラーの原因が記述されている文字列を表示してプログラムを終了します.
int warizan(int x){
if (x == 0) throw new Exception("0では割れないよ");
return 20 / x;
}
try-catch-finally(意訳)
try{}の中でthrowされた例外はcatchでとらえることができます.
また, 例外が発生してもしなくてもfinally{}の中の処理を実行します.
int func_ko{
if(num == 0) throw new Exception("0はだめ")
return num;
}
void func2_oya{
try{
func_ko(num);
}catch(Exception e){
//例外を受け取った時の処理
~~~
}finally{
writeln("とりあえずここは実行される");
}
writeln("例外が起きたら実行されないけど例外が起きなければ実行される");
}
参考:
D言語友の会-例外
スコープガード
スコープガード文はスコープを抜けるときに以下の三つの状態に分けてあとから書いた順に実行されます.
scope (exit) //スコープを抜けるときに必ず実行
scope (success) //スコープを例外以外で抜けるときに実行
scope (failure) //スコープを例外で抜けるときに実行
とりあえずシンプルに
//{}がスコープ内
{
scope(failure) writeln("failure");
scope(success) writeln("success");
scope(exit) writeln("exit");
func1();
}
func1()で例外発生しなかったときの実行結果
$exit
$success
func1()で例外発生したときの実行結果
$exit
$failure
Typeid式
型名を返してくれます
class A { }
class B : A { }
void main() {
writeln(typeid(int)); // int
uint i;
writeln(typeid(i++)); // uint
writeln(i); // 1
A a = new B();
writeln(typeid(a)); // B
writeln(typeid(typeof(a))); // A
}
参考:
式
Is式
内容が意味的に正しいかどうかを判断します.
以下公式サイトの寄せ集め
alias int func(int); // func は関数型への alias
void foo() {
if (is(func[]) ) // 関数の配列は作れないので、
// 条件は満たされない
writeln("satisfied");
else
writeln("not satisfied");
if (is([][])) // エラー。[][] は文法的に正しい型でない
...
}
alias short bar;
void foo(bar x) {
if ( is(bar : int) ) // shortはintに暗黙変換されるので
// 条件は満たされる
writeln("satisfied");
else
writeln("not satisfied");
}
void test(bar x) {
if ( is(bar == int) ) // shortはintと同じ型ではないので、
// 条件は満たされない
writeln("satisfied");
else
writeln("not satisfied");
}
これは応用例かな
import std.stdio;
void main() {
alias long[char[]] AA;
static if (is(AA T : T[U], U : const char[]))
{
writeln(typeid(T)); // long
writeln(typeid(U)); // const char[]
}
static if (is(AA A : A[B], B : int))
{
assert(0); // Bはintではないのでマッチしない
}
static if (is(int[10] W : W[V], int V))
{
writeln(typeid(W)); // int
writeln(V); // 10
}
static if (is(int[10] X : X[Y], int Y : 5))
{
assert(0); // Yは10なのでマッチしない
}
}
std.exception
例外や,エラー処理のためのツール. 受験など一通り終わったら手を出します.
※
Dを教えてくれてるy_ukatama氏によるとstd.exception.enforceは「エラーはnull、成功ならハンドルを返す」的な関数のエラー処理をスマートにできるよう.
Win32 API のエラーをFormatMessageで文字列化してくれる例外クラスを作ってenforceExをつかうと捗るよと言っていました.
また,assert的な使い方があったり, 意外と奥が深そうです.
ログ出力
あんまネットでみないものの便利な機能だと思います.ここではrepeatedlyさんのstd.experimental.loggerを紹介しておきます
最後に
とりあえず一通り終わりましたが適宜修正を加えるかと, なんかありましたら連絡ください.