forked from dlang/dlang.org
-
Notifications
You must be signed in to change notification settings - Fork 0
/
cpptod.dd
825 lines (642 loc) · 18.3 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
819
820
821
822
823
824
825
Ddoc
$(COMMUNITY Programming in D for C++ Programmers,
<!--img src="images/cpp1.gif" border=0 align=right alt="C++"-->
$(P Every experienced C++ programmer accumulates a series of idioms and techniques
which become second nature. Sometimes, when learning a new language, those
idioms can be so comfortable it's hard to see how to do the equivalent in the
new language. So here's a collection of common C++ techniques, and how to do the
corresponding task in D.)
See also: <a href="ctod.html">Programming in D for C Programmers</a>
$(UL
$(LI $(RELATIVE_LINK2 constructors, Defining Constructors))
$(LI $(RELATIVE_LINK2 baseclass, Base class initialization))
$(LI $(RELATIVE_LINK2 structcmp, Comparing structs))
$(LI $(RELATIVE_LINK2 typedefs, Creating a new typedef'd type))
$(LI $(RELATIVE_LINK2 friends, Friends))
$(LI $(RELATIVE_LINK2 operatoroverloading, Operator overloading))
$(LI $(RELATIVE_LINK2 usingdeclaration, Namespace using declarations))
$(LI $(RELATIVE_LINK2 raii, RAII (Resource Acquisition Is Initialization)))
$(LI $(RELATIVE_LINK2 properties, Properties))
$(LI $(RELATIVE_LINK2 recursivetemplates, Recursive Templates))
$(LI $(RELATIVE_LINK2 metatemplates, Meta Templates))
$(LI $(RELATIVE_LINK2 typetraits, Type Traits))
)
<hr><!-- -------------------------------------------- -->
$(H3 <a name="constructors">Defining constructors</a>)
$(H4 The C++ Way)
Constructors have the same name as the class:
$(CPPCODE
class Foo
{
Foo(int x);
};
)
$(H4 The D Way)
Constructors are defined with the this keyword:
------
class Foo
{
this(int x) { }
}
------
which reflects how they are used in D.
<hr><!-- -------------------------------------------- -->
$(H3 <a name="baseclass">Base class initialization</a>)
$(H4 The C++ Way)
Base constructors are called using the base initializer syntax.
$(CPPCODE
class A { A() {... } };
class B : A
{
B(int x)
: A() // call base constructor
{
...
}
};)
$(H4 The D Way)
The base class constructor is called with the super syntax:
------
class A { this() { ... } }
class B : A
{
this(int x)
{
...
super(); // call base constructor
...
}
}
------
It's superior to C++ in that the base constructor call can be flexibly placed anywhere in the derived
constructor. D can also have one constructor call another one:
------
class A
{
int a;
int b;
this() { a = 7; b = foo(); }
this(int x)
{
this();
a = x;
}
}
------
Members can also be initialized to constants before the constructor is ever called, so the above example is
equivalently written as:
------
class A
{
int a = 7;
int b;
this() { b = foo(); }
this(int x)
{
this();
a = x;
}
}
------
<hr><!-- -------------------------------------------- -->
$(H3 <a name="structcmp">Comparing structs</a>)
$(H4 The C++ Way)
While C++ defines struct assignment in a simple, convenient manner:
$(CPPCODE
struct A x, y;
...
x = y;
)
it does not for struct comparisons. Hence, to compare two struct
instances for equality:
$(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)
...
)
$(P Note that the operator overload must be done for every struct
needing to be compared, and the implementation of that overloaded
operator is free of any language help with type checking.
The C++ way has an additional problem in that just inspecting the
(x == y) does not give a clue what is actually happening, you have
to go and find the particular overloaded operator==() that applies
to verify what it really does.)
$(P There's a nasty bug lurking in the memcmp() implementation of operator==().
The layout of a struct, due to alignment, can have $(SINGLEQUOTE holes) in it.
C++ does not guarantee those holes are assigned any values, and so
two different struct instances can have the same value for each member,
but compare different because the holes contain different garbage.)
$(P To address this, the operator==() can be implemented to do a memberwise
compare. Unfortunately, this is unreliable because (1) if a member is added
to the struct definition one may forget to add it to operator==(), and
(2) floating point nan values compare unequal even if their bit patterns
match.)
There just is no robust solution in C++.
$(H4 The D Way)
D does it the obvious, straightforward way:
------
A x, y;
...
if (x == y)
...
------
<hr><!-- -------------------------------------------- -->
$(H3 <a name="typedefs">Creating a new typedef'd type</a>)
$(H4 The C++ Way)
Typedef's in C++ are weak, that is, they really do not introduce
a new type. The compiler doesn't distinguish between a typedef
and its underlying type.
$(CPPCODE
#define HANDLE_INIT ((Handle)(-1))
typedef void *Handle;
void foo(void *);
void bar(Handle);
Handle h = HANDLE_INIT;
foo(h); // coding bug not caught
bar(h); // ok
)
The C++ solution is to create a dummy struct whose sole
purpose is to get type checking and overloading on the new type.
$(CPPCODE
#define HANDLE_INIT ((void *)(-1))
struct Handle
{
void *ptr;
// default initializer
Handle() { ptr = HANDLE_INIT; }
Handle(int i) { ptr = (void *)i; }
// conversion to underlying type
operator void*() { return ptr; }
};
void bar(Handle);
Handle h;
bar(h);
h = func();
if (h != HANDLE_INIT)
...
)
$(H4 The D Way)
No need for idiomatic constructions like the above. Just write:
------
typedef void* Handle = cast(void*)-1;
void bar(Handle);
Handle h;
bar(h);
h = func();
if (h != Handle.init)
...
------
Note how a default initializer can be supplied for the typedef as
a value of the underlying type.
<hr><!-- -------------------------------------------- -->
$(H3 <a name="friends">Friends</a>)
$(H4 The C++ Way)
Sometimes two classes are tightly related but not by inheritance,
but need to access each other's private members. This is done
using $(D friend) declarations:
$(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 The D Way)
In D, friend access is implicit in being a member of the same
module. It makes sense that tightly related classes should be
in the same module, so implicitly granting friend access to
other module members solves the problem neatly:
------
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; }
------
The $(D private) attribute prevents other modules from
accessing the members.
<hr><!-- -------------------------------------------- -->
$(H3 <a name="operatoroverloading">Operator overloading</a>)
$(H4 The C++ Way)
Given a struct that creates a new arithmetic data type,
it's convenient to overload the comparison operators so
it can be compared against integers:
$(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; }
)
A total of 8 functions are necessary.
$(H4 The D Way)
D recognizes that the comparison operators are all fundamentally
related to each other. So only one function is necessary:
------
struct A
{
int opCmp(int i);
}
------
$(P The compiler automatically interprets all the
<, <=, > and >=
operators in terms of the $(D cmp) function, as well
as handling the cases where the left operand is not an
object reference.)
$(P Similar sensible rules hold for other operator overloads,
making using operator overloading in D much less tedious and less
error prone. Far less code needs to be written to accomplish
the same effect.)
<hr><!-- -------------------------------------------- -->
$(H3 <a name="usingdeclaration">Namespace using declarations</a>)
$(H4 The C++ Way)
A $(I using-declaration) in C++ is used to bring a name from
a namespace scope into the current scope:
$(CPPCODE
namespace foo
{
int x;
}
using foo::x;
)
$(H4 The D Way)
D uses modules instead of namespaces and #include files, and
alias declarations take the place of using declarations:
------
/** Module foo.d **/
module foo;
int x;
/** Another module **/
import foo;
alias x = foo.x;
------
Alias is a much more flexible than the single purpose using
declaration. Alias can be used to rename symbols, refer to
template members, refer to nested class types, etc.
<hr><!-- -------------------------------------------- -->
$(H3 <a name="raii">RAII (Resource Acquisition Is Initialization)</a>)
$(H4 The C++ Way)
In C++, resources like memory, etc., all need to be handled
explicitly. Since destructors automatically get called when
leaving a scope, RAII is implemented by putting the resource
release code into the destructor:
$(CPPCODE
class File
{
Handle *h;
~File()
{
h->release();
}
};
)
$(H4 The D Way)
$(P The bulk of resource release problems are simply keeping track
of and freeing memory. This is handled automatically in D by
the garbage collector. The second common resources used are semaphores
and locks, handled automatically with D's $(D synchronized)
declarations and statements.)
$(P The few RAII issues left are handled by $(D struct)s.
A $(D struct) gets its destructor run when it goes out of scope.)
------
struct File
{
Handle h;
~this()
{
h.release();
}
}
void test()
{
if (...)
{
auto f = File();
...
} // f.~this() gets run at closing brace, even if
// scope was exited via a thrown exception
}
------
$(P $(D class)es are typically managed by the garbage collector which doesn't
lend itself to RAII. If you need deterministic destruction with $(D class)es
you can use $(FULL_XREF typecons, scoped) (which will also allocate the
$(D class) on the stack instead of the garbage collector managed heap).)
$(P See also $(GLINK2 statement, ScopeGuardStatement) for a more generalized
mechanism that lets you run arbitrary statements whenever leaving the current
scope.)
<hr><!-- -------------------------------------------- -->
$(H3 <a name="properties">Properties</a>)
$(H4 The C++ Way)
It is common practice to define a field,
along with object-oriented
get and set functions for it:
$(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();
)
All this is quite a bit of typing, and it tends to make
code unreadable by filling
it with getProperty() and setProperty() calls.
$(H4 The D Way)
Properties can be get and set using the normal field syntax,
yet the get and set will invoke methods instead.
------
class Abc
{
// set
@property void property(int newproperty) { myprop = newproperty; }
// get
@property int property() { return myprop; }
private:
int myprop;
}
------
which is used as:
------
Abc a = new Abc;
a.property = 3;
int x = a.property;
------
Thus, in D a property is treated like it was a simple field name.
A property can start out actually being a simple field name,
but if later it becomes
necessary to make getting and setting it function calls,
no code needs to be modified other
than the class definition.
It obviates the wordy practice of defining get and set properties
$(SINGLEQUOTE just in case) a derived class should need to override them.
It's also a way to have interface classes, which do not have
data fields, behave syntactically as if they did.
<hr><!-- -------------------------------------------- -->
$(H3 <a name="recursivetemplates">Recursive Templates</a>)
$(H4 The C++ Way)
An advanced use of templates is to recursively expand
them, relying on specialization to end it. A template
to compute a factorial would be:
$(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 The D Way)
The D version is analogous, though a little simpler, taking
advantage of promotion of single template members to the
enclosing name space:
------
template factorial(int n)
{
enum { factorial = n * .factorial!(n-1) }
}
template factorial(int n : 1)
{
enum { factorial = 1 }
}
void test()
{
writefln("%d", factorial!(4)); // prints 24
}
------
<hr><!-- -------------------------------------------- -->
$(H3 <a name="metatemplates">Meta Templates</a>)
The problem: create a typedef for a signed integral type that is at
least $(I nbits) in size.
$(H4 The C++ Way)
$(P This example is simplified and adapted from one written by
Dr. Carlo Pescio in
<a href="http://www.eptacom.net/pubblicazioni/pub_eng/paramint.html">
Template Metaprogramming: Make parameterized integers portable with this novel technique</a>.)
$(P There is no way in C++ to do conditional compilation based
on the result of an expression based on template parameters, so
all control flow follows from pattern matching of the template
argument against various explicit template specializations.
Even worse, there is no way to do template specializations based
on relationships like "less than or equal to", so the example
uses a clever technique where the template is recursively expanded,
incrementing the template value argument by one each time, until
a specialization matches.
If there is no match, the result is an unhelpful recursive compiler
stack overflow or internal error, or at best a strange syntax
error.)
A preprocessor macro is also needed to make up for the lack
of template typedefs.
$(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 ;
};
// If the required size is not supported, the metaprogram
// will increase the counter until an internal error is
// signaled, or INT_MAX is reached. The INT_MAX
// specialization does not define a int_type, so a
// compiling error is always generated
struct Integer< INT_MAX >
{
};
// A bit of syntactic sugar
#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)
This version uses the C++ Boost library. It was provided
by 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 The D Way)
The D version could also be written with recursive templates,
but there's a better way.
Unlike the C++ example, this one is fairly easy to
figure out what is going on.
It compiles quickly, and gives a sensible compile time message
if it fails.
------
import std.stdio;
template Integer(int nbits)
{
static if (nbits <= 8)
alias Integer = byte;
else static if (nbits <= 16)
alias Integer = short;
else static if (nbits <= 32)
alias Integer = int;
else static if (nbits <= 64)
alias Integer = long;
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>)
Type traits are another term for being able to find out
properties of a type at compile time.
$(H4 The C++ Way)
The following template comes from
<a href="http://www.amazon.com/exec/obidos/ASIN/0201734842/ref=ase_classicempire/102-2957199-2585768">
C++ Templates: The Complete Guide, David Vandevoorde, Nicolai M. Josuttis</a>
pg. 353 which determines if the template's argument type
is a function:
$(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);
}
)
This template relies on the $(SFINAE) principle.
Why it works is a fairly advanced template topic.
$(H4 The D Way)
$(ACRONYM SFINAE, Substitution Failure Is Not An Error)
can be done in D without resorting to template argument
pattern matching:
------
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);
}
------
The task of discovering if a type is a function doesn't need a
template at all, nor does it need the subterfuge of attempting to
create the invalid array of functions type.
The $(ISEXPRESSION) expression can test it directly:
------
void test()
{
alias int fp(int);
assert( is(fp == function) );
}
------
)
Macros:
TITLE=Programming in D for C++ Programmers
WIKI=CPPtoD