forked from dlang/dlang.org
-
Notifications
You must be signed in to change notification settings - Fork 4
/
cpptod.dd
818 lines (638 loc) · 20.9 KB
/
cpptod.dd
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
Ddoc
$(COMMUNITY C++プログラマのためのD言語,
<!--img src="images/cpp1.gif" border=0 align=right alt="C++"-->
熟練したC++プログラマは誰でも、自然とさまざまなイディオムやテクニックを
身につけているものです。新しい言語を学ぼうとすると、時にこれらのイディオムに
慣れすぎたせいで、同じことを別の言語でどう実現するのかわからなくなってしまいます。
そこでここに、C++のよく知られたテクニックと、
対応するDでのやり方を集めてみました。
<p>
参照: <a href="ctod.html">CプログラマのためのD言語</a>
$(UL
$(LI $(LINK2 #constructors, コンストラクタの定義))
$(LI $(LINK2 #baseclass, 基底クラスの初期化))
$(LI $(LINK2 #structcmp, 構造体の比較))
$(LI $(LINK2 #typedefs, typedef された型))
$(LI $(LINK2 #friends, friend))
$(LI $(LINK2 #operatoroverloading, 演算子オーバーロード))
$(LI $(LINK2 #usingdeclaration, 名前空間の using))
$(LI $(LINK2 #raii, RAII (Resource Acquisition Is Initialization)))
$(LI $(LINK2 #properties, プロパティ))
$(LI $(LINK2 #recursivetemplates, 再帰的テンプレート))
$(LI $(LINK2 #metatemplates, メタテンプレート))
$(LI $(LINK2 #typetraits, Type Traits))
)
<hr><!-- -------------------------------------------- -->
<h3><a name="constructors">コンストラクタの定義</a></h3>
<h4>C++ では</h4>
コンストラクタはクラスと同じ名前を持ちます:
$(CPPCODE
class Foo
{
Foo(int x);
};
)
<h4>D では</h4>
コンストラクタは予約語thisで定義されます:
------
class Foo
{
this(int x) { }
}
------
これがDでの方法です。
<hr><!-- -------------------------------------------- -->
<h3><a name="baseclass">基底クラスの初期化</a></h3>
<h4>C++ では</h4>
基底クラスのコンストラクタは、基底クラス初期化構文で呼び出します:
$(CPPCODE
class A { A() {... } };
class B : A
{
B(int x)
: A() // 基底のコンストラクタ呼び出し
{ ...
}
};)
<h4>D では</h4>
基底クラスのコンストラクタは、super構文で呼び出します:
------
class A { this() { ... } }
class B : A
{
this(int x)
{ ...
super(); // 基底のコンストラクタ呼び出し
...
}
}
------
C++と比べて優れている点として、基底クラスのコンストラクタ呼び出しを、派生クラスのコンストラクタの中の好きな場所に置ける、という点があります。
また、Dではコンストラクタからまた別のコンストラクタを呼び出せます:
------
class A
{ int a;
int b;
this() { a = 7; b = foo(); }
this(int x)
{
this();
a = x;
}
}
------
メンバは、コンストラクタの呼び出しより前に定数値で初期化できます。
これを用いると、上のサンプルは次のように書き換わります:
------
class A
{ int a = 7;
int b;
this() { b = foo(); }
this(int x)
{
this();
a = x;
}
}
------
<hr><!-- -------------------------------------------- -->
<h3><a name="structcmp">構造体の比較</a></h3>
<h4>C++ では</h4>
C++では、構造体の代入には単純で便利な方法がありますが:
$(CPPCODE
struct A x, y;
...
x = y;
)
構造体の比較はそうでもありません。
二つのインスタンスの等値性を比較するには:
$(CPPCODE
#include <string.h>
struct A x, y;
inline bool operator==(const A& x, const A& y)
{
return (memcmp(&x, &y, sizeof(struct A)) == 0);
}
...
if (x == y)
...
)
比較したい全ての構造体毎に、演算子オーバーロードが必要です。
しかも、上の==演算子の定義では型チェックによる
言語の助けを受けることが、全く出来ていません。
C++方式のもう一つの問題は、(x == y) と書いてあるのを見ただけでは、
実際に何が起きるのか何もわからないということです。
実際の挙動を確認するには、適用される operator==()
の定義を探して見る必要があります。
<p>
しかも、memcmp() による operator==() の実装には困ったバグが潜んでいます。
アラインメントのせいで、構造体のメンバの間には $(SINGLEQUOTE 穴) があるかもしれません。
C++ はこの穴にどんな値が入るかは保証してくれませんので、
例え全てのメンバの値が等しくても、インスタンスによって'穴'の値が違うせいで
"異なっている"と判定されてしまうかもしれません。
<p>
これを避けるには結局、operator==() では各メンバを一つ一つ比較していく
ことになります。しかし残念なことに、これは信頼性の高い方法ではありません。
なぜなら (1) 構造体にメンバを追加したときに、operator==() への追加を
忘れるかもしれません (2) 浮動小数点数のNaNは、ビットパターンが同じでも
等しくないと判定されます。
<p>
結局、C++の範囲ではロバストな解決策はありません。
<h4>D では</h4>
Dでは直接的で明確な書き方ができます:
------
A x, y;
...
if (x == y)
...
------
<hr><!-- -------------------------------------------- -->
<h3><a name="typedefs">typedef された型</a></h3>
<h4>C++ では</h4>
C++のtypedefは'弱い'typedefです。どういうことかというと、
このtypedefは実際には新しい型を定義しない、ということです。
コンパイラはtypedefされた型と元の型を区別しません。
$(CPPCODE
#define HANDLE_INIT ((Handle)(-1))
typedef void *Handle;
void foo(void *);
void bar(Handle);
Handle h = HANDLE_INIT;
foo(h); // みつかりにくいバグ
bar(h); // ok
)
C++での解決策は、型チェックとオーバーロードを提供するだけのために
ダミーの構造体型を作ることです。
$(CPPCODE
#define HANDLE_INIT ((void *)(-1))
struct Handle
{ void *ptr;
// デフォルト初期化子
Handle() { ptr = HANDLE_INIT; }
Handle(int i) { ptr = (void *)i; }
// 元の型への変換
operator void*() { return ptr; }
};
void bar(Handle);
Handle h;
bar(h);
h = func();
if (h != HANDLE_INIT)
...
)
<h4>D では</h4>
上のようなイディオムは不要で、単にこう書けます:
------
typedef void* Handle = cast(void*)-1;
void bar(Handle);
Handle h;
bar(h);
h = func();
if (h != Handle.init)
...
------
typedefされた型ごとに元の型と違うデフォルト初期化値をあたえられる点にも、
ご注目下さい。
<hr><!-- -------------------------------------------- -->
<h3><a name="friends">friend</a></h3>
<h4>C++ では</h4>
しばしば、二つのクラスが非常に強く結びついていて、
継承関係にはないけれど互いのprivateメンバにアクセスしたい、
ということがあります。これは $(D friend) 宣言で実現します:
$(CPPCODE
class A
{
private:
int a;
public:
int foo(B *j);
friend class B;
friend int abc(A *);
};
class B
{
private:
int b;
public:
int bar(A *j);
friend class A;
};
int A::foo(B *j) { return j->b; }
int B::bar(A *j) { return j->a; }
int abc(A *p) { return p->a; }
)
<h4>D では</h4>
Dでは、同じモジュールのメンバどうしは暗黙のうちにfriend関係になります。
強く結びついたクラスは同じモジュールにあるべき、
というのは理にかなっていますから、
モジュール内はfriendとする、というのは巧みな解決策です:
------
module X;
class A
{
private:
static int a;
public:
int foo(B j) { return j.b; }
}
class B
{
private:
static int b;
public:
int bar(A j) { return j.a; }
}
int abc(A p) { return p.a; }
------
$(D private) 属性は、
他のモジュールからのメンバへのアクセスを禁じます。
<hr><!-- -------------------------------------------- -->
<h3><a name="operatoroverloading">演算子オーバーロード</a></h3>
<h4>C++ では</h4>
struct
を使って新しい算術データ型を作ったならば、
intと比較できるように比較演算子をオーバーロードすると便利です:
$(CPPCODE
struct A
{
int operator < (int i);
int operator <= (int i);
int operator > (int i);
int operator >= (int i);
};
int operator < (int i, A &a) { return a > i; }
int operator <= (int i, A &a) { return a >= i; }
int operator > (int i, A &a) { return a < i; }
int operator >= (int i, A &a) { return a <= i; }
)
合わせて8個の関数が必要でした。
<h4>D では</h4>
Dは、比較演算子どうしには本質的に互いに関係があることを認識しています。
その結果、関数は一つだけしか必要ありません:
------
struct A
{
int opCmp(int i);
}
------
コンパイラは自動で <, <=, > and >=
を解釈して、
左オペランドがオブジェクトへの参照でない場合も含めて、
$(D opCmp)
関数を使って適切に処理します。
<p>
同様の賢い規則は他の演算子のオーバーロードにでも成り立ちます。
Dでの演算子オーバーロードはより手間いらずで、エラーの出にくい
設計になっています。C++と同じことを為し遂げるのにより少ないコードで
十分用が足ります。
<hr><!-- -------------------------------------------- -->
<h3><a name="usingdeclaration">名前空間の using</a></h3>
<h4>C++ では</h4>
C++ の $(I using-declaration) は、
他の名前空間の名前を現在のスコープへと持ち込みます:
$(CPPCODE
namespace foo
{
int x;
}
using foo::x;
)
<h4>D では</h4>
Dは名前空間と#includeの代わりにモジュールを使い、
using宣言の代わりにはalias宣言を使用します:
------
/** モジュール foo.d **/
module foo;
int x;
/** 別のモジュール **/
import foo;
alias foo.x x;
------
alias は using宣言よりも高い柔軟性をそなえています。
シンボルの名前付け替えや、テンプレートメンバの参照、
ネストされたクラス名への名前付けなども alias で可能です。
<hr><!-- -------------------------------------------- -->
<h3><a name="raii">RAII (Resource Acquisition Is Initialization)</a></h3>
<h4>C++ では</h4>
C++では、メモリなどのようなリソースは、
全て明示的に扱う必要があります。
スコープを抜けるときには自動的にデスクトラクタが呼び出されるので、
リソースを解放するコードはデストラクタに置くことで、RAIIが実装されます:
$(CPPCODE
class File
{ Handle *h;
~File()
{
h->release();
}
};
)
<h4>D では</h4>
リソース管理の問題の多くは、
メモリ利用の追跡と解放です。
この問題はDではガベージコレクタによって自動的に処理されています。
二番目によく使われるリソースはセマフォやロックですが、
$(D synchronized) 宣言/文 で自動的に処理されます。
<p>
残った数少ないRAIIの問題は、$(I scope) クラスで扱います。
scopeクラスのデストラクタはスコープ終了と同時に呼び出されます。
------
scope class File
{ Handle h;
~this()
{
h.release();
}
}
void test()
{
if (...)
{ scope f = new File();
...
} // 閉じ括弧に来ると f.~this() が呼ばれます。
// 例え例外が投げられたときでも。
}
------
<hr><!-- -------------------------------------------- -->
<h3><a name="properties">プロパティ</a></h3>
<h4>C++ では</h4>
オブジェクト指向の考え方に沿って、
フィールドを定義するのと一緒に
get や set 関数を作る習慣は一般的です:
$(CPPCODE
class Abc
{
public:
void setProperty(int newproperty) { property = newproperty; }
int getProperty() { return property; }
private:
int property;
};
Abc a;
a.setProperty(3);
int x = a.getProperty();
)
これらはタイプ量はちょっとしたものですし、getProperty() や
setProperty() の呼び出しで溢れかえって、
コードを読みにくくする傾向もあります。
<h4>D では</h4>
プロパティは通常のフィールドアクセスの構文で get/set でき、
しかしgetとsetの際には代わりにメソッドが呼び出されます。
------
class Abc
{
// set
void property(int newproperty) { myprop = newproperty; }
// get
int property() { return myprop; }
private:
int myprop;
}
------
これは次のように使います:
------
Abc a;
a.property = 3; // a.property(3) と同じ
int x = a.property; // int x = a.property() と同じ
------
つまり、
D ではプロパティは単にフィールド名と同じように扱うことができます。
最初は本物のフィールド名として書き始めて後で読み書き用の関数を
かませる必要ができたときも、
クラス定義以外のコードを書き直す必要が全くありません。
派生クラスでオーバーライドの必要ができるかもしれないので
$(SINGLEQUOTE 万が一のために) get/setプロパティを定義しておく、
と言った冗長な慣習は不要のもととなります。
"データフィールドを持たないけれども、構文上は持っているかのように動作する
インターフェイスクラス" を作る方法としても活用できます。
<hr><!-- -------------------------------------------- -->
<h3><a name="recursivetemplates">再帰的テンプレート</a></h3>
<h4>C++ では</h4>
テンプレートの発展的な使い方としては、
特殊化によって停止を期待して、再帰的なテンプレート展開を行うことです。
階乗を計算するテンプレートはこうなります:
$(CPPCODE
template<int n> class factorial
{
public:
enum { result = n * factorial<n - 1>::result };
};
template<> class factorial<1>
{
public:
enum { result = 1 };
};
void test()
{
printf("%d\n", factorial<4>::result); // prints 24
}
)
<h4>D では</h4>
D版も同様ですが、若干シンプルになっています。
テンプレートメンバが一つだけの時は
識別子が周囲の名前空間へ昇格できる、という性質をうまく利用しています:
------
template factorial(int n)
{
enum { factorial = n * .factorial!(n-1) }
}
template factorial(int n : 1)
{
enum { factorial = 1 }
}
void test()
{
writefln("%d", factorial!(4)); // 24を表示
}
------
<hr><!-- -------------------------------------------- -->
<h3><a name="metatemplates">メタテンプレート</a></h3>
問題: 最低 $(I nbits) のサイズを持つ符号付き整数型への
typedef を作りたい。
<h4>C++ では</h4>
この例は、Dr. Carlo Pescio による記事
<a href="http://www.eptacom.net/pubblicazioni/pub_eng/paramint.html">
Template Metaprogramming: Make parameterized integers portable with this novel technique</a>.
を簡単にしたものです。
<p>
C++では、テンプレート引数を使った式の結果に基づく条件コンパイルは、
不可能です。そこで全ての制御フローは、
多数の明示テンプレート特殊化とのパターンマッチングを
追うことで実現されます。
残念なことに、"より小さいか等しい" といった関係に基づくテンプレート特殊化を
記述する方法がないため、今回の例は、
テンプレートの再帰的展開を使った巧妙な技を使用します。
つまり、境界値にマッチするまでテンプレート引数値を1ずつ増やしていくことで、
"より小さいか等しい" を実現しているのです。特殊化版にマッチしなかった場合、
コンパイラの止まらない再帰によるスタックオーバーフローや内部エラーか、
良くても、
奇妙な構文エラーが出力されます。
<p>
template typedef の不在を補うために、
プリプロセッサマクロも必要です。
$(CPPCODE
#include <limits.h>
template< int nbits > struct Integer
{
typedef Integer< nbits + 1 > :: int_type int_type ;
} ;
struct Integer< 8 >
{
typedef signed char int_type ;
} ;
struct Integer< 16 >
{
typedef short int_type ;
} ;
struct Integer< 32 >
{
typedef int int_type ;
} ;
struct Integer< 64 >
{
typedef long long int_type ;
} ;
// 要求されたサイズをサポートしていない場合、メタプログラムは、
// 内部エラーが発生するか INT_MAX に達するまで
// カウンタを増やし続けます。 INT_MAX の特殊化版は
// int_type を提供しないようにすることで、
// 常にコンパイルエラーを起こすことができます。
struct Integer< INT_MAX >
{
} ;
// ちょっとした構文糖
#define Integer( nbits ) Integer< nbits > :: int_type
#include <stdio.h>
int main()
{
Integer( 8 ) i ;
Integer( 16 ) j ;
Integer( 29 ) k ;
Integer( 64 ) l ;
printf("%d %d %d %d\n",
sizeof(i), sizeof(j), sizeof(k), sizeof(l));
return 0 ;
}
)
<h4>The C++ Boost Way</h4>
これは C++ Boost library を使ったバージョンです。
David Abrahams によって書かれました。
$(CPPCODE
#include <boost/mpl/if.hpp>
#include <boost/mpl/assert.hpp>
template <int nbits> struct Integer
: mpl::if_c<(nbits <= 8), signed char
, mpl::if_c<(nbits <= 16), short
, mpl::if_c<(nbits <= 32), long
, long long>::type >::type >
{
BOOST_MPL_ASSERT_RELATION(nbits, <=, 64);
}
#include <stdio.h>
int main()
{
Integer< 8 > i ;
Integer< 16 > j ;
Integer< 29 > k ;
Integer< 64 > l ;
printf("%d %d %d %d\n",
sizeof(i), sizeof(j), sizeof(k), sizeof(l));
return 0 ;
}
)
<h4>D では</h4>
D版も再帰テンプレートを使って書いても構いませんが、
もっといい方法があります。
C++の例とは違って、このコードによって何が起きているのかが
極めてわかりやすくなっています。
コンパイル速度も速く、コンパイル失敗するときも、
何が起きたかわかりやすいエラーメッセージを表示します。
------
import std.stdio;
template Integer(int nbits)
{
static if (nbits <= 8)
alias byte Integer;
else static if (nbits <= 16)
alias short Integer;
else static if (nbits <= 32)
alias int Integer;
else static if (nbits <= 64)
alias long Integer;
else
static assert(0);
}
int main()
{
Integer!(8) i ;
Integer!(16) j ;
Integer!(29) k ;
Integer!(64) l ;
writefln("%d %d %d %d",
i.sizeof, j.sizeof, k.sizeof, l.sizeof);
return 0;
}
------
<hr><!-- -------------------------------------------- -->
<h3><a name="typetraits">Type Traits</a></h3>
"Type traits" とは、
コンパイル時に型固有の情報を探し出す方法の別名です。
<h4>C++ では</h4>
以下のテンプレートは、
<a href="http://www.amazon.co.jp/dp/0201734842">
C++ Templates: The Complete Guide, David Vandevoorde, Nicolai M. Josuttis</a>
の353ページから抜粋したものです。
テンプレート引数型が関数であるかどうかを判定しています:
$(CPPCODE
template<typename T> class IsFunctionT
{
private:
typedef char One;
typedef struct { char a[2]; } Two;
template<typename U> static One test(...);
template<typename U> static Two test(U (*)[1]);
public:
enum { Yes = sizeof(IsFunctionT<T>::test<T>(0)) == 1 };
};
void test()
{
typedef int (fp)(int);
assert(IsFunctionT<fp>::Yes == 1);
}
)
このテンプレートは、$(SFINAE) 原則を利用しています。
そのため、テンプレートの話題としては高度な部類に属するものとされています。
<h4>D では</h4>
Dでは、
$(ACRONYM SFINAE, Substitution Failure Is Not An Error)
やテンプレート引数のパターンマッチといった小技に頼らず実現できます:
------
template IsFunctionT(T)
{
static if ( is(T[]) )
const int IsFunctionT = 0;
else
const int IsFunctionT = 1;
}
void test()
{
typedef int fp(int);
assert(IsFunctionT!(fp) == 1);
}
------
型が関数型かどうかを判定するという処理は、テンプレートの力を借りる必要は全くありませんし、
ましてや配列型の不正な配列を作ろうとするなどというおかしなコードなど、
全く不要です。
Dの $(ISEXPRESSION) がまさにこの仕事をやってのけます:
------
void test()
{
alias int fp(int);
assert( is(fp == function) );
}
------
)
Macros:
TITLE=C++プログラマのためのD言語
WIKI=CPPtoD
CATEGORY_OVERVIEW=$0