diff -uNr dmd-0.135/dmd/html/d/abi.html dmd-0.136/dmd/html/d/abi.html --- dmd-0.135/dmd/html/d/abi.html 2005-09-18 11:39:24.000000000 +0200 +++ dmd-0.136/dmd/html/d/abi.html 2005-10-04 18:05:30.000000000 +0200 @@ -1,289 +1,345 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - -
- -
-
-Home
-| Search
-| D
-| Comments
-
-|
-
- - -· Lexical -· Modules -· Declarations -· Types -· Properties -· Attributes -· Pragmas -· Expressions -· Statements -· Arrays -· Structs & Unions -· Classes -· Interfaces -· Enums -· Functions -· Operator Overloading -· Templates -· Mixins -· Contracts -· Conditional Compilation -· Handling errors -· Garbage Collection -· Memory Management -· Floating Point -· Inline Assembler -· Documentation Comments -· Interfacing To C -· Portability Guide -· Embedding D in HTML -· Named Character Entities -· Application Binary Interface - - |
-
-
-
-
-D Application Binary Interface- - A D implementation that conforms to the D ABI (Application Binary Interface) - will be able to generate libraries, DLL's, etc., that can interoperate with - D binaries built by other implementations. -- - Most of this specification remains TBD (To Be Defined). - - C ABI- - The C ABI referred to in this specification means the C Application - Binary Interface of the target system. - C and D code should be freely linkable together, in particular, D - code shall have access to the entire C ABI runtime library. - -Basic Types- - TBD - -Structs- - Conforms to the target's C ABI struct layout. - -Classes- - An object consists of: - -- offset contents - ------ -------- - 0: pointer to vtable - 4: monitor - 8... non-static members -- - The vtable consists of: - - - 0: pointer to instance of ClassInfo - 4... pointers to virtual member functions -- - The class definition: - -
- class XXXX
- {
- ....
- };
-
-
- Generates the following:
-
-
Interfaces- - TBD - -Arrays- - A dynamic array consists of: - -- 0: array dimension - 4: pointer to array data -- - A dynamic array is declared as: - - - type array[]; -- - whereas a static array is declared as: - - - type array[dimension]; -- - Thus, a static array always has the dimension statically available as part of the type, and - so it is implemented like in C. Static array's and Dynamic arrays can be easily converted back - and forth to each other. - - - Associative Arrays- - TBD - -Reference Types- - D has reference types, but they are implicit. For example, classes are always - referred to by reference; this means that class instances can never reside on the stack - or be passed as function parameters. -- - When passing a static array to a function, the result, although declared as a static array, will - actually be a reference to a static array. For example: - - - int abc[3]; -- - Passing abc to functions results in these implicit conversions: - - - void func(int array[3]); // actually- - - Name Mangling- - TBD - -Function Calling Conventions- - TBD - -Exception Handling- -Windows- - Conforms to the Microsoft Windows Structured Exception Handling - conventions. - TBD - -Linux- - Uses static address range/handler tables. - TBD - -Garbage Collection- - TBD - -Runtime Helper Functions- - TBD - -Module Initialization and Termination- - TBD - -Unit Testing- - TBD - - |
Add feedback and comments regarding this - page.
- -|
+
+
+
+
+ + +· Lexical + + +· Modules + + +· Declarations + + +· Types + + +· Properties + + +· Attributes + + +· Pragmas + + +· Expressions + + +· Statements + + +· Arrays + + +· Structs & Unions + + +· Classes + + +· Interfaces + + +· Enums + + +· Functions + + +· Operator Overloading + + +· Templates + + +· Mixins + + +· Contracts + + +· Conditional Compilation + + +· Handling errors + + +· Garbage Collection + + +· Memory Management + + +· Floating Point + + +· Inline Assembler + + +· Documentation Comments + + +· Interfacing To C + + +· Portability Guide + + +· Embedding D in HTML + + +· Named Character Entities + + +· Application Binary Interface + + + + |
+
+
+
+D Application Binary Interface+ + A D implementation that conforms to the D ABI (Application Binary Interface) + will be able to generate libraries, DLL's, etc., that can interoperate with + D binaries built by other implementations. ++ + Most of this specification remains TBD (To Be Defined). + + C ABI+ + The C ABI referred to in this specification means the C Application + Binary Interface of the target system. + C and D code should be freely linkable together, in particular, D + code shall have access to the entire C ABI runtime library. + +Basic Types+ + TBD + +Structs+ + Conforms to the target's C ABI struct layout. + +Classes+ + An object consists of: ++ +
+ + The vtable consists of: + + +
+ + The class definition: + + class XXXX
+{
+ ....
+};
+
+
+ Generates the following:
+
+
Interfaces+ + TBD + +Arrays+ + A dynamic array consists of: ++ +
+ + A dynamic array is declared as: + + type array[]; ++ + whereas a static array is declared as: + + type array[dimension]; ++ + Thus, a static array always has the dimension statically available as part of the type, and + so it is implemented like in C. Static array's and Dynamic arrays can be easily converted back + and forth to each other. + + + Associative Arrays+ + TBD + +Reference Types+ + D has reference types, but they are implicit. For example, classes are always + referred to by reference; this means that class instances can never reside on the stack + or be passed as function parameters. ++ + When passing a static array to a function, the result, although declared as a static array, will + actually be a reference to a static array. For example: + + int abc[3];
+
+
+ Passing abc to functions results in these implicit conversions:
+
+void func(int array[3]); // actually <reference to><array[3] of><int> +void func(int *p); // abc[3] is converted to a pointer + // to the first element +void func(int array[]); // abc[3] is converted to a dynamic array ++ + + Name Mangling+ + TBD + +Function Calling Conventions+ + TBD + +Exception Handling+ +Windows+ + Conforms to the Microsoft Windows Structured Exception Handling + conventions. + TBD + +Linux+ + Uses static address range/handler tables. + TBD + +Garbage Collection+ + TBD + +Runtime Helper Functions+ + TBD + +Module Initialization and Termination+ + TBD + +Unit Testing+ + TBD + + + |
-
-Home
-| Search
-| D
-| Comments
-
-|
-
- - - · Overview - · D for Win32 - · Win32 DLLs in D - · C .h to D Modules - · FAQ - · Style Guide - · Example: wc - · Future - · D Change Log - · Tech Tips - · Glossary - · Acknowledgements - - Tools - - · DMD D Compiler - · GDC D Compiler - · Linker - · Profiler - - Community - - · News Digest - · News - · Forum - · Announcements - · Learn - · D links - - Archives - - · digitalmars.D - · digitalmars.D.dtl - · digitalmars.D.announce - · digitalmars.D.learn - · digitalmars.D.bugs - · D.gnu - · Old D - - - |
-
-
-
-
-Acknowledgements- - -The following (incomplete) list of people have contributed to the D language -project; -with ideas, code, expertise, marketing, inspiration -and moral support. -- Kris Bell, - Don Clugston, - Hauke Duden, - Bruce Eckel, - Eric Engstrom, - Dave Fladebo, - David Friedman, - Stewart Gordon, - Ben Hinkle, - Jan Knepper, - Thomas Kuehne, - Helmut Leitner, - Lubomir Litchev, - Christopher E. Miller, - Pavel Minayev, - Antonio Monteiro, - Paul Nash, - Pat Nelson, - Derek Parnell, - Burton Radons, - Tim Rentsch, - Fabio Riccardi, - Bob Taniguchi, - Bob W, - John Whited, - Matthew Wilson, - Peter Zatloukal - - |
Add feedback and comments regarding this - page.
- -|
+
+
+
+
+ + + · Overview + + + · D for Win32 + + + · Win32 DLLs in D + + + · C .h to D Modules + + + · FAQ + + + · Style Guide + + + · Example: wc + + + · Future + + + · D Change Log + + + · Tech Tips + + + · Rationale + + + · Glossary + + + · Acknowledgements + + + + Tools + + · DMD D Compiler + + + · GDC D Compiler + + + · Linker + + + · Profiler + + + + Community + + · News Digest + + + · News + + + · Forum + + + · Announcements + + + · Learn + + + · D links + + + + Archives + + · digitalmars.D + + + · digitalmars.D.dtl + + + · digitalmars.D.announce + + + · digitalmars.D.learn + + + · digitalmars.D.bugs + + + · D.gnu + + + · Old D + + + + + |
+
+
+Acknowledgements+ +The following (incomplete) list of people have contributed to the D language +project; +with ideas, code, expertise, marketing, inspiration +and moral support. + + +Kris Bell, + Don Clugston, + Hauke Duden, + Bruce Eckel, + Eric Engstrom, + Dave Fladebo, + David Friedman, + Stewart Gordon, + Ben Hinkle, + Jan Knepper, + Thomas Kuehne, + Helmut Leitner, + Lubomir Litchev, + Christopher E. Miller, + Pavel Minayev, + Antonio Monteiro, + Paul Nash, + Pat Nelson, + Derek Parnell, + Burton Radons, + Tim Rentsch, + Fabio Riccardi, + Bob Taniguchi, + Bob W, + John Whited, + Matthew Wilson, + Peter Zatloukal + + + |
-
-Home
-| Search
-| D
-| Comments
-
-|
-
- - -· Lexical -· Modules -· Declarations -· Types -· Properties -· Attributes -· Pragmas -· Expressions -· Statements -· Arrays -· Structs & Unions -· Classes -· Interfaces -· Enums -· Functions -· Operator Overloading -· Templates -· Mixins -· Contracts -· Conditional Compilation -· Handling errors -· Garbage Collection -· Memory Management -· Floating Point -· Inline Assembler -· Documentation Comments -· Interfacing To C -· Portability Guide -· Embedding D in HTML -· Named Character Entities -· Application Binary Interface - - |
-
-
-
-
-Arrays- - There are four kinds of arrays: - -
Pointers- -- int* p; -- - These are simple pointers to data, analogous to C pointers. - Pointers are provided for interfacing with C and for - specialized systems work. - There - is no length associated with it, and so there is no way for the - compiler or runtime to do bounds checking, etc., on it. - Most conventional uses for pointers can be replaced with - dynamic arrays, out and inout parameters, - and reference types. - - Static Arrays- -- int[3] s; -- - These are analogous to C arrays. Static arrays are distinguished - by having a length fixed at compile time. - - - The total size of a static array cannot exceed 16Mb. - A dynamic array should be used instead for such large arrays. - - Dynamic Arrays- -- int[] a; -- - Dynamic arrays consist of a length and a pointer to the array data. - Multiple dynamic arrays can share all or parts of the array data. - - Array Declarations- - There are two ways to declare arrays, prefix and postfix. - The prefix form is the preferred method, especially for - non-trivial types. - -Prefix Array Declarations- - Prefix declarations appear before the identifier being - declared and read right to left, so: - -- int[] a; // dynamic array of ints - int[4][3] b; // array of 3 arrays of 4 ints each - int[][5] c; // array of 5 dynamic arrays of ints. - int*[]*[3] d; // array of 3 pointers to dynamic arrays of pointers to ints - int[]* e; // pointer to dynamic array of ints -- - - Postfix Array Declarations- - Postfix declarations appear after the identifier being - declared and read left to right. - Each group lists equivalent declarations: - -- // dynamic array of ints - int[] a; - int a[]; - - // array of 3 arrays of 4 ints each - int[4][3] b; - int[4] b[3]; - int b[3][4]; - - // array of 5 dynamic arrays of ints. - int[][5] c; - int[] c[5]; - int c[5][]; - - // array of 3 pointers to dynamic arrays of pointers to ints - int*[]*[3] d; - int*[]* d[3]; - int* (*d[3])[]; - - // pointer to dynamic array of ints - int[]* e; - int (*e[]); -- - Rationale: The postfix form matches the way arrays are - declared in C and C++, and supporting this form provides an - easy migration path for programmers used to it. - - Usage- - There are two broad kinds of operations to do on an array - affecting - the handle to the array, - and affecting the contents of the array. - C only has - operators to affect the handle. In D, both are accessible. -- - The handle to an array is specified by naming the array, as - in p, s or a: - - - int* p; - int[3] s; - int[] a; - - int* q; - int[3] t; - int[] b; - - p = q; p points to the same thing q does. - p = s; p points to the first element of the array s. - p = a; p points to the first element of the array a. - - s = ...; error, since s is a compiled in static - reference to an array. - - a = p; error, since the length of the array pointed - to by p is unknown - a = s; a is initialized to point to the s array - a = b; a points to the same array as b does -- - Slicing- - Slicing an array means to specify a subarray of it. - An array slice does not copy the data, it is only another - reference to it. - For example: - -- int[10] a; // declare array of 10 ints - int[] b; - - b = a[1..3]; // a[1..3] is a 2 element array consisting of - // a[1] and a[2] - foo(b[1]); // equivalent to foo(0) - a[2] = 3; - foo(b[1]); // equivalent to foo(3) -- - The [] is shorthand for a slice of the entire array. - For example, the assignments to b: - - - int[10] a; - int[] b; - - b = a; - b = a[]; - b = a[0 .. a.length]; -- are all semantically equivalent. - - - Slicing - is not only handy for referring to parts of other arrays, - but for converting pointers into bounds-checked arrays: - - - int* p; - int[] b = p[0..8]; -- - Slicing for bit arrays is only allowed if the slice's lower - bound falls on a byte boundary: - - - bit[] b; - ... - b[0..8]; // ok - b[8..16]; // ok - b[8..17]; // ok - b[1..16]; // error, lower bound is not on a byte boundary -- - Misaligned bit array slices will cause an ArrayBoundsError exception - to be thrown at runtime. - - Array Copying- - When the slice operator appears as the lvalue of an assignment - expression, it means that the contents of the array are the - target of the assignment rather than a reference to the array. - Array copying happens when the lvalue is a slice, and the rvalue - is an array of or pointer to the same type. - -- int[3] s; - int[3] t; - - s[] = t; the 3 elements of t[3] are copied into s[3] - s[] = t[]; the 3 elements of t[3] are copied into s[3] - s[1..2] = t[0..1]; same as s[1] = t[0] - s[0..2] = t[1..3]; same as s[0] = t[1], s[1] = t[2] - s[0..4] = t[0..4]; error, only 3 elements in s - s[0..2] = t; error, different lengths for lvalue and rvalue -- - Overlapping copies are an error: - - - s[0..2] = s[1..3]; error, overlapping copy - s[1..3] = s[0..2]; error, overlapping copy- - Disallowing overlapping makes it possible for more aggressive - parallel code optimizations than possible with the serial - semantics of C. - - Array Setting- - If a slice operator appears as the lvalue of an assignment - expression, and the type of the rvalue is the same as the element - type of the lvalue, then the lvalue's array contents - are set to the rvalue. - -- int[3] s; - int* p; - - s[] = 3; same as s[0] = 3, s[1] = 3, s[2] = 3 - p[0..2] = 3; same as p[0] = 3, p[1] = 3 -- - Array Concatenation- - The binary operator ~ is the cat operator. It is used - to concatenate arrays: - -- - int[] a; - int[] b; - int[] c; - - a = b ~ c; Create an array from the concatenation of the - b and c arrays -- - Many languages overload the + operator to mean concatenation. - This confusingly leads to, does: - - - "10" + 3 -- - produce the number 13 or the string "103" as the result? It isn't - obvious, and the language designers wind up carefully writing rules - to disambiguate it - rules that get incorrectly implemented, - overlooked, forgotten, and ignored. It's much better to have + mean - addition, and a separate operator to be array concatenation. - - - Similarly, the ~= operator means append, as in: - - - a ~= b; a becomes the concatenation of a and b -- - Concatenation always creates a copy of its operands, even - if one of the operands is a 0 length array, so: - - - a = b a refers to b - a = b ~ c[0..0] a refers to a copy of b -- - - - Array Operations- - Note: Array operations are not implemented. -- - In general, (a[n..m] op e) is defined as: - - - for (i = n; i < m; i++) - a[i] op e; -- - So, for the expression: - - - a[] = b[] + 3; -- - the result is equivalent to: - - - for (i = 0; i < a.length; i++) - a[i] = b[i] + 3; -- - When more than one [] operator appears in an expression, the range - represented by all must match. - - - a[1..3] = b[] + 3; error, 2 elements not same as 3 elements -- - Examples:- -
- int[3] abc; // static array of 3 ints
- int[] def = [ 1, 2, 3 ]; // dynamic array of 3 ints
-
- void dibb(int *array)
- {
- array[2]; // means same thing as *(array + 2)
- *(array + 2); // get 2nd element
- }
-
- void diss(int[] array)
- {
- array[2]; // ok
- *(array + 2); // error, array is not a pointer
- }
-
- void ditt(int[3] array)
- {
- array[2]; // ok
- *(array + 2); // error, array is not a pointer
- }
-
-
-Rectangular Arrays- - Experienced FORTRAN numerics programmers know that multidimensional - "rectangular" arrays for things like matrix operations are much faster than trying to - access them via pointers to pointers resulting from "array of pointers to array" semantics. - For example, the D syntax: - -- double[][] matrix; -- - declares matrix as an array of pointers to arrays. (Dynamic arrays are implemented as - pointers to the array data.) Since the arrays can have varying sizes (being dynamically - sized), this is sometimes called "jagged" arrays. Even worse for optimizing the code, the - array rows can sometimes point to each other! Fortunately, D static arrays, while using - the same syntax, are implemented as a fixed rectangular layout: - - - double[3][3] matrix; -- - declares a rectangular matrix with 3 rows and 3 columns, all contiguously in memory. In - other languages, this would be called a multidimensional array and be declared as: - - - double matrix[3,3]; -- - Array Length- - Within the [ ] of a static or a dynamic array, - the variable length - is implicitly declared and set to the length of the array. - -- int[4] foo; - int[] bar = foo; - int* p = &foo[0]; - - // These expressions are equivalent: - bar[] - bar[0 .. 4] - bar[0 .. length] - bar[0 .. bar.length] - - p[0 .. length] // 'length' is not defined, since p is not an array - bar[0]+length // 'length' is not defined, out of scope of [ ] - - bar[length-1] // retrieves last element of the array -- - Array Properties- - Static array properties are: -
- - Dynamic array properties are: - -
- - Examples: - - p.length error, length not known for pointer - s.length compile time constant 3 - a.length runtime value - - p.dup error, length not known - s.dup creates an array of 3 elements, copies - elements s into it - a.dup creates an array of a.length elements, copies - elements of a into it -- - Setting Dynamic Array Length- - The .length property of a dynamic array can be set - as the lvalue of an = operator: -- array.length = 7; -- - This causes the array to be reallocated in place, and the existing - contents copied over to the new array. If the new array length is - shorter, - only enough are copied to fill the new array. If the new array length - is longer, the remainder is filled out with the default initializer. - - - To maximize efficiency, the runtime always tries to resize the - array in place to avoid extra copying. It will always do a copy - if the new size is larger and the array was not allocated via the - new operator or a previous - resize operation. - - - This means that if there is an array slice immediately following the - array being resized, the resized array could overlap the slice; i.e.: - - char[] a = new char[20]; - char[] b = a[0..10]; - char[] c = a[10..20]; - - b.length = 15; // always resized in place because it is sliced - // from a[] which has enough memory for 15 chars - b[11] = 'x'; // a[15] and c[5] are also affected - - a.length = 1; - a.length = 20; // no net change to memory layout - - c.length = 12; // always does a copy because c[] is not at the - // start of a gc allocation block - c[5] = 'y'; // does not affect contents of a[] or b[] - - a.length = 25; // may or may not do a copy - a[3] = 'z'; // may or may not affect b[3] which still overlaps - // the old a[3] -- - To guarantee copying behavior, use the .dup property to ensure - a unique array that can be resized. - - - These issues also apply to concatenating arrays with the ~ and ~= - operators. - - - Resizing a dynamic array is a relatively expensive operation. - So, while the following method of filling an array: -
-
- int[] array;
- while (1)
- { c = getinput();
- if (!c)
- break;
- array.length = array.length + 1;
- array[array.length - 1] = c;
- }
-
-
- will work, it will be inefficient. A more practical
- approach would be to minimize the number of resizes:
-
-
- int[] array;
- array.length = 100; // guess
- for (i = 0; 1; i++)
- { c = getinput();
- if (!c)
- break;
- if (i == array.length)
- array.length = array.length * 2;
- array[i] = c;
- }
- array.length = i;
-
-
- Picking a good initial guess is an art, but you usually can
- pick a value covering 99% of the cases.
- For example, when gathering user
- input from the console - it's unlikely to be longer than 80.
-
-Array Bounds Checking- - It is an error to index an array with an index that is less than - 0 or greater than or equal to the array length. If an index is - out of bounds, an ArrayBoundsError exception is raised if detected - at runtime, and an error if detected at compile time. - A program may not rely on array bounds checking happening, for - example, the following program is incorrect: - -
- try
- {
- for (i = 0; ; i++)
- {
- array[i] = 5;
- }
- }
- catch (ArrayBoundsError)
- {
- // terminate loop
- }
-
-
- The loop is correctly written:
-
-
- for (i = 0; i < array.length; i++)
- {
- array[i] = 5;
- }
-
-
- Implementation Note: Compilers should attempt to detect
- array bounds errors at compile time, for example:
-
- - int[3] foo; - int x = foo[3]; // error, out of bounds -- - Insertion of array bounds checking code at runtime should be - turned on and off - with a compile time switch. - - Array Initialization- -Default Initialization- -
Void Initialization- - Void initialization happens when the Initializer for - an array is void. What it means is that no initialization - is done, i.e. the contents of the array will be undefined. - This is most useful as an efficiency optimization. - Void initializations are an advanced technique and should only be used - when profiling indicates that it matters. - -Static Initialization of Static Arrays- -- int[3] a = [ 1:2, 3 ]; // a[0] = 0, a[1] = 2, a[2] = 3 -- - This is most handy when the array indices are given by enums: - -
- enum Color { red, blue, green };
-
- int value[Color.max + 1] = [ blue:6, green:2, red:5 ];
-
-
- If any members of an array are initialized, they all must be.
- This is to catch
- common errors where another element is added to an enum,
- but one of the static instances
- of arrays of that enum was overlooked in updating the
- initializer list.
-
-
-Special Array Types- -Arrays of Bits- - Bit vectors can be constructed: - -- bit[10] x; // array of 10 bits -- - The amount of storage used up is implementation dependent. - Implementation Note: on - Intel CPUs it would be rounded up to the next 32 bit size. - - - x.length // 10, number of bits - x.size // 4, bytes of storage -- - So, the size per element is not (x.size / x.length). - - - - Strings- - Languages should be good at handling strings. C and C++ are not - good at it. The primary difficulties are memory management, - handling of temporaries, constantly rescanning the string looking - for the terminating 0, and the fixed arrays. -- - Dynamic arrays in D suggest the obvious solution - a string is - just a dynamic array of characters. String literals become just - an easy way to write character arrays. - - - char[] str; - char[] str1 = "abc"; -- - char[] strings are in UTF-8 format. - wchar[] strings are in UTF-16 format. - dchar[] strings are in UTF-32 format. - - - Strings can be copied, compared, concatenated, and appended: - - - str1 = str2; - if (str1 < str3) ... - func(str3 ~ str4); - str4 ~= str1; -- - with the obvious semantics. Any generated temporaries get cleaned up - by the garbage collector (or by using alloca()). Not only that, - this works with any - array not just a special String array. - - - A pointer to a char can be generated: - - - char *p = &str[3]; // pointer to 4th element - char *p = str; // pointer to 1st element -- - Since strings, however, are not 0 terminated in D, - when transferring a pointer - to a string to C, add a terminating 0: - - - str ~= "\0"; -- - The type of a string is determined by the semantic phase of - compilation. The type is - one of: char[], wchar[], dchar[], and is determined by - implicit conversion rules. - If there are two equally applicable implicit conversions, - the result is an error. To - disambiguate these cases, a cast is appropriate: - - - cast(wchar [])"abc" // this is an array of wchar characters -- - String literals are implicitly converted between chars, - wchars, and dchars as necessary. - - - Strings a single character in length can also be exactly - converted to a char, wchar or dchar constant: - - - char c; - wchar w; - dchar d; - - c = 'b'; // c is assigned the character 'b' - w = 'b'; // w is assigned the wchar character 'b' - w = 'bc'; // error - only one wchar character at a time - w = "b"[0]; // w is assigned the wchar character 'b' - w = \r; // w is assigned the carriage return wchar character - d = 'd'; // d is assigned the character 'd' -- - printf() and Strings- - printf() is a C function and is not part of D. printf() - will print C strings, which are 0 terminated. There are two ways - to use printf() with D strings. The first is to add a - terminating 0, and cast the result to a char*: - -
- str ~= "\0";
- printf("the string is '%s'\n", (char *)str);
-
-
- The second way is to use the precision specifier. The way D arrays
- are laid out, the length comes first, so the following works:
-
-
- printf("the string is '%.*s'\n", str);
-
-
- In the future, it may be necessary to just add a new format
- specifier to printf() instead of relying on an implementation
- dependent detail.
-
-Implicit Conversions- - A pointer T* can be implicitly converted to - one of the following: - -
- Associative Arrays- - D goes one step further with arrays - adding associative arrays. - Associative arrays have an index that is not necessarily an integer, - and can be sparsely populated. The index for an associative array - is called the key, and its type is called the KeyType. -- - Associative arrays are declared by placing the KeyType - within the [] of an array declaration: - - - int[char[]] b; // associative array b of ints that are - // indexed by an array of characters. - // The KeyType is char[] - b["hello"] = 3; // set value associated with key "hello" to 3 - func(b["hello"]); // pass 3 as parameter to func() -- - Particular keys in an associative array can be removed with the - remove function: - -
- b.remove("hello");
-
-
- The InExpression yields a pointer to the value
- if the key is in the associative array, or null if not:
-
-
- int* p;
- p = ("hello" in b);
- if (p != null)
- ...
-
-
- KeyTypes cannot be functions or voids.
- - - If the KeyType is a struct type, a default mechanism is used - to compute the hash and comparisons of it based on the binary - data within the struct value. A custom mechanism can be used - by providing the following functions as struct members: - - - uint toHash(); - int opCmp(KeyType* s); -- - For example: - -
- import std.string;
-
- struct MyString
- {
- char[] str;
-
- uint toHash()
- { uint hash;
- foreach (char c; s)
- hash = (hash * 9) + c;
- return hash;
- }
-
- int opCmp(MyString* s)
- {
- return std.string.cmp(this.str, s.str);
- }
- }
-
-
-Properties- -Properties for associative arrays are: - -
- Associative Array Example: word count- -
- import std.file; // D file I/O
-
- int main (char[][] args)
- {
- int word_total;
- int line_total;
- int char_total;
- int[char[]] dictionary;
-
- printf(" lines words bytes file\n");
- for (int i = 1; i < args.length; ++i) // program arguments
- {
- char[] input; // input buffer
- int w_cnt, l_cnt, c_cnt; // word, line, char counts
- int inword;
- int wstart;
-
- input = std.file.read(args[i]); // read file into input[]
-
- foreach (char c; input)
- {
- if (c == '\n')
- ++l_cnt;
- if (c >= '0' && c <= '9')
- {
- }
- else if (c >= 'a' && c <= 'z' ||
- c >= 'A' && c <= 'Z')
- {
- if (!inword)
- {
- wstart = j;
- inword = 1;
- ++w_cnt;
- }
- }
- else if (inword)
- { char[] word = input[wstart .. j];
-
- dictionary[word]++; // increment count for word
- inword = 0;
- }
- ++c_cnt;
- }
- if (inword)
- { char[] word = input[wstart .. input.length];
- dictionary[word]++;
- }
- printf("%8ld%8ld%8ld %.*s\n", l_cnt, w_cnt, c_cnt, args[i]);
- line_total += l_cnt;
- word_total += w_cnt;
- char_total += c_cnt;
- }
-
- if (args.length > 2)
- {
- printf("-------------------------------------\n%8ld%8ld%8ld total",
- line_total, word_total, char_total);
- }
-
- printf("-------------------------------------\n");
- char[][] keys = dictionary.keys; // find all words in dictionary[]
- for (int i = 0; i < keys.length; i++)
- { char[] word;
-
- word = keys[i];
- printf("%3d %.*s\n", dictionary[word], word);
- }
- return 0;
- }
-
-
- |
Add feedback and comments regarding this - page.
- -|
+
+
+
+
+ + +· Lexical + + +· Modules + + +· Declarations + + +· Types + + +· Properties + + +· Attributes + + +· Pragmas + + +· Expressions + + +· Statements + + +· Arrays + + +· Structs & Unions + + +· Classes + + +· Interfaces + + +· Enums + + +· Functions + + +· Operator Overloading + + +· Templates + + +· Mixins + + +· Contracts + + +· Conditional Compilation + + +· Handling errors + + +· Garbage Collection + + +· Memory Management + + +· Floating Point + + +· Inline Assembler + + +· Documentation Comments + + +· Interfacing To C + + +· Portability Guide + + +· Embedding D in HTML + + +· Named Character Entities + + +· Application Binary Interface + + + + |
+
+
+
+Arrays+ + There are four kinds of arrays: ++ +
Pointers+ +int* p;
+
+
+ These are simple pointers to data, analogous to C pointers.
+ Pointers are provided for interfacing with C and for
+ specialized systems work.
+ There
+ is no length associated with it, and so there is no way for the
+ compiler or runtime to do bounds checking, etc., on it.
+ Most conventional uses for pointers can be replaced with
+ dynamic arrays, out and inout parameters,
+ and reference types.
+
+Static Arrays+ +int[3] s;
+
+
+ These are analogous to C arrays. Static arrays are distinguished
+ by having a length fixed at compile time.
+ + + The total size of a static array cannot exceed 16Mb. + A dynamic array should be used instead for such large arrays. + + Dynamic Arrays+ +int[] a;
+
+
+ Dynamic arrays consist of a length and a pointer to the array data.
+ Multiple dynamic arrays can share all or parts of the array data.
+
+Array Declarations+ + There are two ways to declare arrays, prefix and postfix. + The prefix form is the preferred method, especially for + non-trivial types. + +Prefix Array Declarations+ + Prefix declarations appear before the identifier being + declared and read right to left, so: + +int[] a; // dynamic array of ints +int[4][3] b; // array of 3 arrays of 4 ints each +int[][5] c; // array of 5 dynamic arrays of ints. +int*[]*[3] d; // array of 3 pointers to dynamic arrays of pointers to ints +int[]* e; // pointer to dynamic array of ints ++ + + Postfix Array Declarations+ + Postfix declarations appear after the identifier being + declared and read left to right. + Each group lists equivalent declarations: + +// dynamic array of ints +int[] a; +int a[]; + +// array of 3 arrays of 4 ints each +int[4][3] b; +int[4] b[3]; +int b[3][4]; + +// array of 5 dynamic arrays of ints. +int[][5] c; +int[] c[5]; +int c[5][]; + +// array of 3 pointers to dynamic arrays of pointers to ints +int*[]*[3] d; +int*[]* d[3]; +int* (*d[3])[]; + +// pointer to dynamic array of ints +int[]* e; +int (*e[]); ++ + Rationale: The postfix form matches the way arrays are + declared in C and C++, and supporting this form provides an + easy migration path for programmers used to it. + + Usage+ + There are two broad kinds of operations to do on an array - affecting + the handle to the array, + and affecting the contents of the array. + C only has + operators to affect the handle. In D, both are accessible. ++ + The handle to an array is specified by naming the array, as + in p, s or a: + + int* p; +int[3] s; +int[] a; + +int* q; +int[3] t; +int[] b; + +p = q; // p points to the same thing q does. +p = s; // p points to the first element of the array s. +p = a; // p points to the first element of the array a. + +s = ...; // error, since s is a compiled in static + // reference to an array. + +a = p; // error, since the length of the array pointed + // to by p is unknown +a = s; // a is initialized to point to the s array +a = b; // a points to the same array as b does ++ + Slicing+ + Slicing an array means to specify a subarray of it. + An array slice does not copy the data, it is only another + reference to it. + For example: + +int[10] a; // declare array of 10 ints +int[] b; + +b = a[1..3]; // a[1..3] is a 2 element array consisting of + // a[1] and a[2] +foo(b[1]); // equivalent to foo(0) +a[2] = 3; +foo(b[1]); // equivalent to foo(3) ++ + The [] is shorthand for a slice of the entire array. + For example, the assignments to b: + + int[10] a; +int[] b; + +b = a; +b = a[]; +b = a[0 .. a.length]; ++ + are all semantically equivalent. + + + Slicing + is not only handy for referring to parts of other arrays, + but for converting pointers into bounds-checked arrays: + + int* p; +int[] b = p[0..8]; ++ + Slicing for bit arrays is only allowed if the slice's lower + bound falls on a byte boundary: + + bit[] b; +... +b[0..8]; // ok +b[8..16]; // ok +b[8..17]; // ok +b[1..16]; // error, lower bound is not on a byte boundary ++ + Misaligned bit array slices will cause an ArrayBoundsError exception + to be thrown at runtime. + + Array Copying+ + When the slice operator appears as the lvalue of an assignment + expression, it means that the contents of the array are the + target of the assignment rather than a reference to the array. + Array copying happens when the lvalue is a slice, and the rvalue + is an array of or pointer to the same type. + +int[3] s; +int[3] t; + +s[] = t; // the 3 elements of t[3] are copied into s[3] +s[] = t[]; // the 3 elements of t[3] are copied into s[3] +s[1..2] = t[0..1]; // same as s[1] = t[0] +s[0..2] = t[1..3]; // same as s[0] = t[1], s[1] = t[2] +s[0..4] = t[0..4]; // error, only 3 elements in s +s[0..2] = t; // error, different lengths for lvalue and rvalue ++ + Overlapping copies are an error: + + s[0..2] = s[1..3]; // error, overlapping copy +s[1..3] = s[0..2]; // error, overlapping copy ++ + Disallowing overlapping makes it possible for more aggressive + parallel code optimizations than possible with the serial + semantics of C. + + Array Setting+ + If a slice operator appears as the lvalue of an assignment + expression, and the type of the rvalue is the same as the element + type of the lvalue, then the lvalue's array contents + are set to the rvalue. + +int[3] s; +int* p; + +s[] = 3; // same as s[0] = 3, s[1] = 3, s[2] = 3 +p[0..2] = 3; // same as p[0] = 3, p[1] = 3 ++ + Array Concatenation+ + The binary operator ~ is the cat operator. It is used + to concatenate arrays: + +int[] a; +int[] b; +int[] c; + +a = b ~ c; // Create an array from the concatenation of the + // b and c arrays ++ + Many languages overload the + operator to mean concatenation. + This confusingly leads to, does: + + "10" + 3
+
+
+ produce the number 13 or the string "103" as the result? It isn't
+ obvious, and the language designers wind up carefully writing rules
+ to disambiguate it - rules that get incorrectly implemented,
+ overlooked, forgotten, and ignored. It's much better to have + mean
+ addition, and a separate operator to be array concatenation.
+ + + Similarly, the ~= operator means append, as in: + + a ~= b; // a becomes the concatenation of a and b
+
+
+ Concatenation always creates a copy of its operands, even
+ if one of the operands is a 0 length array, so:
+
+a = b; // a refers to b +a = b ~ c[0..0]; // a refers to a copy of b ++ + + + Array Operations+ + Note: Array operations are not implemented. ++ + In general, (a[n..m] op e) is defined as: + + for (i = n; i < m; i++)
+ a[i] op e;
+
+
+ So, for the expression:
+
+a[] = b[] + 3; ++ + the result is equivalent to: + + for (i = 0; i < a.length; i++)
+ a[i] = b[i] + 3;
+
+
+ When more than one [] operator appears in an expression, the range
+ represented by all must match.
+
+a[1..3] = b[] + 3; // error, 2 elements not same as 3 elements
+
+
+Examples:+ +int[3] abc; // static array of 3 ints +int[] def = [ 1, 2, 3 ]; // dynamic array of 3 ints + +void dibb(int *array) +{ + array[2]; // means same thing as *(array + 2) + *(array + 2); // get 2nd element +} + +void diss(int[] array) +{ + array[2]; // ok + *(array + 2); // error, array is not a pointer +} + +void ditt(int[3] array) +{ + array[2]; // ok + *(array + 2); // error, array is not a pointer +} ++ + Rectangular Arrays+ + Experienced FORTRAN numerics programmers know that multidimensional + "rectangular" arrays for things like matrix operations are much faster than trying to + access them via pointers to pointers resulting from "array of pointers to array" semantics. + For example, the D syntax: + +double[][] matrix;
+
+
+ declares matrix as an array of pointers to arrays. (Dynamic arrays are implemented as
+ pointers to the array data.) Since the arrays can have varying sizes (being dynamically
+ sized), this is sometimes called "jagged" arrays. Even worse for optimizing the code, the
+ array rows can sometimes point to each other! Fortunately, D static arrays, while using
+ the same syntax, are implemented as a fixed rectangular layout:
+
+double[3][3] matrix;
+
+
+ declares a rectangular matrix with 3 rows and 3 columns, all contiguously in memory. In
+ other languages, this would be called a multidimensional array and be declared as:
+
+double matrix[3,3];
+
+
+Array Length+ + Within the [ ] of a static or a dynamic array, + the variable length + is implicitly declared and set to the length of the array. + +int[4] foo; +int[] bar = foo; +int* p = &foo[0]; + +// These expressions are equivalent: +bar[] +bar[0 .. 4] +bar[0 .. length] +bar[0 .. bar.length] + +p[0 .. length] // 'length' is not defined, since p is not an array +bar[0]+length // 'length' is not defined, out of scope of [ ] + +bar[length-1] // retrieves last element of the array ++ + Array Properties+ + Static array properties are: +
+ + Dynamic array properties are: + +
+ + Examples: + + p.length // error, length not known for pointer +s.length // compile time constant 3 +a.length // runtime value + +p.dup // error, length not known +s.dup // creates an array of 3 elements, copies + // elements s into it +a.dup // creates an array of a.length elements, copies + // elements of a into it ++ + Setting Dynamic Array Length+ + The .length property of a dynamic array can be set + as the lvalue of an = operator: + +array.length = 7; ++ + This causes the array to be reallocated in place, and the existing + contents copied over to the new array. If the new array length is + shorter, + only enough are copied to fill the new array. If the new array length + is longer, the remainder is filled out with the default initializer. + + + To maximize efficiency, the runtime always tries to resize the + array in place to avoid extra copying. It will always do a copy + if the new size is larger and the array was not allocated via the + new operator or a previous + resize operation. + + + This means that if there is an array slice immediately following the + array being resized, the resized array could overlap the slice; i.e.: + + char[] a = new char[20]; +char[] b = a[0..10]; +char[] c = a[10..20]; + +b.length = 15; // always resized in place because it is sliced + // from a[] which has enough memory for 15 chars +b[11] = 'x'; // a[15] and c[5] are also affected + +a.length = 1; +a.length = 20; // no net change to memory layout + +c.length = 12; // always does a copy because c[] is not at the + // start of a gc allocation block +c[5] = 'y'; // does not affect contents of a[] or b[] + +a.length = 25; // may or may not do a copy +a[3] = 'z'; // may or may not affect b[3] which still overlaps + // the old a[3] ++ + To guarantee copying behavior, use the .dup property to ensure + a unique array that can be resized. + + + These issues also apply to concatenating arrays with the ~ and ~= + operators. + + + Resizing a dynamic array is a relatively expensive operation. + So, while the following method of filling an array: + + int[] array; +while (1) +{ c = getinput(); + if (!c) + break; + array.length = array.length + 1; + array[array.length - 1] = c; +} ++ + will work, it will be inefficient. A more practical + approach would be to minimize the number of resizes: + + int[] array; +array.length = 100; // guess +for (i = 0; 1; i++) +{ c = getinput(); + if (!c) + break; + if (i == array.length) + array.length = array.length * 2; + array[i] = c; +} +array.length = i; ++ + Picking a good initial guess is an art, but you usually can + pick a value covering 99% of the cases. + For example, when gathering user + input from the console - it's unlikely to be longer than 80. + + Array Bounds Checking+ + It is an error to index an array with an index that is less than + 0 or greater than or equal to the array length. If an index is + out of bounds, an ArrayBoundsError exception is raised if detected + at runtime, and an error if detected at compile time. + A program may not rely on array bounds checking happening, for + example, the following program is incorrect: + +try +{ + for (i = 0; ; i++) + { + array[i] = 5; + } +} +catch (ArrayBoundsError) +{ + // terminate loop +} ++ + The loop is correctly written: + + for (i = 0; i < array.length; i++)
+{
+ array[i] = 5;
+}
+
+
+ Implementation Note: Compilers should attempt to detect
+ array bounds errors at compile time, for example:
+
+int[3] foo; +int x = foo[3]; // error, out of bounds ++ + Insertion of array bounds checking code at runtime should be + turned on and off + with a compile time switch. + + Array Initialization+ +Default Initialization+ +
Void Initialization+ + Void initialization happens when the Initializer for + an array is void. What it means is that no initialization + is done, i.e. the contents of the array will be undefined. + This is most useful as an efficiency optimization. + Void initializations are an advanced technique and should only be used + when profiling indicates that it matters. + +Static Initialization of Static Arrays+ +int[3] a = [ 1:2, 3 ]; // a[0] = 0, a[1] = 2, a[2] = 3 ++ + This is most handy when the array indices are given by enums: + + enum Color { red, blue, green }; + +int value[Color.max + 1] = [ blue:6, green:2, red:5 ]; ++ + If any members of an array are initialized, they all must be. + This is to catch + common errors where another element is added to an enum, + but one of the static instances + of arrays of that enum was overlooked in updating the + initializer list. + + + Special Array Types+ +Arrays of Bits+ + Bit vectors can be constructed: + +bit[10] x; // array of 10 bits ++ + The amount of storage used up is implementation dependent. + Implementation Note: on + Intel CPUs it would be rounded up to the next 32 bit size. + + x.length // 10, number of bits +x.size // 4, bytes of storage ++ + So, the size per element is not (x.size / x.length). + + + + Strings+ + Languages should be good at handling strings. C and C++ are not + good at it. The primary difficulties are memory management, + handling of temporaries, constantly rescanning the string looking + for the terminating 0, and the fixed arrays. ++ + Dynamic arrays in D suggest the obvious solution - a string is + just a dynamic array of characters. String literals become just + an easy way to write character arrays. + + char[] str; +char[] str1 = "abc"; ++ + char[] strings are in UTF-8 format. + wchar[] strings are in UTF-16 format. + dchar[] strings are in UTF-32 format. + + + Strings can be copied, compared, concatenated, and appended: + + str1 = str2;
+if (str1 < str3) ...
+func(str3 ~ str4);
+str4 ~= str1;
+
+
+ with the obvious semantics. Any generated temporaries get cleaned up
+ by the garbage collector (or by using alloca()). Not only that,
+ this works with any
+ array not just a special String array.
+ + + A pointer to a char can be generated: + + char *p = &str[3]; // pointer to 4th element +char *p = str; // pointer to 1st element ++ + Since strings, however, are not 0 terminated in D, + when transferring a pointer + to a string to C, add a terminating 0: + + str ~= "\0";
+
+
+ The type of a string is determined by the semantic phase of
+ compilation. The type is
+ one of: char[], wchar[], dchar[], and is determined by
+ implicit conversion rules.
+ If there are two equally applicable implicit conversions,
+ the result is an error. To
+ disambiguate these cases, a cast is appropriate:
+
+cast(wchar [])"abc" // this is an array of wchar characters ++ + String literals are implicitly converted between chars, + wchars, and dchars as necessary. + + + Strings a single character in length can also be exactly + converted to a char, wchar or dchar constant: + + char c; +wchar w; +dchar d; + +c = 'b'; // c is assigned the character 'b' +w = 'b'; // w is assigned the wchar character 'b' +w = 'bc'; // error - only one wchar character at a time +w = "b"[0]; // w is assigned the wchar character 'b' +w = \r; // w is assigned the carriage return wchar character +d = 'd'; // d is assigned the character 'd' ++ + printf() and Strings+ + printf() is a C function and is not part of D. printf() + will print C strings, which are 0 terminated. There are two ways + to use printf() with D strings. The first is to add a + terminating 0, and cast the result to a char*: + +str ~= "\0"; +printf("the string is '%s'\n", (char *)str); ++ + The second way is to use the precision specifier. The way D arrays + are laid out, the length comes first, so the following works: + + printf("the string is '%.*s'\n", str);
+
+
+ In the future, it may be necessary to just add a new format
+ specifier to printf() instead of relying on an implementation
+ dependent detail.
+
+Implicit Conversions+ + A pointer T* can be implicitly converted to + one of the following: + +
+ Associative Arrays+ + D goes one step further with arrays - adding associative arrays. + Associative arrays have an index that is not necessarily an integer, + and can be sparsely populated. The index for an associative array + is called the key, and its type is called the KeyType. ++ + Associative arrays are declared by placing the KeyType + within the [] of an array declaration: + + int[char[]] b; // associative array b of ints that are + // indexed by an array of characters. + // The KeyType is char[] +b["hello"] = 3; // set value associated with key "hello" to 3 +func(b["hello"]); // pass 3 as parameter to func() ++ + Particular keys in an associative array can be removed with the + remove function: + + b.remove("hello");
+
+
+ The InExpression yields a pointer to the value
+ if the key is in the associative array, or null if not:
+
+int* p; +p = ("hello" in b); +if (p != null) + ... ++ + KeyTypes cannot be functions or voids. + + + If the KeyType is a struct type, a default mechanism is used + to compute the hash and comparisons of it based on the binary + data within the struct value. A custom mechanism can be used + by providing the following functions as struct members: + + uint toHash(); +int opCmp(KeyType* s); ++ + For example: + + import std.string; + +struct MyString +{ + char[] str; + + uint toHash() + { uint hash; + foreach (char c; s) + hash = (hash * 9) + c; + return hash; + } + + int opCmp(MyString* s) + { + return std.string.cmp(this.str, s.str); + } +} ++ + Properties+ +Properties for associative arrays are: + +
+ Associative Array Example: word count+ +import std.file; // D file I/O + +int main (char[][] args) +{ + int word_total; + int line_total; + int char_total; + int[char[]] dictionary; + + printf(" lines words bytes file\n"); + for (int i = 1; i < args.length; ++i) // program arguments + { + char[] input; // input buffer + int w_cnt, l_cnt, c_cnt; // word, line, char counts + int inword; + int wstart; + + input = std.file.read(args[i]); // read file into input[] + + foreach (char c; input) + { + if (c == '\n') + ++l_cnt; + if (c >= '0' && c <= '9') + { + } + else if (c >= 'a' && c <= 'z' || + c >= 'A' && c <= 'Z') + { + if (!inword) + { + wstart = j; + inword = 1; + ++w_cnt; + } + } + else if (inword) + { char[] word = input[wstart .. j]; + + dictionary[word]++; // increment count for word + inword = 0; + } + ++c_cnt; + } + if (inword) + { char[] word = input[wstart .. input.length]; + dictionary[word]++; + } + printf("%8ld%8ld%8ld %.*s\n", l_cnt, w_cnt, c_cnt, args[i]); + line_total += l_cnt; + word_total += w_cnt; + char_total += c_cnt; + } + + if (args.length > 2) + { + printf("-------------------------------------\n%8ld%8ld%8ld total", + line_total, word_total, char_total); + } + + printf("-------------------------------------\n"); + char[][] keys = dictionary.keys; // find all words in dictionary[] + for (int i = 0; i < keys.length; i++) + { char[] word; + + word = keys[i]; + printf("%3d %.*s\n", dictionary[word], word); + } + return 0; +} ++ + + |
-
-Home
-| Search
-| D
-| Comments
-
-|
-
- - -· Lexical -· Modules -· Declarations -· Types -· Properties -· Attributes -· Pragmas -· Expressions -· Statements -· Arrays -· Structs & Unions -· Classes -· Interfaces -· Enums -· Functions -· Operator Overloading -· Templates -· Mixins -· Contracts -· Conditional Compilation -· Handling errors -· Garbage Collection -· Memory Management -· Floating Point -· Inline Assembler -· Documentation Comments -· Interfacing To C -· Portability Guide -· Embedding D in HTML -· Named Character Entities -· Application Binary Interface - - |
-
-
-
-Attributes- -- AttributeSpecifier: - Attribute : - Attribute DeclDefBlock - Pragma ; - - AttributeElseSpecifier: - AttributeElse : - AttributeElse DeclDefBlock - AttributeElse DeclDefBlock else DeclDefBlock - - Attribute: - LinkageAttribute - AlignAttribute - Pragma - deprecated - private - package - protected - public - export - static - final - override - abstract - const - auto - - AttributeElse: - DebugAttribute - VersionAttribute - - DeclDefBlock - DeclDef - { } - { DeclDefs } -- - Attributes are a way to modify one or more declarations. - The general forms are: - -
- attribute declaration; affects the declaration
-
- attribute: affects all declarations until the next }
- declaration;
- declaration;
- ...
-
- attribute affects all declarations in the block
- {
- declaration;
- declaration;
- ...
- }
-
-
- For attributes with an optional else clause:
-
-
- attribute
- declaration;
- else
- declaration;
-
- attribute affects all declarations in the block
- {
- declaration;
- declaration;
- ...
- }
- else
- {
- declaration;
- declaration;
- ...
- }
-
-
-Linkage Attribute- -- LinkageAttribute: - extern - extern ( LinkageType ) - - LinkageType: - C - C++ - D - Windows - Pascal -- - D provides an easy way to call C functions and operating - system API functions, as compatibility with both is essential. - The LinkageType is case sensitive, and is meant to be - extensible by the implementation (they are not keywords). - C and D must be supplied, the others are what - makes sense for the implementation. - C++ is reserved for future use. - Implementation Note: - for Win32 platforms, Windows and Pascal should exist. - - - C function calling conventions are - specified by: - - - extern (C): - int foo(); call foo() with C conventions -- - D conventions are: - - - extern (D): -- - or: - - - extern: -- - - Windows API conventions are: - - - extern (Windows): - void *VirtualAlloc( - void *lpAddress, - uint dwSize, - uint flAllocationType, - uint flProtect - ); -- - Align Attribute- -- AlignAttribute: - align - align ( Integer ) -- - Specifies the alignment of struct members. align by itself - sets it to the default, which matches the default member alignment - of the companion C compiler. Integer specifies the alignment - which matches the behavior of the companion C compiler when non-default - alignments are used. - - - Matching the behavior of the companion C compiler can have some - surprising results, such as the following for Digital Mars C++: - -
- struct S
- { align(4) byte a; // placed at offset 0
- align(4) byte b; // placed at offset 1
- }
-
- AlignAttribute is meant for C ABI compatiblity, which is not
- the same thing as binary compatibility across diverse platforms.
- For that, use packed structs:
-
-
- align (1) struct S
- { byte a; // placed at offset 0
- byte[3] filler1;
- byte b; // placed at offset 4
- byte[3] filler2;
- }
-
- A value of 1 means that no alignment is done;
- members are packed together.
- - - Do not align references or pointers that were allocated - using NewExpression on boundaries that are not - a multipe of 4. The garbage collector assumes that pointers - and references to gc allocated objects will be on 4 - byte boundaries. If they are not, undefined behavior will - result. - - - AlignAttribute is ignored when applied to declarations - that are not structs or struct members. - - Deprecated Attribute- - It is often necessary to deprecate a feature in a library, - yet retain it for backwards compatibility. Such - declarations can be marked as deprecated, which means - that the compiler can be set to produce an error - if any code refers to deprecated - declarations: - -
- deprecated
- {
- void oldFoo();
- }
-
-
- Implementation Note: The compiler should have a switch
- specifying if deprecated declarations should be compiled with
- out complaint or not.
-
-
-Protection Attribute- - Protection is an attribute that is one of - private, package, protected, - public or export. -- - Private means that only members of the enclosing class can access - the member, or members and functions in the same module as the - enclosing class. - Private members cannot be overridden. - Private module members are equivalent to static declarations - in C programs. - - - Package extends private so that package members can be accessed - from code in other modules that are in the same package. - This applies to the innermost package only, if a module is in - nested packages. - - - Protected means that only members of the enclosing class or any - classes derived - from that class, or members and functions in the same module - as the enclosing class, can access the member. - Protected module members are illegal. - - - Public means that any code within the executable can access the member. - - - Export means that any code outside the executable can access the - member. Export - is analogous to exporting definitions from a DLL. - - Const Attribute- -- const -- - The const attribute declares constants that can be - evaluated at compile time. For example: - -
- const int foo = 7;
-
- const
- {
- double bar = foo + 6;
- }
-
-
- A const declaration without an initializer must be initialized
- in a constructor (for class fields) or in a static constructor
- (for static class members, or module variable declarations).
-
-
- const int x;
- const int y;
-
- static this()
- {
- x = 3; // ok
- // error: y not initialized
- }
-
- void foo()
- {
- x = 4; // error, x is const and not in static constructor
- }
-
- class C
- {
- const int a;
- const int b;
- static const int c;
- static const int d;
-
- this()
- { a = 3; // ok
- a = 4; // ok, multiple initialization allowed
- C p = this;
- p.a = 4; // error, only members of this instance
- c = 5; // error, should initialize in static constructor
- // error, b is not initialized
- }
-
- this(int x);
- {
- this(); // ok, forwarding constructor
- }
-
- static this()
- {
- c = 3; // ok
- // error, d is not initialized
- }
- }
-
-
- It is not an error to have const module variable declarations without
- initializers if there is no constructor. This is to support the practice
- of having modules serve only as declarations that are not linked in,
- the implementation of it will be in another module that is linked in.
-
-
-Override Attribute- -- override -- - The override attribute applies to virtual functions. - It means that the function must override a function with the - same name and parameters in a base class. The override attribute - is useful for catching errors when a base class's member function - gets its parameters changed, and all derived classes need to have - their overriding functions updated. - -
- class Foo
- {
- int bar();
- int abc(int x);
- }
-
- class Foo2 : Foo
- {
- override
- {
- int bar(char c); // error, no bar(char) in Foo
- int abc(int x); // ok
- }
- }
-
-
-Static Attribute- -- static -- - The static attribute applies to functions and data. - It means that the declaration does not apply to a particular - instance of an object, but to the type of the object. In - other words, it means there is no this reference. - static is ignored when applied to other declarations. - -
- class Foo
- {
- static int bar() { return 6; }
- int foobar() { return 7; }
- }
-
- ...
-
- Foo f = new Foo;
- Foo.bar(); // produces 6
- Foo.foobar(); // error, no instance of Foo
- f.bar(); // produces 6;
- f.foobar(); // produces 7;
-
-
- Static functions are never virtual.
- - - Static data has only one instance for the entire program, - not once per object. - - - Static does not have the additional C meaning of being local - to a file. Use the private attribute in D to achieve that. - For example: - - - module foo; - int x = 3; // x is global - private int y = 4; // y is local to module foo -- - - Auto Attribute- -- auto -- - The auto attribute is used for local variables and for class - declarations. For class declarations, the auto attribute creates - an auto class. - For local declarations, auto implements the RAII (Resource - Acquisition Is Initialization) protocol. This means that the - destructor for an object is automatically called when the auto - reference to it goes out of scope. The destructor is called even - if the scope is exited via a thrown exception, thus auto - is used to guarantee cleanup. - - - If there is more than one auto variable going out of scope - at the same point, then the destructors are called in the reverse - order that the variables were constructed. - - - Auto cannot be applied to globals, statics, data members, inout - or out parameters. Arrays of autos are not allowed, and auto - function return values are not allowed. Assignment to an auto, - other than initialization, is not allowed. - Rationale: These restrictions may get relaxed in the future - if a compelling reason to appears. - - - Abstract Attribute- - If a class is abstract, it cannot be instantiated - directly. It can only be instantiated as a base class of - another, non-abstract, class. -- - Classes become abstract if they are defined within an - abstract attribute, or if any of the virtual member functions - within it are declared as abstract. - - - Non-virtual functions cannot be declared as abstract. - - - Functions declared as abstract can still have function - bodies. This is so that even though they must be overridden, - they can still provide 'base class functionality.' - - - |
Add feedback and comments regarding this - page.
- -|
+
+
+
+
+ + +· Lexical + + +· Modules + + +· Declarations + + +· Types + + +· Properties + + +· Attributes + + +· Pragmas + + +· Expressions + + +· Statements + + +· Arrays + + +· Structs & Unions + + +· Classes + + +· Interfaces + + +· Enums + + +· Functions + + +· Operator Overloading + + +· Templates + + +· Mixins + + +· Contracts + + +· Conditional Compilation + + +· Handling errors + + +· Garbage Collection + + +· Memory Management + + +· Floating Point + + +· Inline Assembler + + +· Documentation Comments + + +· Interfacing To C + + +· Portability Guide + + +· Embedding D in HTML + + +· Named Character Entities + + +· Application Binary Interface + + + + |
+
+
+Attributes+ ++AttributeSpecifier: + Attribute : + Attribute DeclDefBlock + Pragma ; + +AttributeElseSpecifier: + AttributeElse : + AttributeElse DeclDefBlock + AttributeElse DeclDefBlock else DeclDefBlock + +Attribute: + LinkageAttribute + AlignAttribute + Pragma + deprecated + private + package + protected + public + export + static + final + override + abstract + const + auto + +AttributeElse: + DebugAttribute + VersionAttribute + +DeclDefBlock + DeclDef + { } + { DeclDefs } ++ + + Attributes are a way to modify one or more declarations. + The general forms are: + +
+attribute declaration; affects the declaration
+
+attribute: affects all declarations until the next }
+ declaration;
+ declaration;
+ ...
+
+attribute affects all declarations in the block
+{
+ declaration;
+ declaration;
+ ...
+}
+
+
+ For attributes with an optional else clause:
+
+
+attribute
+ declaration;
+else
+ declaration;
+
+attribute affects all declarations in the block
+{
+ declaration;
+ declaration;
+ ...
+}
+else
+{
+ declaration;
+ declaration;
+ ...
+}
+
+
+Linkage Attribute+ ++LinkageAttribute: + extern + extern ( LinkageType ) + +LinkageType: + C + C++ + D + Windows + Pascal ++ + + D provides an easy way to call C functions and operating + system API functions, as compatibility with both is essential. + The LinkageType is case sensitive, and is meant to be + extensible by the implementation (they are not keywords). + C and D must be supplied, the others are what + makes sense for the implementation. + C++ is reserved for future use. + Implementation Note: + for Win32 platforms, Windows and Pascal should exist. + + + C function calling conventions are + specified by: + + extern (C): + int foo(); // call foo() with C conventions ++ + D conventions are: + + extern (D):
+
+
+ or:
+
+extern:
+
+
+
+ Windows API conventions are:
+
+extern (Windows): + void *VirtualAlloc( + void *lpAddress, + uint dwSize, + uint flAllocationType, + uint flProtect + ); ++ + Align Attribute+ ++AlignAttribute: + align + align ( Integer ) ++ + + Specifies the alignment of struct members. align by itself + sets it to the default, which matches the default member alignment + of the companion C compiler. Integer specifies the alignment + which matches the behavior of the companion C compiler when non-default + alignments are used. + + + Matching the behavior of the companion C compiler can have some + surprising results, such as the following for Digital Mars C++: + + struct S +{ align(4) byte a; // placed at offset 0 + align(4) byte b; // placed at offset 1 +} ++ + AlignAttribute is meant for C ABI compatiblity, which is not + the same thing as binary compatibility across diverse platforms. + For that, use packed structs: + + align (1) struct S +{ byte a; // placed at offset 0 + byte[3] filler1; + byte b; // placed at offset 4 + byte[3] filler2; +} ++ + A value of 1 means that no alignment is done; + members are packed together. + + + Do not align references or pointers that were allocated + using NewExpression on boundaries that are not + a multipe of 4. The garbage collector assumes that pointers + and references to gc allocated objects will be on 4 + byte boundaries. If they are not, undefined behavior will + result. + + + AlignAttribute is ignored when applied to declarations + that are not structs or struct members. + + Deprecated Attribute+ + It is often necessary to deprecate a feature in a library, + yet retain it for backwards compatibility. Such + declarations can be marked as deprecated, which means + that the compiler can be set to produce an error + if any code refers to deprecated + declarations: + +deprecated +{ + void oldFoo(); +} ++ + Implementation Note: The compiler should have a switch + specifying if deprecated declarations should be compiled with + out complaint or not. + + + Protection Attribute+ + Protection is an attribute that is one of + private, package, protected, + public or export. ++ + Private means that only members of the enclosing class can access + the member, or members and functions in the same module as the + enclosing class. + Private members cannot be overridden. + Private module members are equivalent to static declarations + in C programs. + + + Package extends private so that package members can be accessed + from code in other modules that are in the same package. + This applies to the innermost package only, if a module is in + nested packages. + + + Protected means that only members of the enclosing class or any + classes derived + from that class, or members and functions in the same module + as the enclosing class, can access the member. + Protected module members are illegal. + + + Public means that any code within the executable can access the member. + + + Export means that any code outside the executable can access the + member. Export + is analogous to exporting definitions from a DLL. + + Const Attribute+ ++const ++ + + The const attribute declares constants that can be + evaluated at compile time. For example: + + const int foo = 7; + +const +{ + double bar = foo + 6; +} ++ + A const declaration without an initializer must be initialized + in a constructor (for class fields) or in a static constructor + (for static class members, or module variable declarations). + + const int x; +const int y; + +static this() +{ + x = 3; // ok + // error: y not initialized +} + +void foo() +{ + x = 4; // error, x is const and not in static constructor +} + +class C +{ + const int a; + const int b; + static const int c; + static const int d; + + this() + { a = 3; // ok + a = 4; // ok, multiple initialization allowed + C p = this; + p.a = 4; // error, only members of this instance + c = 5; // error, should initialize in static constructor + // error, b is not initialized + } + + this(int x); + { + this(); // ok, forwarding constructor + } + + static this() + { + c = 3; // ok + // error, d is not initialized + } +} ++ + It is not an error to have const module variable declarations without + initializers if there is no constructor. This is to support the practice + of having modules serve only as declarations that are not linked in, + the implementation of it will be in another module that is linked in. + + + Override Attribute+ ++override ++ + + The override attribute applies to virtual functions. + It means that the function must override a function with the + same name and parameters in a base class. The override attribute + is useful for catching errors when a base class's member function + gets its parameters changed, and all derived classes need to have + their overriding functions updated. + + class Foo +{ + int bar(); + int abc(int x); +} + +class Foo2 : Foo +{ + override + { + int bar(char c); // error, no bar(char) in Foo + int abc(int x); // ok + } +} ++ + Static Attribute+ ++static ++ + + The static attribute applies to functions and data. + It means that the declaration does not apply to a particular + instance of an object, but to the type of the object. In + other words, it means there is no this reference. + static is ignored when applied to other declarations. + + class Foo +{ + static int bar() { return 6; } + int foobar() { return 7; } +} + +... + +Foo f = new Foo; +Foo.bar(); // produces 6 +Foo.foobar(); // error, no instance of Foo +f.bar(); // produces 6; +f.foobar(); // produces 7; ++ + Static functions are never virtual. + + + Static data has only one instance for the entire program, + not once per object. + + + Static does not have the additional C meaning of being local + to a file. Use the private attribute in D to achieve that. + For example: + + module foo; +int x = 3; // x is global +private int y = 4; // y is local to module foo ++ + + Auto Attribute+ ++auto ++ + + The auto attribute is used for local variables and for class + declarations. For class declarations, the auto attribute creates + an auto class. + For local declarations, auto implements the RAII (Resource + Acquisition Is Initialization) protocol. This means that the + destructor for an object is automatically called when the auto + reference to it goes out of scope. The destructor is called even + if the scope is exited via a thrown exception, thus auto + is used to guarantee cleanup. + + + If there is more than one auto variable going out of scope + at the same point, then the destructors are called in the reverse + order that the variables were constructed. + + + Auto cannot be applied to globals, statics, data members, inout + or out parameters. Arrays of autos are not allowed, and auto + function return values are not allowed. Assignment to an auto, + other than initialization, is not allowed. + Rationale: These restrictions may get relaxed in the future + if a compelling reason to appears. + + + Abstract Attribute+ + If a class is abstract, it cannot be instantiated + directly. It can only be instantiated as a base class of + another, non-abstract, class. ++ + Classes become abstract if they are defined within an + abstract attribute, or if any of the virtual member functions + within it are declared as abstract. + + + Non-virtual functions cannot be declared as abstract. + + + Functions declared as abstract can still have function + bodies. This is so that even though they must be overridden, + they can still provide 'base class functionality.' + + + + |
-
-Home
-| Search
-| D
-| Comments
-
-|
-
- - - · Overview - · D for Win32 - · Win32 DLLs in D - · C .h to D Modules - · FAQ - · Style Guide - · Example: wc - · Future - · D Change Log - · Tech Tips - · Glossary - · Acknowledgements - - Tools - - · DMD D Compiler - · GDC D Compiler - · Linker - · Profiler - - Community - - · News Digest - · News - · Forum - · Announcements - · Learn - · D links - - Archives - - · digitalmars.D - · digitalmars.D.dtl - · digitalmars.D.announce - · digitalmars.D.learn - · digitalmars.D.bugs - · D.gnu - · Old D - - - |
-
-
-
-
-D Change Log- - -
- - What's New for - D 0.134 -- -Sep 28, 2005 -- - New/Changed Features-
Ddoc-
Bugs Fixed-
- - What's New for - D 0.133 -- -Sep 24, 2005 -- - New/Changed Features-
Ddoc-
Bugs Fixed-
- - What's New for - D 0.132 -- -Sep 19, 2005 -- - New/Changed Features-
- - What's New for - D 0.131 -- -Sep 8, 2005 -- - New/Changed Features-
Bugs Fixed-
- - What's New for - D 0.130 -- -Sep 6, 2005 -- - New/Changed Features-
Bugs Fixed-
- - What's New for - D 0.129 -- -Aug 5, 2005 -- - New/Changed Features-
Bugs Fixed-
- - What's New for - D 0.128 -- -Jul 10, 2005 -- - New/Changed Features-
Bugs Fixed-
- - What's New for - D 0.127 -- -Jun 16, 2005 -- - New/Changed Features-
Bugs Fixed-
- - What's New for - D 0.126 -- -Jun 7, 2005 -- - New/Changed Features-
Bugs Fixed-
- - What's New for - D 0.125 -- -May 20, 2005 -- - New/Changed Features-
Bugs Fixed-
- - What's New for - D 0.124 -- -May 19, 2005 -- - New/Changed Features-
Bugs Fixed-
- - What's New for - D 0.123 -- -May 11, 2005 -- - New/Changed Features-
Bugs Fixed-
- - What's New for - D 0.122 -- -May 3, 2005 -- - New/Changed Features-
Bugs Fixed-
- - What's New for - D 0.121 -- -Apr 15, 2005 -- - New/Changed Features-
Bugs Fixed-
- - What's New for - D 0.120 -- -Apr 6, 2005 -- - New/Changed Features-
Bugs Fixed-
- - What's New for - D 0.119 -- -Mar 18, 2005 -- - New/Changed Features-
Bugs Fixed-
- - What's New for - D 0.118 -- -Mar 12, 2005 -- - Bugs Fixed-
- - What's New for - D 0.117 -- -Mar 10, 2005 -- - New/Changed Features-
Bugs Fixed-
- - What's New for - D 0.116 -- -Mar 7, 2005 -- - New/Changed Features-
Bugs Fixed-
- - What's New for - D 0.115 -- -Feb 28, 2005 -- - Bugs Fixed-
- - What's New for - D 0.114 -- -Feb 27, 2005 -- - New/Changed Features-
Bugs Fixed-
- - What's New for - D 0.113 -- -Feb 12, 2005 -- - New/Changed Features-
Bugs Fixed-
- - What's New for - D 0.112 -- -Jan 26, 2005 -- -Note: This is a library only change, the dmd executables are still -at 0.111. - - New/Changed Features-
- - What's New for - D 0.111 -- -Jan 15, 2005 - -New/Changed Features-
Bugs Fixed-
- - What's New for - D 0.110 -- -Dec 30, 2004 - -New/Changed Features-
Bugs Fixed-
- - What's New for - D 0.109 -- -Dec 5, 2004 - -New/Changed Features-
Bugs Fixed-
- - What's New for - D 0.108 -- -Nov 30, 2004 - -Bugs Fixed-
- - What's New for - D 0.107 -- -Nov 29, 2004 - -New/Changed Features-
Bugs Fixed-
- - What's New for - D 0.106 -- -Nov 9, 2004 - -New/Changed Features-
Bugs Fixed-
- - What's New for - D 0.105 -- -Oct 28, 2004 - -New/Changed Features-
Bugs Fixed-
- - What's New for - D 0.104 -- -Oct 21, 2004 - -Bugs Fixed-
- What's New for D 0.103- -Oct 20, 2004 - -New/Changed Features-
Bugs Fixed-
- What's New for D 0.102- -Sep 20, 2004 - -New/Changed Features-
Bugs Fixed-
- What's New for D 0.101- -Aug 30, 2004 - -New/Changed Features-
Bugs Fixed-
- What's New for D 0.100- -Aug 20, 2004 - -Bugs Fixed-
- What's New for D 0.99- -Aug 19, 2004 - -New/Changed Features-
Bugs Fixed-
- What's New for D 0.98- -Aug 5, 2004 - -New/Changed Features-
Bugs Fixed-
- What's New for D 0.97- -Jul 26, 2004 - -New/Changed Features-
Bugs Fixed-
- What's New for D 0.96- -Jul 21, 2004 - -New/Changed Features-
Bugs Fixed-
- What's New for D 0.95- -Jul 6, 2004 - -New/Changed Features-
Bugs Fixed-
- What's New for D 0.94- -Jun 27, 2004 - -Bugs Fixed-
- What's New for D 0.93- -Jun 22, 2004 - -New/Changed Features-
Bugs Fixed-
- What's New for D 0.92- -Jun 7, 2004 - -New/Changed Features-
Bugs Fixed-
- What's New for D 0.91- -May 27, 2004 - -New/Changed Features-
Bugs Fixed-
- What's New for D 0.90- -May 20, 2004 - -Bugs Fixed-
- What's New for D 0.89- -May 17, 2004 - -New/Changed Features-
Bugs Fixed-
- What's New for D 0.88- -May 5, 2004 - -New/Changed Features-
Bugs Fixed-
- What's New for D 0.86- -Apr 23, 2004 - -New/Changed Features-
Bugs Fixed-
- What's New for D 0.82- -Mar 28, 2004 - -Bugs Fixed-
- What's New for D 0.81- -Mar 7, 2004 - -Bugs Fixed-
- What's New for D 0.80- -Mar 5, 2004 - -New/Changed Features-
Bugs Fixed-
- What's New for D 0.79- -Feb 2, 2004 - -New/Changed Features-
Bugs Fixed-
- What's New for D 0.78- -Jan 14, 2004 - -New/Changed Features-
Bugs Fixed-
- What's New for D 0.77- -Jan 2, 2004 - -New/Changed Features-
Bugs Fixed-
- What's New for D 0.76- -Nov 21, 2003 - -New/Changed Features-
Bugs Fixed-
- What's New for D 0.75- -Nov 4, 2003 - -New/Changed Features-
- What's New for D 0.74- -Oct 15, 2003 - -New/Changed Features-
Bugs Fixed-
- What's New for D 0.73- -Sep 18, 2003 - -New/Changed Features-
Bugs Fixed-
- What's New for D 0.72- -Sep 14, 2003 - -New/Changed Features-
Bugs Fixed-
Linux Bugs Fixed-
- What's New for D 0.71- -Sep 3, 2003 - -New/Changed Features-
Bugs Fixed-
- What's New for D 0.70- -Aug 24, 2003 - -New/Changed Features-
Bugs Fixed-
- What's New for D 0.69- -Aug 11, 2003 - -New/Changed Features-
Bugs Fixed-
- What's New for D 0.68- -Jul 8, 2003 - -
- What's New for D 0.67- -Jun 17, 2003 - -
- What's New for D 0.66- -Jun 8, 2003 - -
- What's New for D 0.65- -May 13, 2003 - -linux version- -
- What's New for D 0.64- -May 12, 2003 - -linux version- -
- What's New for D 0.63- -May 10, 2003 - -
- What's New for D 0.61- -Mar 30, 2003 - -
- What's New for D 0.59- -Mar 6, 2003 - -
- What's New for D 0.58- -Mar 3, 2003 - -
- What's New for D 0.57- -Feb 25, 2003 - -
- What's New for D 0.56- -Feb 20, 2003 - -
- What's New for D 0.55- -Feb 17, 2003 - -
- What's New for D 0.54- -Feb 14, 2003 - -
- What's New for D 0.53- -Feb 8, 2003 - -
- What's New for D 0.52- -Feb 5, 2003 - -
- What's New for D 0.51- -Jan 27, 2003 - -
- What's New for D 0.50- -Nov 20, 2002 - -
- What's New for D 0.49- -Nov 18, 2002 - -
- What's New for D 0.48- -Oct 25, 2002 - -
- What's New for D 0.46- -Oct 22, 2002 - -
- What's New for D 0.45- -Oct 8, 2002 - -
- What's New for D 0.44- -Oct 1, 2002 - -
- What's New for D 0.43- -Sep 28, 2002 - -
|
Add feedback and comments regarding this - page.
- -+ +
+ +
+ +
+ +
+ +
+ +
+ +
+ +
+ +
+ +
+ +
+ +
+ +
+ +
+ +
+ +
+ +
+++
+- replace uint buffer lengths with size_t +
- add line-by-line opApply to Stream and InputStream (due to Regan + Heath)
+ int opApply(int delegate(inout char[] line) dg)
+ int opApply(int delegate(inout ulong n, inout char[] line) dg)
+ int opApply(int delegate(inout wchar[] line) dg)
+ int opApply(int delegate(inout ulong n, inout wchar[] line) dg) +- move readable/writeable/seekable tests from contracts to the body + since phobos is compiled in release mode and users can easily try + to write to an unwritable stream +
- return "this" from writef and writefln to allow chaining (eg flush) +
- fix TArrayStream read/write to check for "eof" (Derick Eddington) +
- make SliceStream preserve the source buffer position if seekable + and pay attention to isOpen (Derick) +
- implement available() for more stream types (Derick) +
- copyFrom bug fixes and position preserving if seekable (Derick) +
- move the initialization of the BOMs to the initializer instead of the + module constructor. +
- make isopen protected (from private) so that subclasses can see it. +
- more unittest (Derick and Ben) +
- fix File.size() bug on Windows +
- fix File.open() error message to say "Cannot open or create file" +
+ +
+ +
+ +
+ +
+ module foo.bar;+ then bar is not in scope, one must use foo.bar. +
+ +
+ +
+ IPv6 not supported but the C interface for it should all be there. + Split up socket.d and put the C stuff in std.c modules. + std.c.linux.linux updated because most of the socket functions + are actually regular I/O functions. Added classes Protocol and Service. + Added Socket.accepting() to allow derived classes to be + accepted. + Documentation and samples updated. + Fixed major bug in the linux version. ++ +
+ +
+ +Note: This is a library only change, the dmd executables are still +at 0.111. + +
+ I've attached modified versions of std.stream and mmfile and the help + section for std.mmfile and std.stream. + The std.mmfile changes are: +++
+ The std.stream changes are: +- change module declaration to std.mmfile +
- change class declaration to not be auto +
- add mMode read-only property to get the file mode of type MmFile.Mode +
+
+- added Dave Fladebo's performance improvements for buffered readLine +
- fixed a bug in read/write cfloat/cdouble/creal endian support +
- uncommented MmFileStream and cleaned it up to use MmFile's mode property. +
+ template foo(T:T[]) ++ now correctly resolve T in foo(int[]) to int rather than int[]. +
+ char[][] foo; + foo = new char[][45]; // new, correct way to allocate array of 45 strings + //foo = new char[45][]; // old, now wrong, way ++
+ int[3][4] a; // a is 4 arrays of 3 arrays of ints + int b[4][3]; // b is 4 arrays of 3 arrays of ints ++
+ byte b = 0x10; // ok + ubyte c = 0x100; // error + byte d = 0x80; // error + ubyte e = 0x80; // ok ++
-
-Home
-| Search
-| D
-| Comments
-
-|
-
- - -· Lexical -· Modules -· Declarations -· Types -· Properties -· Attributes -· Pragmas -· Expressions -· Statements -· Arrays -· Structs & Unions -· Classes -· Interfaces -· Enums -· Functions -· Operator Overloading -· Templates -· Mixins -· Contracts -· Conditional Compilation -· Handling errors -· Garbage Collection -· Memory Management -· Floating Point -· Inline Assembler -· Documentation Comments -· Interfacing To C -· Portability Guide -· Embedding D in HTML -· Named Character Entities -· Application Binary Interface - - |
-
-
-
-
-Classes- - The object-oriented features of D all come from classes. The class hierarchy - has as its root the class Object. Object defines a minimum level of functionality - that each derived class has, and a default implementation for that functionality. -- - Classes are programmer defined types. Support for classes are what - make D an object oriented language, giving it encapsulation, inheritance, - and polymorphism. D classes support the single inheritance paradigm, extended - by adding support for interfaces. Class objects are instantiated by reference - only. - - - A class can be exported, which means its name and all its non-private - members are exposed externally to the DLL or EXE. - - - A class declaration is defined: - -
- ClassDeclaration:
- class Identifier BaseClassListopt ClassBody
-
- BaseClassList:
- : SuperClass
- : SuperClass InterfaceClasses
- : InterfaceClass
- : InterfaceClass InterfaceClasses
-
- SuperClass:
- Identifier
- Protection Identifier
-
- InterfaceClasses:
- InterfaceClass
- InterfaceClass InterfaceClasses
-
- InterfaceClass:
- Identifier
- Protection Identifier
-
- Protection:
- private
- package
- public
- export
-
- ClassBody:
- { }
- { ClassBodyDeclarations }
-
- ClassBodyDeclarations:
- ClassBodyDeclaration
- ClassBodyDeclaration ClassBodyDeclarations
-
- ClassBodyDeclaration:
- Declaration
- Constructor
- Destructor
- StaticConstructor
- StaticDestructor
- Invariant
- UnitTest
- ClassAllocator
- ClassDeallocator
-
-
-Classes consist of:
-
-
- class Foo
- {
- ... members ...
- }
-
-
- Note that there is no trailing ; after the closing } of the class
- definition.
- It is also not possible to declare a variable var like:
-
-
- class Foo { } var;
-
-
- Instead:
-
-
- class Foo { }
- Foo var;
-
-
-Fields- - Class members are always accessed with the . operator. There are no :: or -> - operators as in C++. -- - The D compiler is free to rearrange the order of fields in a class to - optimally pack them in an implementation-defined manner. - Consider the fields much like the local - variables in a function - - the compiler assigns some to registers and shuffles others around all to - get the optimal - stack frame layout. This frees the code designer to organize the fields - in a manner that - makes the code more readable rather than being forced to organize it - according to - machine optimization rules. Explicit control of field layout is provided - by struct/union - types, not classes. - - Field Properties- - The .offsetof property gives the offset in bytes of the field - from the beginning of the class instantiation. - .offsetof can only be applied to fields qualified with the - type of the class, not expressions which produce the type of - the field itself: - -
- class Foo
- {
- int x;
- }
- ...
- void test(Foo foo)
- {
- size_t o;
-
- o = Foo.x.offsetof; // yields 8
- o = foo.x.offsetof; // error, .offsetof an int type
- }
-
-
-Super Class- - All classes inherit from a super class. If one is not specified, - it inherits from Object. Object forms the root of the D class - inheritance hierarchy. - -Constructors- -- Constructor: - this( ParameterList ) BlockStatement -- - Members are always initialized to the default initializer - for their type, which is usually 0 for integer types and - NAN for floating point types. - This eliminates an entire - class of obscure problems that come from - neglecting to initialize a member in one of the constructors. - In the class definition, - there can be a static initializer to be - used instead of the default: - -
- class Abc
- {
- int a; // default initializer for a is 0
- long b = 7; // default initializer for b is 7
- float f; // default initializer for f is NAN
- }
-
-
- This static initialization is done before any constructors are
- called.
- - - Constructors are defined with a function name of this - and having no return value: - -
- class Foo
- {
- this(int x) // declare constructor for Foo
- { ...
- }
- this()
- { ...
- }
- }
-
-
- Base class construction is done by calling the base class
- constructor by the name super:
-
-
- class A { this(int y) { } }
-
- class B : A
- {
- int j;
- this()
- {
- ...
- super(3); // call base constructor A.this(3)
- ...
- }
- }
-
-
- Constructors can also call other constructors for the same class
- in order to share common initializations:
-
-
- class C
- {
- int j;
- this()
- {
- ...
- }
- this(int i)
- {
- this();
- j = i;
- }
- }
-
-
- If no call to constructors via this or super appear
- in a constructor, and the base class has a constructor, a call
- to super() is inserted at the beginning of the constructor.
- - - If there is no constructor for a class, but there is a constructor - for the base class, a default constructor of the form: - -
- this() { }
-
-
- is implicitly generated.
- - - Class object construction is very flexible, but some restrictions - apply: -
- A a = new A(3); -- - The following steps happen: - -
Destructors- -- Destructor: - ~this() BlockStatement -- - The garbage collector calls the destructor function when the object - is deleted. The syntax - is: - -
- class Foo
- {
- ~this() // destructor for Foo
- {
- }
- }
-
-
- There can be only one destructor per class, the destructor
- does not have any parameters,
- and has no attributes. It is always virtual.
- - - The destructor is expected to release any resources held by the object. - - - The program can explicitly inform the garbage collector that an - object is no longer referred to (with the delete expression), and - then the garbage collector calls the destructor - immediately, and adds the object's memory to the free storage. - The destructor is guaranteed to never be called twice. - - - The destructor for the super class automatically gets called when - the destructor ends. There is no way to call the super destructor - explicitly. - - - When the garbage collector calls a destructor for an object of a class - that has - members that are references to garbage collected objects, those - references are no longer valid. This means that destructors - cannot reference sub objects. - This rule does not apply to auto objects or objects deleted - with the DeleteExpression. - - - The garbage collector is not guaranteed to run the destructor - for all unreferenced objects. Furthermore, the order in which the - garbage collector calls destructors for unreference objects - is not specified. - - - Objects referenced from the data segment never get collected - by the gc. - - Static Constructors- -- StaticConstructor: - static this() BlockStatement -- - A static constructor is defined as a function that performs - initializations before the - main() function gets control. Static constructors are used to - initialize - static class members - with values that cannot be computed at compile time. - - - Static constructors in other languages are built implicitly by using - member - initializers that can't be computed at compile time. The trouble with - this stems from not - having good control over exactly when the code is executed, for example: - -
- class Foo
- {
- static int a = b + 1;
- static int b = a * 2;
- }
-
-
- What values do a and b end up with, what order are the initializations
- executed in, what
- are the values of a and b before the initializations are run, is this a
- compile error, or is this
- a runtime error? Additional confusion comes from it not being obvious if
- an initializer is
- static or dynamic.
- - - D makes this simple. All member initializations must be determinable by - the compiler at - compile time, hence there is no order-of-evaluation dependency for - member - initializations, and it is not possible to read a value that has not - been initialized. Dynamic - initialization is performed by a static constructor, defined with - a special syntax static this(). - -
- class Foo
- {
- static int a; // default initialized to 0
- static int b = 1;
- static int c = b + a; // error, not a constant initializer
-
- static this() // static constructor
- {
- a = b + 1; // a is set to 2
- b = a * 2; // b is set to 4
- }
- }
-
-
- static this() is called by the startup code before
- main() is called. If it returns normally
- (does not throw an exception), the static destructor is added
- to the list of functions to be
- called on program termination.
- Static constructors have empty parameter lists.
- - - Static constructors within a module are executed in the lexical - order in which they appear. - All the static constructors for modules that are directly or - indirectly imported - are executed before the static constructors for the importer. - - - The static in the static constructor declaration is not - an attribute, it must appear immediately before the this: - -
- class Foo
- {
- static this() { ... } // a static constructor
- static private this() { ... } // not a static constructor
- static
- {
- this() { ... } // not a static constructor
- }
- static:
- this() { ... } // not a static constructor
- }
-
-
-Static Destructor- -- StaticDestructor: - static ~this() BlockStatement -- - A static destructor is defined as a special static function with the - syntax static ~this(). - -
- class Foo
- {
- static ~this() // static destructor
- {
- }
- }
-
-
- A static destructor gets called on program termination, but only if
- the static constructor
- completed successfully.
- Static destructors have empty parameter lists.
- Static destructors get called in the reverse order that the static
- constructors were called in.
- - - The static in the static denstructor declaration is not - an attribute, it must appear immediately before the ~this: - -
- class Foo
- {
- static ~this() { ... } // a static destructor
- static private ~this() { ... } // not a static destructor
- static
- {
- ~this() { ... } // not a static destructor
- }
- static:
- ~this() { ... } // not a static destructor
- }
-
-
-Class Invariants- -- ClassInvariant: - invariant BlockStatement -- - Class invariants are used to specify characteristics of a class that always - must be true (except while executing a member function). For example, a - class representing a date might have an invariant that the day must be 1..31 - and the hour must be 0..23: - - - -
- class Date
- {
- int day;
- int hour;
-
- invariant
- {
- assert(1 <= day && day <= 31);
- assert(0 <= hour && hour < 24);
- }
- }
-
-
- The class invariant is a contract saying that the asserts must hold
- true.
- The invariant is checked when a class constructor completes,
- at the start of the class destructor, before a public or exported
- member is run, and after a public or exported function finishes.
- - - The code in the invariant may not call any public non-static members - of the - class, either directly or indirectly. - Doing so will result in a stack overflow, as the invariant will wind - up being called in an infinitely recursive manner. - -
- class Foo
- {
- public void f() { }
- private void g() { }
-
- invariant
- {
- f(); // error, cannot call public member function from invariant
- g(); // ok, g() is not public
- }
- }
-
-
- The invariant
- can be checked when a class object is the argument to an
- assert() expression, as:
-
- - Date mydate; - ... - assert(mydate); // check that class Date invariant holds -- - If the invariant fails, it throws an InvariantException.
- Class invariants are inherited, that is,
- any class invariant is implicitly anded with the invariants of its base classes.
- - - There can be only one ClassInvariant per class. - - - When compiling for release, the invariant code is not generated, and the compiled program - runs at maximum speed. - - Unit Tests- -- UnitTest: - unittest BlockStatement -- - Unit tests are a series of test cases applied to a class to determine - if it is working properly. Ideally, unit tests should be run every - time a program is compiled. The best way to make sure that unit - tests do get run, and that they are maintained along with the class - code is to put the test code right in with the class implementation - code. - - - Classes can have a special member function called: - -
- unittest
- {
- ...test code...
- }
-
-
- A compiler switch, such as -unittest for dmd, will
- cause the unittest test code to be compiled and incorporated into
- the resulting executable. The unittest code gets run after
- static initialization is run and before the main()
- function is called.
- - - For example, given a class Sum that is used to add two values: - -
- class Sum
- {
- int add(int x, int y) { return x + y; }
-
- unittest
- {
- Sum sum = new Sum;
- assert(sum.add(3,4) == 7);
- assert(sum.add(-2,0) == -2);
- }
- }
-
-
-Class Allocators- -- ClassAllocator: - new ( ParameterList ) BlockStatement -- - A class member function of the form: - -
- new(uint size)
- {
- ...
- }
-
-
- is called a class allocator.
- The class allocator can have any number of parameters, provided
- the first one is of type uint.
- Any number can be defined for a class, the correct one is
- determined by the usual function overloading rules.
- When a new expression:
-
- - new Foo; -- - is executed, and Foo is a class that has - an allocator, the allocator is called with the first argument - set to the size in bytes of the memory to be allocated for the - instance. - The allocator must allocate the memory and return it as a - void*. - If the allocator fails, it must not return a null, but - must throw an exception. - If there is more than one parameter to the allocator, the - additional arguments are specified within parentheses after - the new in the NewExpression: - -
- class Foo
- {
- this(char[] a) { ... }
-
- new(uint size, int x, int y)
- {
- ...
- }
- }
-
- ...
-
- new(1,2) Foo(a); // calls new(Foo.size,1,2)
-
-
- Derived classes inherit any allocator from their base class,
- if one is not specified.
- - - See also Explicit Class Instance Allocation. - - Class Deallocators- -- ClassDeallocator: - delete ( ParameterList ) BlockStatement -- - A class member function of the form: - -
- delete(void *p)
- {
- ...
- }
-
-
- is called a class deallocator.
- The deallocator must have exactly one parameter of type void*.
- Only one can be specified for a class.
- When a delete expression:
-
- - delete f; -- - is executed, and f is a reference to a class instance that has - a deallocator, the deallocator is called with a pointer to the - class instance after the destructor (if any) for the class is - called. It is the responsibility of the deallocator to free - the memory. - - - Derived classes inherit any deallocator from their base class, - if one is not specified. - - - See also Explicit Class Instance Allocation. - - Auto Classes- - An auto class is a class with the auto attribute, as in: - -
- auto class Foo { ... }
-
-
- The auto characteristic is inherited, so if any classes derived
- from an auto class are also auto.
- - - An auto class reference can only appear as a function local variable. - It must be declared as being auto: - -
- auto class Foo { ... }
-
- void func()
- {
- Foo f; // error, reference to auto class must be auto
- auto Foo g = new Foo(); // correct
- }
-
-
- When an auto class reference goes out of scope, the destructor
- (if any) for it is automatically called. This holds true even if
- the scope was exited via a thrown exception.
-
-Nested Classes- - A nested class is a class that is declared inside the scope - of a function or another class. - A nested class has access to the variables and other symbols - of the classes and functions it is nested inside: - -
- class Outer
- {
- int m;
-
- class Inner
- {
- int foo()
- {
- return m; // Ok to access member of Outer
- }
- }
- }
-
- void func()
- { int m;
-
- class Inner
- {
- int foo()
- {
- return m; // Ok to access local variable m of func()
- }
- }
- }
-
-
- If a nested class has the static attribute, then it can
- not access variables of the enclosing scope that are local to the
- stack or need a this:
-
-
- class Outer
- {
- int m;
- static int n;
-
- static class Inner
- {
- int foo()
- {
- return m; // Error, Inner is static and m needs a this
- return n; // Ok, n is static
- }
- }
- }
-
- void func()
- { int m;
- static int n;
-
- static class Inner
- {
- int foo()
- {
- return m; // Error, Inner is static and m is local to the stack
- return n; // Ok, n is static
- }
- }
- }
-
-
- Non-static nested classes work by containing an extra hidden member
- (called the context pointer)
- that is the frame pointer of the enclosing function if it is nested
- inside a function, or the this of the enclosing class's instance
- if it is nested inside a class.
- - - When a non-static nested class is instantiated, the context pointer - is assigned before the class's constructor is called, therefore - the constructor has full access to the enclosing variables. - A non-static nested class can only be instantiated when the necessary - context pointer information is available: - -
- class Outer
- {
- class Inner { }
-
- static class SInner { }
- }
-
- void func()
- {
- class Nested { }
-
- Outer o = new Outer; // Ok
- Outer.Inner oi = new Outer.Inner; // Error, no 'this' for Outer
- Outer.SInner os = new Outer.SInner; // Ok
-
- Nested n = new Nested; // Ok
- }
-
-
- While a non-static nested class can access the stack variables
- of its enclosing function, that access becomes invalid once
- the enclosing function exits:
-
-
- class Base
- {
- int foo() { return 1; }
- }
-
- Base func()
- { int m = 3;
-
- class Nested : Base
- {
- int foo() { return m; }
- }
-
- Base b = new Nested;
-
- assert(b.foo() == 3); // Ok, func() is still active
- return b;
- }
-
- int test()
- {
- Base b = func();
- return b.foo(); // Error, func().m is undefined
- }
-
-
- If this kind of functionality is needed, the way to make it work
- is to make copies of the needed variables within the nested class's
- constructor:
-
-
- class Base
- {
- int foo() { return 1; }
- }
-
- Base func()
- { int m = 3;
-
- class Nested : Base
- { int m_;
-
- this() { m_ = m;
- int foo() { return m_; }
- }
-
- Base b = new Nested;
-
- assert(b.foo() == 3); // Ok, func() is still active
- return b;
- }
-
- int test()
- {
- Base b = func();
- return b.foo(); // Ok, using cached copy of func().m
- }
-
-
-Anonymous Nested Classes- - An anonymous nested class is both defined and instantiated with - a NewAnonClassExpression: - -- NewAnonClassExpression: - new (ArgumentList)opt class (ArgumentList)opt SuperClassopt InterfaceClassesopt ClassBody -- - which is equivalent to: - - - class Identifier : SuperClass InterfaceClasses - ClassBody - - new (ArgumentList) Identifier (ArgumentList); -- - where Identifier is the name generated for the anonymous - nested class. - - |
Add feedback and comments regarding this - page.
- -|
+
+
+
+
+ + +· Lexical + + +· Modules + + +· Declarations + + +· Types + + +· Properties + + +· Attributes + + +· Pragmas + + +· Expressions + + +· Statements + + +· Arrays + + +· Structs & Unions + + +· Classes + + +· Interfaces + + +· Enums + + +· Functions + + +· Operator Overloading + + +· Templates + + +· Mixins + + +· Contracts + + +· Conditional Compilation + + +· Handling errors + + +· Garbage Collection + + +· Memory Management + + +· Floating Point + + +· Inline Assembler + + +· Documentation Comments + + +· Interfacing To C + + +· Portability Guide + + +· Embedding D in HTML + + +· Named Character Entities + + +· Application Binary Interface + + + + |
+
+
+
+Classes+ + The object-oriented features of D all come from classes. The class hierarchy + has as its root the class Object. Object defines a minimum level of functionality + that each derived class has, and a default implementation for that functionality. ++ + Classes are programmer defined types. Support for classes are what + make D an object oriented language, giving it encapsulation, inheritance, + and polymorphism. D classes support the single inheritance paradigm, extended + by adding support for interfaces. Class objects are instantiated by reference + only. + + + A class can be exported, which means its name and all its non-private + members are exposed externally to the DLL or EXE. + + + A class declaration is defined: + +
+ClassDeclaration:
+ class Identifier BaseClassListopt ClassBody
+
+BaseClassList:
+ : SuperClass
+ : SuperClass InterfaceClasses
+ : InterfaceClass
+ : InterfaceClass InterfaceClasses
+
+SuperClass:
+ Identifier
+ Protection Identifier
+
+InterfaceClasses:
+ InterfaceClass
+ InterfaceClass InterfaceClasses
+
+InterfaceClass:
+ Identifier
+ Protection Identifier
+
+Protection:
+ private
+ package
+ public
+ export
+
+ClassBody:
+ { }
+ { ClassBodyDeclarations }
+
+ClassBodyDeclarations:
+ ClassBodyDeclaration
+ ClassBodyDeclaration ClassBodyDeclarations
+
+ClassBodyDeclaration:
+ Declaration
+ Constructor
+ Destructor
+ StaticConstructor
+ StaticDestructor
+ Invariant
+ UnitTest
+ ClassAllocator
+ ClassDeallocator
+
+
+
+Classes consist of:
+
+
class Foo
+{
+ ... members ...
+}
+
+
+ Note that there is no trailing ; after the closing } of the class
+ definition.
+ It is also not possible to declare a variable var like:
+
+class Foo { } var;
+
+
+ Instead:
+
+class Foo { }
+Foo var;
+
+
+Fields+ + Class members are always accessed with the . operator. There are no :: or -> + operators as in C++. ++ + The D compiler is free to rearrange the order of fields in a class to + optimally pack them in an implementation-defined manner. + Consider the fields much like the local + variables in a function - + the compiler assigns some to registers and shuffles others around all to + get the optimal + stack frame layout. This frees the code designer to organize the fields + in a manner that + makes the code more readable rather than being forced to organize it + according to + machine optimization rules. Explicit control of field layout is provided + by struct/union + types, not classes. + + Field Properties+ + The .offsetof property gives the offset in bytes of the field + from the beginning of the class instantiation. + .offsetof can only be applied to fields qualified with the + type of the class, not expressions which produce the type of + the field itself: + +class Foo +{ + int x; +} +... +void test(Foo foo) +{ + size_t o; + + o = Foo.x.offsetof; // yields 8 + o = foo.x.offsetof; // error, .offsetof an int type +} ++ + Super Class+ + All classes inherit from a super class. If one is not specified, + it inherits from Object. Object forms the root of the D class + inheritance hierarchy. + +Constructors+ ++Constructor: + this ( ParameterList ) BlockStatement ++ + + Members are always initialized to the default initializer + for their type, which is usually 0 for integer types and + NAN for floating point types. + This eliminates an entire + class of obscure problems that come from + neglecting to initialize a member in one of the constructors. + In the class definition, + there can be a static initializer to be + used instead of the default: + + class Abc +{ + int a; // default initializer for a is 0 + long b = 7; // default initializer for b is 7 + float f; // default initializer for f is NAN +} ++ + This static initialization is done before any constructors are + called. + + + Constructors are defined with a function name of this + and having no return value: + + class Foo +{ + this(int x) // declare constructor for Foo + { ... + } + this() + { ... + } +} ++ + Base class construction is done by calling the base class + constructor by the name super: + + class A { this(int y) { } } + +class B : A +{ + int j; + this() + { + ... + super(3); // call base constructor A.this(3) + ... + } +} ++ + Constructors can also call other constructors for the same class + in order to share common initializations: + + class C +{ + int j; + this() + { + ... + } + this(int i) + { + this(); + j = i; + } +} ++ + If no call to constructors via this or super appear + in a constructor, and the base class has a constructor, a call + to super() is inserted at the beginning of the constructor. + + + If there is no constructor for a class, but there is a constructor + for the base class, a default constructor of the form: + + this() { }
+
+
+ is implicitly generated.
+ + + Class object construction is very flexible, but some restrictions + apply: +
A a = new A(3);
+
+
+ The following steps happen:
+
+
Destructors+ ++Destructor: + ~this() BlockStatement ++ + + The garbage collector calls the destructor function when the object + is deleted. The syntax + is: + + class Foo +{ + ~this() // destructor for Foo + { + } +} ++ + There can be only one destructor per class, the destructor + does not have any parameters, + and has no attributes. It is always virtual. + + + The destructor is expected to release any resources held by the object. + + + The program can explicitly inform the garbage collector that an + object is no longer referred to (with the delete expression), and + then the garbage collector calls the destructor + immediately, and adds the object's memory to the free storage. + The destructor is guaranteed to never be called twice. + + + The destructor for the super class automatically gets called when + the destructor ends. There is no way to call the super destructor + explicitly. + + + When the garbage collector calls a destructor for an object of a class + that has + members that are references to garbage collected objects, those + references are no longer valid. This means that destructors + cannot reference sub objects. + This rule does not apply to auto objects or objects deleted + with the DeleteExpression. + + + The garbage collector is not guaranteed to run the destructor + for all unreferenced objects. Furthermore, the order in which the + garbage collector calls destructors for unreference objects + is not specified. + + + Objects referenced from the data segment never get collected + by the gc. + + Static Constructors+ ++StaticConstructor: + static this() BlockStatement ++ + + A static constructor is defined as a function that performs + initializations before the + main() function gets control. Static constructors are used to + initialize + static class members + with values that cannot be computed at compile time. + + + Static constructors in other languages are built implicitly by using + member + initializers that can't be computed at compile time. The trouble with + this stems from not + having good control over exactly when the code is executed, for example: + + class Foo +{ + static int a = b + 1; + static int b = a * 2; +} ++ + What values do a and b end up with, what order are the initializations + executed in, what + are the values of a and b before the initializations are run, is this a + compile error, or is this + a runtime error? Additional confusion comes from it not being obvious if + an initializer is + static or dynamic. + + + D makes this simple. All member initializations must be determinable by + the compiler at + compile time, hence there is no order-of-evaluation dependency for + member + initializations, and it is not possible to read a value that has not + been initialized. Dynamic + initialization is performed by a static constructor, defined with + a special syntax static this(). + + class Foo +{ + static int a; // default initialized to 0 + static int b = 1; + static int c = b + a; // error, not a constant initializer + + static this() // static constructor + { + a = b + 1; // a is set to 2 + b = a * 2; // b is set to 4 + } +} ++ + static this() is called by the startup code before + main() is called. If it returns normally + (does not throw an exception), the static destructor is added + to the list of functions to be + called on program termination. + Static constructors have empty parameter lists. + + + Static constructors within a module are executed in the lexical + order in which they appear. + All the static constructors for modules that are directly or + indirectly imported + are executed before the static constructors for the importer. + + + The static in the static constructor declaration is not + an attribute, it must appear immediately before the this: + + class Foo +{ + static this() { ... } // a static constructor + static private this() { ... } // not a static constructor + static + { + this() { ... } // not a static constructor + } + static: + this() { ... } // not a static constructor +} ++ + Static Destructor+ ++StaticDestructor: + static ~this() BlockStatement ++ + + A static destructor is defined as a special static function with the + syntax static ~this(). + + class Foo +{ + static ~this() // static destructor + { + } +} ++ + A static destructor gets called on program termination, but only if + the static constructor + completed successfully. + Static destructors have empty parameter lists. + Static destructors get called in the reverse order that the static + constructors were called in. + + + The static in the static denstructor declaration is not + an attribute, it must appear immediately before the ~this: + + class Foo +{ + static ~this() { ... } // a static destructor + static private ~this() { ... } // not a static destructor + static + { + ~this() { ... } // not a static destructor + } + static: + ~this() { ... } // not a static destructor +} ++ + Class Invariants+ ++ClassInvariant: + invariant BlockStatement ++ + + Class invariants are used to specify characteristics of a class that always + must be true (except while executing a member function). For example, a + class representing a date might have an invariant that the day must be 1..31 + and the hour must be 0..23: + + + + class Date +{ + int day; + int hour; + + invariant + { + assert(1 <= day && day <= 31); + assert(0 <= hour && hour < 24); + } +} ++ + The class invariant is a contract saying that the asserts must hold + true. + The invariant is checked when a class constructor completes, + at the start of the class destructor, before a public or exported + member is run, and after a public or exported function finishes. + + + The code in the invariant may not call any public non-static members + of the + class, either directly or indirectly. + Doing so will result in a stack overflow, as the invariant will wind + up being called in an infinitely recursive manner. + + class Foo +{ + public void f() { } + private void g() { } + + invariant + { + f(); // error, cannot call public member function from invariant + g(); // ok, g() is not public + } +} ++ + The invariant + can be checked when a class object is the argument to an + assert() expression, as:
+
+Date mydate; +... +assert(mydate); // check that class Date invariant holds ++ + If the invariant fails, it throws an InvariantException.
+ Class invariants are inherited, that is,
+ any class invariant is implicitly anded with the invariants of its base classes.
+ + + There can be only one ClassInvariant per class. + + + When compiling for release, the invariant code is not generated, and the compiled program + runs at maximum speed. + + Unit Tests+ ++UnitTest: + unittest BlockStatement ++ + + Unit tests are a series of test cases applied to a class to determine + if it is working properly. Ideally, unit tests should be run every + time a program is compiled. The best way to make sure that unit + tests do get run, and that they are maintained along with the class + code is to put the test code right in with the class implementation + code. + + + Classes can have a special member function called: + + unittest
+{
+ ...test code...
+}
+
+
+ A compiler switch, such as -unittest for dmd, will
+ cause the unittest test code to be compiled and incorporated into
+ the resulting executable. The unittest code gets run after
+ static initialization is run and before the main()
+ function is called.
+ + + For example, given a class Sum that is used to add two values: + + class Sum +{ + int add(int x, int y) { return x + y; } + + unittest + { + Sum sum = new Sum; + assert(sum.add(3,4) == 7); + assert(sum.add(-2,0) == -2); + } +} ++ + Class Allocators+ ++ClassAllocator: + new () ParameterList BlockStatement ++ + + A class member function of the form: + + new(uint size) +{ + ... +} ++ + is called a class allocator. + The class allocator can have any number of parameters, provided + the first one is of type uint. + Any number can be defined for a class, the correct one is + determined by the usual function overloading rules. + When a new expression: + + new Foo;
+
+
+ is executed, and Foo is a class that has
+ an allocator, the allocator is called with the first argument
+ set to the size in bytes of the memory to be allocated for the
+ instance.
+ The allocator must allocate the memory and return it as a
+ void*.
+ If the allocator fails, it must not return a null, but
+ must throw an exception.
+ If there is more than one parameter to the allocator, the
+ additional arguments are specified within parentheses after
+ the new in the NewExpression:
+
+class Foo +{ + this(char[] a) { ... } + + new(uint size, int x, int y) + { + ... + } +} + +... + +new(1,2) Foo(a); // calls new(Foo.size,1,2) ++ + Derived classes inherit any allocator from their base class, + if one is not specified. + + + See also Explicit Class Instance Allocation. + + Class Deallocators+ ++ClassDeallocator: + delete () ParameterList BlockStatement ++ + + A class member function of the form: + + delete(void *p) +{ + ... +} ++ + is called a class deallocator. + The deallocator must have exactly one parameter of type void*. + Only one can be specified for a class. + When a delete expression: + + delete f;
+
+
+ is executed, and f is a reference to a class instance that has
+ a deallocator, the deallocator is called with a pointer to the
+ class instance after the destructor (if any) for the class is
+ called. It is the responsibility of the deallocator to free
+ the memory.
+ + + Derived classes inherit any deallocator from their base class, + if one is not specified. + + + See also Explicit Class Instance Allocation. + + Auto Classes+ + An auto class is a class with the auto attribute, as in: + +auto class Foo { ... } ++ + The auto characteristic is inherited, so if any classes derived + from an auto class are also auto. + + + An auto class reference can only appear as a function local variable. + It must be declared as being auto: + + auto class Foo { ... } + +void func() +{ + Foo f; // error, reference to auto class must be auto + auto Foo g = new Foo(); // correct +} ++ + When an auto class reference goes out of scope, the destructor + (if any) for it is automatically called. This holds true even if + the scope was exited via a thrown exception. + + Nested Classes+ + A nested class is a class that is declared inside the scope + of a function or another class. + A nested class has access to the variables and other symbols + of the classes and functions it is nested inside: + +class Outer +{ + int m; + + class Inner + { + int foo() + { + return m; // Ok to access member of Outer + } + } +} + +void func() +{ int m; + + class Inner + { + int foo() + { + return m; // Ok to access local variable m of func() + } + } +} ++ + If a nested class has the static attribute, then it can + not access variables of the enclosing scope that are local to the + stack or need a this: + + class Outer +{ + int m; + static int n; + + static class Inner + { + int foo() + { + return m; // Error, Inner is static and m needs a this + return n; // Ok, n is static + } + } +} + +void func() +{ int m; + static int n; + + static class Inner + { + int foo() + { + return m; // Error, Inner is static and m is local to the stack + return n; // Ok, n is static + } + } +} ++ + Non-static nested classes work by containing an extra hidden member + (called the context pointer) + that is the frame pointer of the enclosing function if it is nested + inside a function, or the this of the enclosing class's instance + if it is nested inside a class. + + + When a non-static nested class is instantiated, the context pointer + is assigned before the class's constructor is called, therefore + the constructor has full access to the enclosing variables. + A non-static nested class can only be instantiated when the necessary + context pointer information is available: + + class Outer +{ + class Inner { } + + static class SInner { } +} + +void func() +{ + class Nested { } + + Outer o = new Outer; // Ok + Outer.Inner oi = new Outer.Inner; // Error, no 'this' for Outer + Outer.SInner os = new Outer.SInner; // Ok + + Nested n = new Nested; // Ok +} ++ + While a non-static nested class can access the stack variables + of its enclosing function, that access becomes invalid once + the enclosing function exits: + + class Base +{ + int foo() { return 1; } +} + +Base func() +{ int m = 3; + + class Nested : Base + { + int foo() { return m; } + } + + Base b = new Nested; + + assert(b.foo() == 3); // Ok, func() is still active + return b; +} + +int test() +{ + Base b = func(); + return b.foo(); // Error, func().m is undefined +} ++ + If this kind of functionality is needed, the way to make it work + is to make copies of the needed variables within the nested class's + constructor: + + class Base +{ + int foo() { return 1; } +} + +Base func() +{ int m = 3; + + class Nested : Base + { int m_; + + this() { m_ = m; + int foo() { return m_; } + } + + Base b = new Nested; + + assert(b.foo() == 3); // Ok, func() is still active + return b; +} + +int test() +{ + Base b = func(); + return b.foo(); // Ok, using cached copy of func().m +} ++ + Anonymous Nested Classes+ + An anonymous nested class is both defined and instantiated with + a NewAnonClassExpression: + ++NewAnonClassExpression: + new ()ArgumentListopt class ()ArgumentListopt SuperClassopt InterfaceClassesopt ClassBody ++ + + which is equivalent to: + + class Identifier : SuperClass InterfaceClasses + ClassBody + +new (ArgumentList) Identifier (ArgumentList); ++ + where Identifier is the name generated for the anonymous + nested class. + + + |
-
-Home
-| Search
-| D
-| Comments
-
-|
-
- - -D vs C/C++/C#/Java -Rationale for Builtins -Converting C to D -Converting C++ to D -The C Preprocessor vs D -D strings vs C++ std::string -D complex vs C++ std::complex -D Contract Programming vs C++ - - |
-
-
-
-D vs Other Languages- - This table is a quick and rough comparison of various features of - D - with other languages it is frequently compared with. - While many capabilities are available with standard libraries, this - table is for features built in to the core language itself. - Rationale. - Only official standardized features are considered, not proposed - features, betas, or extensions. And, like all language comparisons, - it is biased in terms of what features are mentioned, omitted, and - my interpretation of those features. -- -
Notes- -
Errors- - If I've made any errors in this table, please contact me so - I can correct them: - - |
Add feedback and comments regarding this - page.
- -|
+
+
+
+
+ + +· D vs C/C++/C#/Java + + +· Rationale for Builtins + + +· Converting C to D + + +· Converting C++ to D + + +· The C Preprocessor vs D + + +· D strings vs C++ std::string + + +· D complex vs C++ std::complex + + +· D Contract Programming vs C++ + + + + |
+
+
+D + vs Other Languages+ + This table is a quick and rough comparison of various features of + D + + with other languages it is frequently compared with. + While many capabilities are available with standard libraries, this + table is for features built in to the core language itself. + Rationale. + Only official standardized features are considered, not proposed + features, betas, or extensions. And, like all language comparisons, + it is biased in terms of what features are mentioned, omitted, and + my interpretation of those features. ++ +
Notes+ +
Errors+ + If I've made any errors in this table, please contact me so + I can correct them: + + + |
-
-Home
-| Search
-| D
-| Comments
-
-|
-
- - -D vs C/C++/C#/Java -Rationale for Builtins -Converting C to D -Converting C++ to D -The C Preprocessor vs D -D strings vs C++ std::string -D complex vs C++ std::complex -D Contract Programming vs C++ - - |
-
-
-
-D Complex Types and C++ std::complex- - How do D's complex numbers compare with C++'s std::complex class? - -Syntactical Aesthetics- - In C++, the complex types are: - -- complex<float> - complex<double> - complex<long double> -- - C++ has no distinct imaginary type. D has 3 complex types and 3 - imaginary types: - - - cfloat - cdouble - creal - ifloat - idouble - ireal -- - A C++ complex number can interact with an arithmetic literal, but - since there is no imaginary type, imaginary numbers can only be - created with the constructor syntax: - - - complex<long double> a = 5; // a = 5 + 0i - complex<long double> b(0,7); // b = 0 + 7i - c = a + b + complex<long double>(0,7); // c = 5 + 14i -- - In D, an imaginary numeric literal has the 'i' suffix. - The corresponding code would be the more natural: - - - creal a = 5; // a = 5 + 0i - ireal b = 7i; // b = 7i - c = a + b + 7i; // c = 5 + 14i -- - For more involved expressions involving constants: - - - c = (6 + 2i - 1 + 3i) / 3i; -- - In C++, this would be: - - - c = (complex<double>(6,2) + complex<double>(-1,3)) / complex<double>(0,3); -- - or if an imaginary class were added to C++ it might be: - - - c = (6 + imaginary<double>(2) - 1 + imaginary<double>(3))) / imaginary<double>(3); -- - In other words, an imaginary number nn can be represented with - just nni rather than writing a constructor call - complex<long double>(0,nn). - - Efficiency- - The lack of an imaginary type in C++ means that operations on - imaginary numbers wind up with a lot of extra computations done - on the 0 real part. For example, adding two imaginary numbers - in D is one add: - -- ireal a, b, c; - c = a + b; -- - In C++, it is two adds, as the real parts get added too: - - - c.re = a.re + b.re; - c.im = a.im + b.im; -- - Multiply is worse, as 4 multiplies and two adds are done instead of - one multiply: - - - c.re = a.re * b.re - a.im * b.im; - c.im = a.im * b.re + a.re * b.im; -- - Divide is the worst - D has one divide, whereas C++ implements - complex division with typically one comparison, 3 divides, - 3 multiplies and 3 additions: - -
- if (fabs(b.re) < fabs(b.im))
- {
- r = b.re / b.im;
- den = b.im + r * b.re;
- c.re = (a.re * r + a.im) / den;
- c.im = (a.im * r - a.re) / den;
- }
- else
- {
- r = b.im / b.re;
- den = b.re + r * b.im;
- c.re = (a.re + r * a.im) / den;
- c.im = (a.im - r * a.re) / den;
- }
-
-
- To avoid these efficiency concerns in C++, one could simulate
- an imaginary number using a double. For example, given the D:
-
- - cdouble c; - idouble im; - c *= im; -- - it could be written in C++ as: - - - complex<double> c; - double im; - c = complex<double>(-c.imag() * im, c.real() * im); -- - but then the advantages of complex being a library type integrated - in with the arithmetic operators have been lost. - - Semantics- - Worst of all, the lack of an imaginary type can cause the wrong - answer to be inadvertently produced. - To quote - Prof. Kahan: - -- "A streamline goes astray when the complex functions SQRT and LOG - are implemented, as is necessary in Fortran and in libraries - currently distributed with C/C++ compilers, in a way that - disregards the sign of 0.0 in IEEE 754 arithmetic and consequently - violates identities like SQRT( CONJ( Z ) ) = CONJ( SQRT( Z ) ) and - LOG( CONJ( Z ) ) = CONJ( LOG( Z ) ) whenever the COMPLEX variable Z - takes negative real values. Such anomalies are unavoidable if - Complex Arithmetic operates on pairs (x, y) instead of notional - sums x + i*y of real and imaginary - variables. The language of pairs is incorrect for Complex - Arithmetic; it needs the Imaginary type." -- - The semantic problems are: - -
References- - - How Java's Floating-Point Hurts Everyone Everywhere - Prof. W. Kahan and Joseph D. Darcy -- - - The Numerical Analyst as Computer Science Curmudgeon - by Prof. W. Kahan -
-
- "Branch Cuts for Complex Elementary Functions,
- or Much Ado About Nothing's Sign Bit"
- by W. Kahan, ch. |
Add feedback and comments regarding this - page.
- -|
+
+
+
+
+ + +· D vs C/C++/C#/Java + + +· Rationale for Builtins + + +· Converting C to D + + +· Converting C++ to D + + +· The C Preprocessor vs D + + +· D strings vs C++ std::string + + +· D complex vs C++ std::complex + + +· D Contract Programming vs C++ + + + + |
+
+
+D Complex Types and C++ std::complex+ + How do D's complex numbers compare with C++'s std::complex class? + +Syntactical Aesthetics+ + In C++, the complex types are: + +complex<float> +complex<double> +complex<long double> ++ + C++ has no distinct imaginary type. D has 3 complex types and 3 + imaginary types: + + cfloat +cdouble +creal +ifloat +idouble +ireal ++ + A C++ complex number can interact with an arithmetic literal, but + since there is no imaginary type, imaginary numbers can only be + created with the constructor syntax: + + complex<long double> a = 5; // a = 5 + 0i +complex<long double> b(0,7); // b = 0 + 7i +c = a + b + complex<long double>(0,7); // c = 5 + 14i ++ + In D, an imaginary numeric literal has the 'i' suffix. + The corresponding code would be the more natural: + + creal a = 5; // a = 5 + 0i +ireal b = 7i; // b = 7i +c = a + b + 7i; // c = 5 + 14i ++ + For more involved expressions involving constants: + + c = (6 + 2i - 1 + 3i) / 3i; ++ + In C++, this would be: + + c = (complex<double>(6,2) + complex<double>(-1,3)) / complex<double>(0,3); ++ + or if an imaginary class were added to C++ it might be: + + c = (6 + imaginary<double>(2) - 1 + imaginary<double>(3)) / imaginary<double>(3); ++ + In other words, an imaginary number nn can be represented with + just nni rather than writing a constructor call + complex<long double>(0,nn). + + Efficiency+ + The lack of an imaginary type in C++ means that operations on + imaginary numbers wind up with a lot of extra computations done + on the 0 real part. For example, adding two imaginary numbers + in D is one add: + +ireal a, b, c;
+c = a + b;
+
+
+ In C++, it is two adds, as the real parts get added too:
+
+c.re = a.re + b.re; +c.im = a.im + b.im; ++ + Multiply is worse, as 4 multiplies and two adds are done instead of + one multiply: + + c.re = a.re * b.re - a.im * b.im; +c.im = a.im * b.re + a.re * b.im; ++ + Divide is the worst - D has one divide, whereas C++ implements + complex division with typically one comparison, 3 divides, + 3 multiplies and 3 additions: + + if (fabs(b.re) < fabs(b.im))
+{
+ r = b.re / b.im;
+ den = b.im + r * b.re;
+ c.re = (a.re * r + a.im) / den;
+ c.im = (a.im * r - a.re) / den;
+}
+else
+{
+ r = b.im / b.re;
+ den = b.re + r * b.im;
+ c.re = (a.re + r * a.im) / den;
+ c.im = (a.im - r * a.re) / den;
+}
+
+
+ To avoid these efficiency concerns in C++, one could simulate
+ an imaginary number using a double. For example, given the D:
+
+cdouble c; +idouble im; +c *= im; ++ + it could be written in C++ as: + + complex<double> c; +double im; +c = complex<double>(-c.imag() * im, c.real() * im); ++ + but then the advantages of complex being a library type integrated + in with the arithmetic operators have been lost. + + Semantics+ + Worst of all, the lack of an imaginary type can cause the wrong + answer to be inadvertently produced. + To quote + Prof. Kahan: + ++ "A streamline goes astray when the complex functions SQRT and LOG + are implemented, as is necessary in Fortran and in libraries + currently distributed with C/C++ compilers, in a way that + disregards the sign of 0.0 in IEEE 754 arithmetic and consequently + violates identities like SQRT( CONJ( Z ) ) = CONJ( SQRT( Z ) ) and + LOG( CONJ( Z ) ) = CONJ( LOG( Z ) ) whenever the COMPLEX variable Z + takes negative real values. Such anomalies are unavoidable if + Complex Arithmetic operates on pairs (x, y) instead of notional + sums x + i*y of real and imaginary + variables. The language of pairs is incorrect for Complex + Arithmetic; it needs the Imaginary type." ++ + The semantic problems are: + +
References+ + + How Java's Floating-Point Hurts Everyone Everywhere + Prof. W. Kahan and Joseph D. Darcy ++ + + The Numerical Analyst as Computer Science Curmudgeon + by Prof. W. Kahan +
+
+ "Branch Cuts for Complex Elementary Functions,
+ or Much Ado About Nothing's Sign Bit"
+ by W. Kahan, ch. |
-
-Home
-| Search
-| D
-| Comments
-
-|
-
- - -D vs C/C++/C#/Java -Rationale for Builtins -Converting C to D -Converting C++ to D -The C Preprocessor vs D -D strings vs C++ std::string -D complex vs C++ std::complex -D Contract Programming vs C++ - - |
-
-
-
-D's Contract Programming vs C++'s- - Many people have written me saying that D's Contract Programming - (DbC) does not add anything that C++ does not already support. - They go on to illustrate their point with a technique for doing DbC in - C++. -- - It makes sense to review what DbC is, how it is done in D, - and stack that up with what each of the various C++ DbC techniques - can do. - - - Digital Mars C++ adds - extensions to C++ - to support DbC, but they are not covered here because they are not - part of standard C++ and are not supported by any other C++ compiler. - - Contract Programming in D- - This is more fully documented in the D - Contract Programming document. - To sum up, DbC in D has the following characteristics: - -
Contract Programming in C++- -The assert Macro- - C++ does have the basic assert macro, which tests its argument - and if it fails, aborts the program. assert can be turned - on and off with the NDEBUG macro. -- - assert does not know anything about class invariants, - and does not throw an exception when it fails. It just aborts - the program after writing a message. assert relies on - a macro text preprocessor to work. - - - assert is where explicit support for DbC in Standard C++ - begins and ends. - - Class Invariants- - Consider a class invariant in D: - -
- class A
- {
- invariant() { ...contracts... }
-
- this() { ... } // constructor
- ~this() { ... } // destructor
-
- void foo() { ... } // public member function
- }
-
- class B : A
- {
- invariant() { ...contracts... }
- ...
- }
-
-
- To accomplish the equivalent in C++ (thanks to Bob Bell for providing
- this):
-
- - template- - There's an additional complication with A::foo(). Upon every - normal exit from the function, the invariant() should be - called. - This means that code that looks like: - -
- int A::foo()
- {
- ...
- if (...)
- return bar();
- return 3;
- }
-
-
- would need to be written as:
-
-
- int A::foo()
- {
- int result;
- check_invariant(*this);
- ...
- if (...)
- {
- result = bar();
- check_invariant(*this);
- return result;
- }
- check_invariant(*this);
- return 3;
- }
-
-
- Or recode the function so it has a single exit point.
- One possibility to mitigate this is to use RAII techniques:
-
-
- int A::foo()
- {
- #if DBC
- struct Sentry {
- Sentry(A& iA) : mA(iA) { check_invariants(iA); }
- ~Sentry() { check_invariants(mA); }
- A& mA;
- } sentry(*this);
- #endif
- ...
- if (...)
- return bar();
- return 3;
- }
-
-
- The #if DBC is still there because some compilers may not
- optimize the whole thing away if check_invariants compiles to nothing.
-
-Preconditions and Postconditions- - Consider the following in D: - -
- void foo()
- in { ...preconditions... }
- out { ...postconditions... }
- body
- {
- ...implementation...
- }
-
-
- This is nicely handled in C++ with the nested Sentry struct:
-
-
- void foo()
- {
- struct Sentry
- { Sentry() { ...preconditions... }
- ~Sentry() { ...postconditions... }
- } sentry;
- ...implementation...
- }
-
-
- If the preconditions and postconditions consist of nothing
- more than assert macros, the whole doesn't need to
- be wrapped in a #ifdef pair, since a good C++ compiler will
- optimize the whole thing away if the asserts are turned off.
- - - But suppose foo() sorts an array, and the postcondition needs - to walk the array and verify that it really is sorted. Now - the shebang needs to be wrapped in #ifdef: - -
- void foo()
- {
- #ifdef DBC
- struct Sentry
- { Sentry() { ...preconditions... }
- ~Sentry() { ...postconditions... }
- } sentry;
- #endif
- ...implementation...
- }
-
-
- (One can make use of the C++ rule that templates are only
- instantiated when used can be used to avoid the #ifdef, by
- putting the conditions into a template function referenced
- by the assert.)
- - - Let's add a return value to foo() that needs to be checked in - the postconditions. In D: - -
- int foo()
- in { ...preconditions... }
- out (result) { ...postconditions... }
- body
- {
- ...implementation...
- if (...)
- return bar();
- return 3;
- }
-
-
- In C++:
-
-
- int foo()
- {
- #ifdef DBC
- struct Sentry
- { int result;
- Sentry() { ...preconditions... }
- ~Sentry() { ...postconditions... }
- } sentry;
- #endif
- ...implementation...
- if (...)
- { int i = bar();
- #ifdef DBC
- sentry.result = i;
- #endif
- return i;
- }
- #ifdef DBC
- sentry.result = 3;
- #endif
- return 3;
- }
-
-
- Now add a couple parameters to foo(). In D:
-
-
- int foo(int a, int b)
- in { ...preconditions... }
- out (result) { ...postconditions... }
- body
- {
- ...implementation...
- if (...)
- return bar();
- return 3;
- }
-
-
- In C++:
-
-
- int foo(int a, int b)
- {
- #ifdef DBC
- struct Sentry
- { int a, b;
- int result;
- Sentry(int a, int b)
- { this->a = a;
- this->b = b;
- ...preconditions...
- }
- ~Sentry() { ...postconditions... }
- } sentry(a,b);
- #endif
- ...implementation...
- if (...)
- { int i = bar();
- #ifdef DBC
- sentry.result = i;
- #endif
- return i;
- }
- #ifdef DBC
- sentry.result = 3;
- #endif
- return 3;
- }
-
-
-Preconditions and Postconditions for Member Functions- - Consider the use of preconditions and postconditions for a - polymorphic function in D: - -
- class A
- {
- void foo()
- in { ...Apreconditions... }
- out { ...Apostconditions... }
- body
- {
- ...implementation...
- }
- }
-
- class B : A
- {
- void foo()
- in { ...Bpreconditions... }
- out { ...Bpostconditions... }
- body
- {
- ...implementation...
- }
- }
-
-
- The semantics for a call to B.foo() are:
-
-
- class A
- {
- protected:
- #if DBC
- int foo_preconditions() { ...Apreconditions... }
- void foo_postconditions() { ...Apostconditions... }
- #else
- int foo_preconditions() { return 1; }
- void foo_postconditions() { }
- #endif
-
- void foo_internal()
- {
- ...implementation...
- }
-
- public:
- virtual void foo()
- {
- foo_preconditions();
- foo_internal();
- foo_postconditions();
- }
- };
-
- class B : A
- {
- protected:
- #if DBC
- int foo_preconditions() { ...Bpreconditions... }
- void foo_postconditions() { ...Bpostconditions... }
- #else
- int foo_preconditions() { return 1; }
- void foo_postconditions() { }
- #endif
-
- void foo_internal()
- {
- ...implementation...
- }
-
- public:
- virtual void foo()
- {
- assert(foo_preconditions() || A::foo_preconditions());
- foo_internal();
- A::foo_postconditions();
- foo_postconditions();
- }
- };
-
-
- Something interesting has happened here. The preconditions can
- no longer be done using assert, since the results need
- to be OR'd together. I'll leave as a reader exercise adding
- in a class invariant, function return values for foo(),
- and parameters
- for foo().
-
-Conclusion- - These C++ techniques can work up to a point. But, aside from - assert, they are not standardized and so will vary from - project to project. Furthermore, they require much tedious - adhesion to a particular convention, and add significant clutter - to the code. Perhaps that's why it's rarely seen in practice. -- - By adding support for DbC into the language, D offers an easy - way to use DbC and get it right. Being in the language standardizes - the way it will be used from project to project. - - References- - Chapter C.11 introduces the theory and rationale of - Contract Programming in - - Object-Oriented Software Construction -- Bertrand Meyer, Prentice Hall -
-
- Chapters 24.3.7.1 to 24.3.7.3 discuss Contract Programming in C++ in
-
- The C++ Programming Language Special Edition
- - - |
Add feedback and comments regarding this - page.
- -|
+
+
+
+
+ + +· D vs C/C++/C#/Java + + +· Rationale for Builtins + + +· Converting C to D + + +· Converting C++ to D + + +· The C Preprocessor vs D + + +· D strings vs C++ std::string + + +· D complex vs C++ std::complex + + +· D Contract Programming vs C++ + + + + |
+
+
+D's Contract Programming vs C++'s+ + Many people have written me saying that D's Contract Programming + (DbC) does not add anything that C++ does not already support. + They go on to illustrate their point with a technique for doing DbC in + C++. ++ + It makes sense to review what DbC is, how it is done in D, + and stack that up with what each of the various C++ DbC techniques + can do. + + + Digital Mars C++ adds + extensions to C++ + to support DbC, but they are not covered here because they are not + part of standard C++ and are not supported by any other C++ compiler. + + Contract Programming in D+ + This is more fully documented in the D + Contract Programming document. + To sum up, DbC in D has the following characteristics: + +
Contract Programming in C+++ +The assert Macro+ + C++ does have the basic assert macro, which tests its argument + and if it fails, aborts the program. assert can be turned + on and off with the NDEBUG macro. ++ + assert does not know anything about class invariants, + and does not throw an exception when it fails. It just aborts + the program after writing a message. assert relies on + a macro text preprocessor to work. + + + assert is where explicit support for DbC in Standard C++ + begins and ends. + + Class Invariants+ + Consider a class invariant in D: + +class A +{ + invariant() { ...contracts... } + + this() { ... } // constructor + ~this() { ... } // destructor + + void foo() { ... } // public member function +} + +class B : A +{ + invariant() { ...contracts... } + ... +} ++ + To accomplish the equivalent in C++ (thanks to Bob Bell for providing + this): + + template+ + There's an additional complication with A::foo(). Upon every + normal exit from the function, the invariant() should be + called. + This means that code that looks like: + + int A::foo()
+{
+ ...
+ if (...)
+ return bar();
+ return 3;
+}
+
+
+ would need to be written as:
+
+int A::foo()
+{
+ int result;
+ check_invariant(*this);
+ ...
+ if (...)
+ {
+ result = bar();
+ check_invariant(*this);
+ return result;
+ }
+ check_invariant(*this);
+ return 3;
+}
+
+
+ Or recode the function so it has a single exit point.
+ One possibility to mitigate this is to use RAII techniques:
+
+int A::foo()
+{
+#if DBC
+ struct Sentry {
+ Sentry(A& iA) : mA(iA) { check_invariants(iA); }
+ ~Sentry() { check_invariants(mA); }
+ A& mA;
+ } sentry(*this);
+#endif
+ ...
+ if (...)
+ return bar();
+ return 3;
+}
+
+
+ The #if DBC is still there because some compilers may not
+ optimize the whole thing away if check_invariants compiles to nothing.
+
+Preconditions and Postconditions+ + Consider the following in D: + +void foo() + in { ...preconditions... } + out { ...postconditions... } + body + { + ...implementation... + } ++ + This is nicely handled in C++ with the nested Sentry struct: + + void foo()
+{
+ struct Sentry
+ { Sentry() { ...preconditions... }
+ ~Sentry() { ...postconditions... }
+ } sentry;
+ ...implementation...
+}
+
+
+ If the preconditions and postconditions consist of nothing
+ more than assert macros, the whole doesn't need to
+ be wrapped in a #ifdef pair, since a good C++ compiler will
+ optimize the whole thing away if the asserts are turned off.
+ + + But suppose foo() sorts an array, and the postcondition needs + to walk the array and verify that it really is sorted. Now + the shebang needs to be wrapped in #ifdef: + + void foo()
+{
+#ifdef DBC
+ struct Sentry
+ { Sentry() { ...preconditions... }
+ ~Sentry() { ...postconditions... }
+ } sentry;
+#endif
+ ...implementation...
+}
+
+
+ (One can make use of the C++ rule that templates are only
+ instantiated when used can be used to avoid the #ifdef, by
+ putting the conditions into a template function referenced
+ by the assert.)
+ + + Let's add a return value to foo() that needs to be checked in + the postconditions. In D: + + int foo() + in { ...preconditions... } + out (result) { ...postconditions... } + body + { + ...implementation... + if (...) + return bar(); + return 3; + } ++ + In C++: + + int foo()
+{
+#ifdef DBC
+ struct Sentry
+ { int result;
+ Sentry() { ...preconditions... }
+ ~Sentry() { ...postconditions... }
+ } sentry;
+#endif
+ ...implementation...
+ if (...)
+ { int i = bar();
+#ifdef DBC
+ sentry.result = i;
+#endif
+ return i;
+ }
+#ifdef DBC
+ sentry.result = 3;
+#endif
+ return 3;
+}
+
+
+ Now add a couple parameters to foo(). In D:
+
+int foo(int a, int b) + in { ...preconditions... } + out (result) { ...postconditions... } + body + { + ...implementation... + if (...) + return bar(); + return 3; + } ++ + In C++: + + int foo(int a, int b)
+{
+#ifdef DBC
+ struct Sentry
+ { int a, b;
+ int result;
+ Sentry(int a, int b)
+ { this->a = a;
+ this->b = b;
+ ...preconditions...
+ }
+ ~Sentry() { ...postconditions... }
+ } sentry(a,b);
+#endif
+ ...implementation...
+ if (...)
+ { int i = bar();
+#ifdef DBC
+ sentry.result = i;
+#endif
+ return i;
+ }
+#ifdef DBC
+ sentry.result = 3;
+#endif
+ return 3;
+}
+
+
+Preconditions and Postconditions for Member Functions+ + Consider the use of preconditions and postconditions for a + polymorphic function in D: + +class A +{ + void foo() + in { ...Apreconditions... } + out { ...Apostconditions... } + body + { + ...implementation... + } +} + +class B : A +{ + void foo() + in { ...Bpreconditions... } + out { ...Bpostconditions... } + body + { + ...implementation... + } +} ++ + The semantics for a call to B.foo() are: + +
class A
+{
+protected:
+ #if DBC
+ int foo_preconditions() { ...Apreconditions... }
+ void foo_postconditions() { ...Apostconditions... }
+ #else
+ int foo_preconditions() { return 1; }
+ void foo_postconditions() { }
+ #endif
+
+ void foo_internal()
+ {
+ ...implementation...
+ }
+
+public:
+ virtual void foo()
+ {
+ foo_preconditions();
+ foo_internal();
+ foo_postconditions();
+ }
+};
+
+class B : A
+{
+protected:
+ #if DBC
+ int foo_preconditions() { ...Bpreconditions... }
+ void foo_postconditions() { ...Bpostconditions... }
+ #else
+ int foo_preconditions() { return 1; }
+ void foo_postconditions() { }
+ #endif
+
+ void foo_internal()
+ {
+ ...implementation...
+ }
+
+public:
+ virtual void foo()
+ {
+ assert(foo_preconditions() || A::foo_preconditions());
+ foo_internal();
+ A::foo_postconditions();
+ foo_postconditions();
+ }
+};
+
+
+ Something interesting has happened here. The preconditions can
+ no longer be done using assert, since the results need
+ to be OR'd together. I'll leave as a reader exercise adding
+ in a class invariant, function return values for foo(),
+ and parameters
+ for foo().
+
+Conclusion+ + These C++ techniques can work up to a point. But, aside from + assert, they are not standardized and so will vary from + project to project. Furthermore, they require much tedious + adhesion to a particular convention, and add significant clutter + to the code. Perhaps that's why it's rarely seen in practice. ++ + By adding support for DbC into the language, D offers an easy + way to use DbC and get it right. Being in the language standardizes + the way it will be used from project to project. + + References+ + Chapter C.11 introduces the theory and rationale of + Contract Programming in + + Object-Oriented Software Construction ++ Bertrand Meyer, Prentice Hall +
+
+ Chapters 24.3.7.1 to 24.3.7.3 discuss Contract Programming in C++ in
+
+ The C++ Programming Language Special Edition
+ + + + |
-
-Home
-| Search
-| D
-| Comments
-
-|
-
- - -D vs C/C++/C#/Java -Rationale for Builtins -Converting C to D -Converting C++ to D -The C Preprocessor vs D -D strings vs C++ std::string -D complex vs C++ std::complex -D Contract Programming vs C++ - - |
-
-
-
-D Strings vs C++ Strings- - -Why have strings built-in to the core language of D rather than entirely in -a library as in C++ Strings? What's the point? Where's the improvement? - -Concatenation Operator- - C++ Strings are stuck with overloading existing operators. The - obvious choice for concatenation is += and +. - But someone just looking at the code will see + and think "addition". - He'll have to look up the types (and types are frequently buried - behind multiple typedef's) to see that it's a string type, and - it's not adding strings but concatenating them. -- - Additionally, if one has an array of floats, is '+' overloaded to - be the same as a vector addition, or an array concatenation? - - - In D, these problems are avoided by introducing a new binary - operator ~ as the concatenation operator. It works with - arrays (of which strings are a subset). ~= is the corresponding - append operator. ~ on arrays of floats would concatenate them, - + would imply a vector add. Adding a new operator makes it possible - for orthogonality and consistency in the treatment of arrays. - (In D, strings are simply arrays of characters, not a special - type.) - - Interoperability With C String Syntax- - Overloading of operators only really works if one of the operands - is overloadable. So the C++ string class cannot consistently - handle arbitrary expressions containing strings. Consider: - -- const char abc[5] = "world"; - string str = "hello" + abc; - -- - That isn't going to work. But it does work when the core language - knows about strings: - - - const char[5] abc = "world"; - char[] str = "hello" ~ abc; - -- - Consistency With C String Syntax- - There are three ways to find the length of a string in C++: - -- const char abc[] = "world"; : sizeof(abc)/sizeof(abc[0])-1 - : strlen(abc) - string str : str.length() - -- - That kind of inconsistency makes it hard to write generic templates. - Consider D: - - - char[5] abc = "world"; : abc.length - char[] str : str.length - -- - Checking For Empty Strings- - C++ strings use a function to determine if a string is empty: -- string str; - if (str.empty()) - // string is empty -- - In D, an empty string has zero length: - - - char[] str; - if (!str.length) - // string is empty -- - - Resizing Existing String- - C++ handles this with the resize() member function: - -- string str; - str.resize(newsize); -- - D takes advantage of knowing that str is a string, and - so resizing it is just changing the length property: - - - char[] str; - str.length = newsize; -- - Slicing a String- - C++ slices an existing string using a special constructor: - -- string s1 = "hello world"; - string s2(s1, 6, 5); // s2 is "world" -- - D has the array slice syntax, not possible with C++: - - - char[] s1 = "hello world"; - char[] s2 = s1[6 .. 11]; // s2 is "world" -- - Slicing, of course, works with any array in D, not just strings. - - Copying a String- - C++ copies strings with the replace function: - -- string s1 = "hello world"; - string s2 = "goodbye "; - s2.replace(8, 5, s1, 6, 5); // s2 is "goodbye world" -- - D uses the slice syntax as an lvalue: - - - char[] s1 = "hello world"; - char[] s2 = "goodbye "; - s2[8..13] = s1[6..11]; // s2 is "goodbye world" -- - Conversions to C Strings- - This is needed for compatibility with C API's. In C++, this - uses the c_str() member function: - -- void foo(const char *); - string s1; - foo(s1.c_str()); -- - In D, strings can be implicitly converted to char*: - - - void foo(char *); - char[] s1; - foo(s1); -- - Note: some will argue that it is a mistake in D to have an implicit - conversion from char[] to char*. - - Array Bounds Checking- - In C++, string array bounds checking for [] is not done. - In D, array bounds checking is on by default and it can be turned off - with a compiler switch after the program is debugged. - -String Switch Statements- - Are not possible in C++, nor is there any way to add them - by adding more to the library. In D, they take the obvious - syntactical forms: - -
- switch (str)
- {
- case "hello":
- case "world":
- ...
- }
-
-
- where str can be any of literal "string"s, fixed string arrays
- like char[10], or dynamic strings like char[]. A quality implementation
- can, of course, explore many strategies of efficiently implementing
- this based on the contents of the case strings.
-
-Filling a String- - In C++, this is done with the replace() member function: - -- string str = "hello"; - str.replace(1,2,2,'?'); // str is "h??lo" -- - In D, use the array slicing syntax in the natural manner: - - - char[] str = "hello"; - str[1..2] = '?'; // str is "h??lo" -- - Value vs Reference- - C++ strings, as implemented by STLport, are by value and are - 0-terminated. [The latter is an implementation choice, but - STLport seems to be the most popular implementation.] - This, coupled with no garbage collection, has - some consequences. First of all, any string created must make - its own copy of the string data. The 'owner' of the string - data must be kept track of, because when the owner is deleted - all references become invalid. If one tries to avoid the - dangling reference problem by treating strings as value types, - there will be a lot of overhead of memory allocation, - data copying, and memory deallocation. Next, the 0-termination - implies that strings cannot refer to other strings. String - data in the data segment, stack, etc., cannot - be referred to. -- - D strings are reference types, and the memory is garbage collected. - This means that only references need to be copied, not the - string data. D strings can refer to data in the static data - segment, data on the stack, data inside other strings, objects, - file buffers, etc. There's no need to keep track of the 'owner' - of the string data. - - - The obvious question is if multiple D strings refer to the same - string data, what happens if the data is modified? All the - references will now point to the modified data. This can have - its own consequences, which can be avoided if the copy-on-write - convention is followed. All copy-on-write is is that if - a string is written to, an actual copy of the string data is made - first. - - - The result of D strings being reference only and garbage collected - is that code that does a lot of string manipulating, such as - an lzw compressor, can be a lot more efficient in terms of both - memory consumption and speed. - - Benchmark- - Let's take a look at a small utility, wordcount, that counts up - the frequency of each word in a text file. In D, it looks like this: - -
- import file;
-
- int main (char[][] args)
- {
- int w_total;
- int l_total;
- int c_total;
- int[char[]] dictionary;
-
- printf(" lines words bytes file\n");
- for (int i = 1; i < args.length; ++i)
- {
- char[] input;
- int w_cnt, l_cnt, c_cnt;
- int inword;
- int wstart;
-
- input = cast(char[])file.read(args[i]);
-
- for (int j = 0; j < input.length; j++)
- { char c;
-
- c = input[j];
- if (c == '\n')
- ++l_cnt;
- if (c >= '0' && c <= '9')
- {
- }
- else if (c >= 'a' && c <= 'z' ||
- c >= 'A' && c <= 'Z')
- {
- if (!inword)
- {
- wstart = j;
- inword = 1;
- ++w_cnt;
- }
- }
- else if (inword)
- { char[] word = input[wstart .. j];
-
- dictionary[word]++;
- inword = 0;
- }
- ++c_cnt;
- }
- if (inword)
- { char[] w = input[wstart .. input.length];
- dictionary[w]++;
- }
- printf("%8lu%8lu%8lu %.*s\n", l_cnt, w_cnt, c_cnt, args[i]);
- l_total += l_cnt;
- w_total += w_cnt;
- c_total += c_cnt;
- }
-
- if (args.length > 2)
- {
- printf("--------------------------------------\n%8lu%8lu%8lu total",
- l_total, w_total, c_total);
- }
-
- printf("--------------------------------------\n");
-
- foreach (char[] word1; dictionary.keys.sort)
- {
- printf("%3d %.*s\n", dictionary[word1], word1);
- }
- return 0;
- }
-
-
- Two people have written C++ implementations using the C++ standard
- template library,
- wccpp1
- and
- wccpp2.
- The input file
- alice30.txt
- is the text of "Alice in Wonderland."
- The D compiler,
- dmd,
- and the C++ compiler,
- dmc,
- share the same
- optimizer and code generator, which provides a more apples to
- apples comparison of the efficiency of the semantics of the languages
- rather than the optimization and code generator sophistication.
- Tests were run on a Win XP machine. dmc uses STLport for the template
- implementation.
- - -
- - The following tests were run on linux, again comparing a D compiler - (gdc) - and a C++ compiler (g++) that share a common optimizer and - code generator. The system is Pentium III 800MHz running RedHat Linux 8.0 - and gcc 3.4.2. - The Digital Mars D compiler for linux (dmd) - is included for comparison. - - - -
- - These tests compare gdc with g++ on a PowerMac G5 2x2.0GHz - running MacOS X 10.3.5 and gcc 3.4.2. (Timings are a little - less accurate.) - - -
- wccpp2 by Allan Odgaard- -
- #include <algorithm>
- #include <cstdio>
- #include <fstream>
- #include <iterator>
- #include <map>
- #include <vector>
-
- bool isWordStartChar (char c) { return isalpha(c); }
- bool isWordEndChar (char c) { return !isalnum(c); }
-
- int main (int argc, char const* argv[])
- {
- using namespace std;
- printf("Lines Words Bytes File:\n");
-
- map<string, int> dict;
- int tLines = 0, tWords = 0, tBytes = 0;
- for(int i = 1; i < argc; i++)
- {
- ifstream file(argv[i]);
- istreambuf_iterator<char> from(file.rdbuf()), to;
- vector<char> v(from, to);
- vector<char>::iterator first = v.begin(), last = v.end(), bow, eow;
-
- int numLines = count(first, last, '\n');
- int numWords = 0;
- int numBytes = last - first;
-
- for(eow = first; eow != last; )
- {
- bow = find_if(eow, last, isWordStartChar);
- eow = find_if(bow, last, isWordEndChar);
- if(bow != eow)
- ++dict[string(bow, eow)], ++numWords;
- }
-
- printf("%5d %5d %5d %s\n", numLines, numWords, numBytes, argv[i]);
-
- tLines += numLines;
- tWords += numWords;
- tBytes += numBytes;
- }
-
- if(argc > 2)
- printf("-----------------------\n%5d %5d %5d\n", tLines, tWords, tBytes);
- printf("-----------------------\n\n");
-
- for(map<string, int>::const_iterator it = dict.begin(); it != dict.end(); ++it)
- printf("%5d %s\n", it->second, it->first.c_str());
-
- return 0;
- }
-
-
- |
Add feedback and comments regarding this - page.
- -|
+
+
+
+
+ + +· D vs C/C++/C#/Java + + +· Rationale for Builtins + + +· Converting C to D + + +· Converting C++ to D + + +· The C Preprocessor vs D + + +· D strings vs C++ std::string + + +· D complex vs C++ std::complex + + +· D Contract Programming vs C++ + + + + |
+
+
+D Strings vs C++ Strings+ + +Why have strings built-in to the core language of D rather than entirely in +a library as in C++ Strings? What's the point? Where's the improvement? + +Concatenation Operator+ + C++ Strings are stuck with overloading existing operators. The + obvious choice for concatenation is += and +. + But someone just looking at the code will see + and think "addition". + He'll have to look up the types (and types are frequently buried + behind multiple typedef's) to see that it's a string type, and + it's not adding strings but concatenating them. ++ + Additionally, if one has an array of floats, is '+' overloaded to + be the same as a vector addition, or an array concatenation? + + + In D, these problems are avoided by introducing a new binary + operator ~ as the concatenation operator. It works with + arrays (of which strings are a subset). ~= is the corresponding + append operator. ~ on arrays of floats would concatenate them, + + would imply a vector add. Adding a new operator makes it possible + for orthogonality and consistency in the treatment of arrays. + (In D, strings are simply arrays of characters, not a special + type.) + + Interoperability With C String Syntax+ + Overloading of operators only really works if one of the operands + is overloadable. So the C++ string class cannot consistently + handle arbitrary expressions containing strings. Consider: + +const char abc[5] = "world"; + string str = "hello" + abc; ++ + That isn't going to work. But it does work when the core language + knows about strings: + + const char[5] abc = "world"; + char[] str = "hello" ~ abc; ++ + Consistency With C String Syntax+ + There are three ways to find the length of a string in C++: + +const char abc[] = "world"; : sizeof(abc)/sizeof(abc[0])-1 + : strlen(abc) + string str : str.length() ++ + That kind of inconsistency makes it hard to write generic templates. + Consider D: + + char[5] abc = "world"; : abc.length + char[] str : str.length ++ + Checking For Empty Strings+ + C++ strings use a function to determine if a string is empty: + +string str; + if (str.empty()) + // string is empty ++ + In D, an empty string has zero length: + + char[] str; + if (!str.length) + // string is empty ++ + + Resizing Existing String+ + C++ handles this with the resize() member function: + +string str; + str.resize(newsize); ++ + D takes advantage of knowing that str is a string, and + so resizing it is just changing the length property: + + char[] str;
+ str.length = newsize;
+
+
+Slicing a String+ + C++ slices an existing string using a special constructor: + +string s1 = "hello world"; + string s2(s1, 6, 5); // s2 is "world" ++ + D has the array slice syntax, not possible with C++: + + char[] s1 = "hello world"; + char[] s2 = s1[6 .. 11]; // s2 is "world" ++ + Slicing, of course, works with any array in D, not just strings. + + Copying a String+ + C++ copies strings with the replace function: + +string s1 = "hello world"; + string s2 = "goodbye "; + s2.replace(8, 5, s1, 6, 5); // s2 is "goodbye world" ++ + D uses the slice syntax as an lvalue: + + char[] s1 = "hello world"; + char[] s2 = "goodbye "; + s2[8..13] = s1[6..11]; // s2 is "goodbye world" ++ + Conversions to C Strings+ + This is needed for compatibility with C API's. In C++, this + uses the c_str() member function: + +void foo(const char *); + string s1; + foo(s1.c_str()); ++ + In D, strings can be implicitly converted to char*: + + void foo(char *); + char[] s1; + foo(s1); ++ + Note: some will argue that it is a mistake in D to have an implicit + conversion from char[] to char*. + + Array Bounds Checking+ + In C++, string array bounds checking for [] is not done. + In D, array bounds checking is on by default and it can be turned off + with a compiler switch after the program is debugged. + +String Switch Statements+ + Are not possible in C++, nor is there any way to add them + by adding more to the library. In D, they take the obvious + syntactical forms: + +switch (str) + { + case "hello": + case "world": + ... + } ++ + where str can be any of literal "string"s, fixed string arrays + like char[10], or dynamic strings like char[]. A quality implementation + can, of course, explore many strategies of efficiently implementing + this based on the contents of the case strings. + + Filling a String+ + In C++, this is done with the replace() member function: + +string str = "hello"; + str.replace(1,2,2,'?'); // str is "h??lo" ++ + In D, use the array slicing syntax in the natural manner: + + char[] str = "hello"; + str[1..2] = '?'; // str is "h??lo" ++ + Value vs Reference+ + C++ strings, as implemented by STLport, are by value and are + 0-terminated. [The latter is an implementation choice, but + STLport seems to be the most popular implementation.] + This, coupled with no garbage collection, has + some consequences. First of all, any string created must make + its own copy of the string data. The 'owner' of the string + data must be kept track of, because when the owner is deleted + all references become invalid. If one tries to avoid the + dangling reference problem by treating strings as value types, + there will be a lot of overhead of memory allocation, + data copying, and memory deallocation. Next, the 0-termination + implies that strings cannot refer to other strings. String + data in the data segment, stack, etc., cannot + be referred to. ++ + D strings are reference types, and the memory is garbage collected. + This means that only references need to be copied, not the + string data. D strings can refer to data in the static data + segment, data on the stack, data inside other strings, objects, + file buffers, etc. There's no need to keep track of the 'owner' + of the string data. + + + The obvious question is if multiple D strings refer to the same + string data, what happens if the data is modified? All the + references will now point to the modified data. This can have + its own consequences, which can be avoided if the copy-on-write + convention is followed. All copy-on-write is is that if + a string is written to, an actual copy of the string data is made + first. + + + The result of D strings being reference only and garbage collected + is that code that does a lot of string manipulating, such as + an lzw compressor, can be a lot more efficient in terms of both + memory consumption and speed. + + Benchmark+ + Let's take a look at a small utility, wordcount, that counts up + the frequency of each word in a text file. In D, it looks like this: + +import file; + +int main (char[][] args) +{ + int w_total; + int l_total; + int c_total; + int[char[]] dictionary; + + printf(" lines words bytes file\n"); + for (int i = 1; i < args.length; ++i) + { + char[] input; + int w_cnt, l_cnt, c_cnt; + int inword; + int wstart; + + input = cast(char[])file.read(args[i]); + + for (int j = 0; j < input.length; j++) + { char c; + + c = input[j]; + if (c == '\n') + ++l_cnt; + if (c >= '0' && c <= '9') + { + } + else if (c >= 'a' && c <= 'z' || + c >= 'A' && c <= 'Z') + { + if (!inword) + { + wstart = j; + inword = 1; + ++w_cnt; + } + } + else if (inword) + { char[] word = input[wstart .. j]; + + dictionary[word]++; + inword = 0; + } + ++c_cnt; + } + if (inword) + { char[] w = input[wstart .. input.length]; + dictionary[w]++; + } + printf("%8lu%8lu%8lu %.*s\n", l_cnt, w_cnt, c_cnt, args[i]); + l_total += l_cnt; + w_total += w_cnt; + c_total += c_cnt; + } + + if (args.length > 2) + { + printf("--------------------------------------\n%8lu%8lu%8lu total", + l_total, w_total, c_total); + } + + printf("--------------------------------------\n"); + + foreach (char[] word1; dictionary.keys.sort) + { + printf("%3d %.*s\n", dictionary[word1], word1); + } + return 0; +} ++ + Two people have written C++ implementations using the C++ standard + template library, + wccpp1 + and + wccpp2. + The input file + alice30.txt + is the text of "Alice in Wonderland." + The D compiler, + dmd, + and the C++ compiler, + dmc, + share the same + optimizer and code generator, which provides a more apples to + apples comparison of the efficiency of the semantics of the languages + rather than the optimization and code generator sophistication. + Tests were run on a Win XP machine. dmc uses STLport for the template + implementation. + + +
The following tests were run on linux, again comparing a D compiler + (gdc) + and a C++ compiler (g++) that share a common optimizer and + code generator. The system is Pentium III 800MHz running RedHat Linux 8.0 + and gcc 3.4.2. + The Digital Mars D compiler for linux (dmd) + is included for comparison. + + + +
These tests compare gdc with g++ on a PowerMac G5 2x2.0GHz + running MacOS X 10.3.5 and gcc 3.4.2. (Timings are a little + less accurate.) + + +
+ wccpp2 by Allan Odgaard+ +#include <algorithm>
+#include <cstdio>
+#include <fstream>
+#include <iterator>
+#include <map>
+#include <vector>
+
+bool isWordStartChar (char c) { return isalpha(c); }
+bool isWordEndChar (char c) { return !isalnum(c); }
+
+int main (int argc, char const* argv[])
+{
+ using namespace std;
+ printf("Lines Words Bytes File:\n");
+
+ map<string, int> dict;
+ int tLines = 0, tWords = 0, tBytes = 0;
+ for(int i = 1; i < argc; i++)
+ {
+ ifstream file(argv[i]);
+ istreambuf_iterator<char> from(file.rdbuf()), to;
+ vector<char> v(from, to);
+ vector<char>::iterator first = v.begin(), last = v.end(), bow, eow;
+
+ int numLines = count(first, last, '\n');
+ int numWords = 0;
+ int numBytes = last - first;
+
+ for(eow = first; eow != last; )
+ {
+ bow = find_if(eow, last, isWordStartChar);
+ eow = find_if(bow, last, isWordEndChar);
+ if(bow != eow)
+ ++dict[string(bow, eow)], ++numWords;
+ }
+
+ printf("%5d %5d %5d %s\n", numLines, numWords, numBytes, argv[i]);
+
+ tLines += numLines;
+ tWords += numWords;
+ tBytes += numBytes;
+ }
+
+ if(argc > 2)
+ printf("-----------------------\n%5d %5d %5d\n", tLines, tWords, tBytes);
+ printf("-----------------------\n\n");
+
+ for(map<string, int>::const_iterator it = dict.begin(); it != dict.end(); ++it)
+ printf("%5d %s\n", it->second, it->first.c_str());
+
+ return 0;
+}
+
+
+
+ |
-
-Home
-| Search
-| D
-| Comments
-
-|
-
- - -D vs C/C++/C#/Java -Rationale for Builtins -Converting C to D -Converting C++ to D -The C Preprocessor vs D -D strings vs C++ std::string -D complex vs C++ std::complex -D Contract Programming vs C++ - - |
-
-
-
-Programming in D for C++ Programmers- -
-
-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: Programming in D for C Programmers - -
- - Defining constructors- -The C++ Way- - Constructors have the same name as the class: - -
- class Foo
- {
- Foo(int x);
- };
-
-The D Way- - Constructors are defined with the this keyword: - -
- class Foo
- {
- this(int x) { }
- }
-
- which reflects how they are used in D.
-
-- Base class initialization- -The C++ Way- - Base constructors are called using the base initializer syntax. - -
- class A { A() {... } };
- class B : A
- {
- B(int x)
- : A() // call base constructor
- { ...
- }
- };
-
-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;
- }
- }
-
-- Comparing structs- -The C++ Way- - While C++ defines struct assignment in a simple, convenient manner: - -- struct A x, y; - ... - x = y; -- - it does not for struct comparisons. Hence, to compare two struct - instances for equality: - -
- #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)
- ...
-
-
- 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.
- - - There's a nasty bug lurking in the memcmp() implementation of operator==(). - The layout of a struct, due to alignment, can have '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. - - - 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++. - - The D Way- - D does it the obvious, straightforward way: - -- A x, y; - ... - if (x == y) - ... -- - - Creating a new typedef'd type- -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. - -- #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. - -
- #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)
- ...
-
-
-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. - - - Friends- -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 friend declarations: - -
- 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; }
-
-
-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 private attribute prevents other modules from
- accessing the members.
-
-- Operator overloading- -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: - -
- 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.
-
-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);
- }
-
-
- The compiler automatically interprets all the
- <, <=, > and >=
- operators in terms of the cmp function, as well
- as handling the cases where the left operand is not an
- object reference.
- - - 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. - - - Namespace using declarations- -The C++ Way- - A using-declaration in C++ is used to bring a name from - a namespace scope into the current scope: - -
- namespace Foo
- {
- int x;
- }
- using Foo::x;
-
-
-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 Foo.x 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. - - - RAII (Resource Acquisition Is Initialization)- -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: - -
- class File
- { Handle *h;
-
- ~File()
- {
- h->release();
- }
- };
-
-
-The D Way- - 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 synchronized - declarations and statements. -- - The few RAII issues left are handled by auto classes. - Auto classes get their destructors run when they go out of scope. - -
- auto class File
- { Handle h;
-
- ~this()
- {
- h.release();
- }
- }
-
- void test()
- {
- if (...)
- { auto File f = new File();
- ...
- } // f.~this() gets run at closing brace, even if
- // scope was exited via a thrown exception
- }
-
-
-- Properties- -The C++ Way- - It is common practice to define a field, - along with object-oriented - get and set functions for it: - -
- 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.
-
-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
- void property(int newproperty) { myprop = newproperty; }
-
- // get
- int property() { return myprop; }
-
- private:
- int myprop;
- }
-
-
- which is used as:
-
- - Abc a; - a.property = 3; // equivalent to a.property(3) - int x = a.property; // equivalent to int x = a.property() -- - Thus, in D a property can be treated like it was a simple field name. - A property can start out actually being a simple field name, - but if later if 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 - '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. - - - Recursive Templates- -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: - -
- 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
- }
-
-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()
- {
- printf("%d\n", factorial!(4)); // prints 24
- }
-
-- - Meta Templates- - The problem: create a typedef for a signed integral type that is at - least nbits in size. - -The C++ Way- - This example is simplified and adapted from one written by - Dr. Carlo Pescio in - - Template Metaprogramming: Make parameterized integers portable with this novel technique. -- - 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. - -
- #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 ;
- }
-
-
-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. - -
- 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 ;
- printf("%d %d %d %d\n",
- i.sizeof, j.sizeof, k.sizeof, l.sizeof);
- return 0;
- }
-
-- - Type Traits- - Type traits are another term for being able to find out - properties of a type at compile time. - -The C++ Way- - The following template comes from - - C++ Templates: The Complete Guide, David Vandevoorde, Nicolai M. Josuttis - pg. 353 which determines if the template's argument type - is a function: - -
- 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
- (Substitution Failure Is Not An Error) principle.
- Why it works is a fairly advanced template topic.
-
-The D Way- - SFINAE can be done in D without resorting to template argument - pattern matching: - -
- template IsFunctionT(T)
- {
- static if ( is(T[]) )
- const int IsFunctionT = 1;
- else
- const int IsFunctionT = 0;
- }
-
- 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 is
- expression can test it directly:
-
-
- void test()
- {
- typedef int fp(int);
-
- assert( is(fp == function) );
- }
-
-
-
- |
Add feedback and comments regarding this - page.
- -|
+
+
+
+
+ + +· D vs C/C++/C#/Java + + +· Rationale for Builtins + + +· Converting C to D + + +· Converting C++ to D + + +· The C Preprocessor vs D + + +· D strings vs C++ std::string + + +· D complex vs C++ std::complex + + +· D Contract Programming vs C++ + + + + |
+
+
+Programming in D for C++ Programmers+ +
+
+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: Programming in D for C Programmers + +
+ + Defining constructors+ +The C++ Way+ + Constructors have the same name as the class: + +class Foo
+{
+ Foo(int x);
+};
+
+
+The D Way+ + Constructors are defined with the this keyword: + +class Foo +{ + this(int x) { } +} ++ + which reflects how they are used in D. + + + Base class initialization+ +The C++ Way+ + Base constructors are called using the base initializer syntax. + +class A { A() {... } };
+class B : A
+{
+ B(int x)
+ : A() // call base constructor
+ { ...
+ }
+};
+
+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; + } +} ++ + + Comparing structs+ +The C++ Way+ + While C++ defines struct assignment in a simple, convenient manner: + +struct A x, y; +... +x = y; ++ + it does not for struct comparisons. Hence, to compare two struct + instances for equality: + + #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)
+ ...
+
+
+ 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.
+ + + There's a nasty bug lurking in the memcmp() implementation of operator==(). + The layout of a struct, due to alignment, can have '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. + + + 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++. + + The D Way+ + D does it the obvious, straightforward way: + +A x, y;
+...
+if (x == y)
+ ...
+
+
++ Creating a new typedef'd type+ +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. + +#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. + + #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)
+ ...
+
+
+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. + + + Friends+ +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 friend declarations: + +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; }
+
+
+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 private attribute prevents other modules from + accessing the members. + + + Operator overloading+ +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: + +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.
+
+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); +} ++ + The compiler automatically interprets all the + <, <=, > and >= + operators in terms of the cmp function, as well + as handling the cases where the left operand is not an + object reference. + + + 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. + + + Namespace using declarations+ +The C++ Way+ + A using-declaration in C++ is used to bring a name from + a namespace scope into the current scope: + +namespace Foo
+{
+ int x;
+}
+using Foo::x;
+
+
+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 Foo.x 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. + + + RAII (Resource Acquisition Is Initialization)+ +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: + +class File
+{ Handle *h;
+
+ ~File()
+ {
+ h->release();
+ }
+};
+
+
+The D Way+ + 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 synchronized + declarations and statements. ++ + The few RAII issues left are handled by auto classes. + Auto classes get their destructors run when they go out of scope. + + auto class File +{ Handle h; + + ~this() + { + h.release(); + } +} + +void test() +{ + if (...) + { auto File f = new File(); + ... + } // f.~this() gets run at closing brace, even if + // scope was exited via a thrown exception +} ++ + + Properties+ +The C++ Way+ + It is common practice to define a field, + along with object-oriented + get and set functions for it: + +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.
+
+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 + void property(int newproperty) { myprop = newproperty; } + + // get + int property() { return myprop; } + + private: + int myprop; +} ++ + which is used as: + + Abc a; +a.property = 3; // equivalent to a.property(3) +int x = a.property; // equivalent to int x = a.property() ++ + Thus, in D a property can be treated like it was a simple field name. + A property can start out actually being a simple field name, + but if later if 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 + '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. + + + Recursive Templates+ +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: + +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
+}
+
+
+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() +{ + printf("%d\n", factorial!(4)); // prints 24 +} ++ + + + Meta Templates+ + The problem: create a typedef for a signed integral type that is at + least nbits in size. + +The C++ Way+ + This example is simplified and adapted from one written by + Dr. Carlo Pescio in + + Template Metaprogramming: Make parameterized integers portable with this novel technique. ++ + 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. + + #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 ;
+}
+
+
+
+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. + +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 ; + printf("%d %d %d %d\n", + i.sizeof, j.sizeof, k.sizeof, l.sizeof); + return 0; +} ++ + + + Type Traits+ + Type traits are another term for being able to find out + properties of a type at compile time. + +The C++ Way+ + The following template comes from + + C++ Templates: The Complete Guide, David Vandevoorde, Nicolai M. Josuttis + pg. 353 which determines if the template's argument type + is a function: + +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
+ (Substitution Failure Is Not An Error) principle.
+ Why it works is a fairly advanced template topic.
+
+The D Way+ + SFINAE can be done in D without resorting to template argument + pattern matching: + +template IsFunctionT(T) +{ + static if ( is(T[]) ) + const int IsFunctionT = 1; + else + const int IsFunctionT = 0; +} + +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 is + expression can test it directly: + + void test() +{ + typedef int fp(int); + + assert( is(fp == function) ); +} ++ + + + |
-
-Home
-| Search
-| D
-| Comments
-
-|
-
- - -D vs C/C++/C#/Java -Rationale for Builtins -Converting C to D -Converting C++ to D -The C Preprocessor vs D -D strings vs C++ std::string -D complex vs C++ std::complex -D Contract Programming vs C++ - - |
-
-
-
-Programming in D for C Programmers- -
-
-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.
-- -Since C does not have object-oriented features, there's a separate section -for object-oriented issues -Programming in D for C++ Programmers. - - -The C preprocessor is covered in -The C Preprocessor vs D. - -
- Getting the Size of a Type- -The C Way- -sizeof(int) - sizeof(char *) - sizeof(double) - sizeof(struct Foo)- - The D Way- -Use the size property: - -int.size - (char *).size - double.size - Foo.size- - - - Get the max and min values of a type- -The C Way- -#include <limits.h> - #include <math.h> - - CHAR_MAX - CHAR_MIN - ULONG_MAX - DBL_MIN -- - The D Way- -char.max - char.min - ulong.max - double.min -- - - - Primitive Types- -C to D types- -bool => bit - char => char - signed char => byte - unsigned char => ubyte - short => short - unsigned short => ushort - wchar_t => wchar - int => int - unsigned => uint - long => int - unsigned long => uint - long long => long - unsigned long long => ulong - float => float - double => double - long double => real - _Imaginary long double => imaginary - _Complex long double => complex - -- - Although char is an unsigned 8 bit type, and - wchar is an unsigned 16 bit type, they have their own separate types - in order to aid overloading and type safety. - - Ints and unsigneds in C are of varying size; not so in D. - - - - Special Floating Point Values- -The C Way- -#include <fp.h> - - NAN - INFINITY - - #include <float.h> - - DBL_DIG - DBL_EPSILON - DBL_MANT_DIG - DBL_MAX_10_EXP - DBL_MAX_EXP - DBL_MIN_10_EXP - DBL_MIN_EXP -- - The D Way- -double.nan - double.infinity - double.dig - double.epsilon - double.mant_dig - double.max_10_exp - double.max_exp - double.min_10_exp - double.min_exp -- - - Remainder after division of floating point numbers- -The C Way- -- #include <math.h> - - float f = fmodf(x,y); - double d = fmod(x,y); - long double e = fmodl(x,y); -- - The D Way- -D supports the remainder ('%') operator on floating point operands: - -- float f = x % y; - double d = x % y; - extended e = x % y; -- - - - Dealing with NAN's in floating point compares- -The C Way- - C doesn't define what happens if an operand to a compare - is NAN, and few C compilers check for it (the Digital Mars - C compiler is an exception, DM's compilers do check for NAN operands). - -- #include <math.h> - - if (isnan(x) || isnan(y)) - result = FALSE; - else - result = (x < y); -- - The D Way-- D offers a full complement of comparisons and operators - that work with NAN arguments. - - result = (x < y); // false if x or y is nan -- - - - Assert's are a necessary part of any good defensive coding strategy.- -The C Way--C doesn't directly support assert, but does support __FILE__ -and __LINE__ from which an assert macro can be built. In fact, -there appears to be practically no other use for __FILE__ and __LINE__. - - - #include <assert.h> - - assert(e == 0); -- - The D Way- -D simply builds assert into the language: - -- assert(e == 0); -- -[NOTE: trace functions?] - - - - Initializing all elements of an array- -The C Way- -- #define ARRAY_LENGTH 17 - int array[ARRAY_LENGTH]; - for (i = 0; i < ARRAY_LENGTH; i++) - array[i] = value; -- - The D Way- -- int array[17]; - array[] = value; -- - - - Looping through an array- -The C Way-- The array length is defined separately, or a clumsy - sizeof() expression is used to get the length. - - - #define ARRAY_LENGTH 17 - int array[ARRAY_LENGTH]; - for (i = 0; i < ARRAY_LENGTH; i++) - func(array[i]); -- -or: - - - int array[17]; - for (i = 0; i < sizeof(array) / sizeof(array[0]); i++) - func(array[i]); -- - The D Way- -The length of an array is accessible the property "length". - -- int array[17]; - for (i = 0; i < array.length; i++) - func(array[i]); -- -or even better: - - - int array[17]; - foreach (int value; array) - func(value); -- - - - - Creating an array of variable size- -The C Way- - C cannot do this with arrays. It is necessary to create a separate - variable for the length, and then explicitly manage the size of - the array: - -
- #include <stdlib.h>
-
- int array_length;
- int *array;
- int *newarray;
-
- newarray = (int *)
- realloc(array, (array_length + 1) * sizeof(int));
- if (!newarray)
- error("out of memory");
- array = newarray;
- array[array_length++] = x;
-
-
-The D Way- - D supports dynamic arrays, which can be easily resized. D supports - all the requisite memory management. - -- int[] array; - - array.length = array.length + 1; - array[array.length - 1] = x; -- - - - String Concatenation- -The C Way- - There are several difficulties to be resolved, like - when can storage be free'd, dealing with null pointers, - finding the length of the strings, and memory allocation: - -
- #include <string.h>
-
- char *s1;
- char *s2;
- char *s;
-
- // Concatenate s1 and s2, and put result in s
- free(s);
- s = (char *)malloc((s1 ? strlen(s1) : 0) +
- (s2 ? strlen(s2) : 0) + 1);
- if (!s)
- error("out of memory");
- if (s1)
- strcpy(s, s1);
- else
- *s = 0;
- if (s2)
- strcpy(s + strlen(s), s2);
-
- // Append "hello" to s
- char hello[] = "hello";
- char *news;
- size_t lens = s ? strlen(s) : 0;
- news = (char *)
- realloc(s, (lens + sizeof(hello) + 1) * sizeof(char));
- if (!news)
- error("out of memory");
- s = news;
- memcpy(s + lens, hello, sizeof(hello));
-
-
-The D Way- - D overloads the operators ~ and ~= for char and wchar arrays to mean - concatenate and append, respectively: - -- char[] s1; - char[] s2; - char[] s; - - s = s1 ~ s2; - s ~= "hello"; -- - - - Formatted printing- -The C Way- - printf() is the general purpose formatted print routine: - -
- #include <stdio.h>
-
- printf("Calling all cars %d times!\n", ntimes);
-
-
-The D Way- - What can we say? printf() rules: - -
- import stdio;
-
- printf("Calling all cars %d times!\n", ntimes);
-
-
-- - Forward referencing functions- -The C Way- - Functions cannot be forward referenced. Hence, to call a function - not yet encountered in the source file, it is necessary to insert - a function declaration lexically preceding the call. - -
- void forwardfunc();
-
- void myfunc()
- {
- forwardfunc();
- }
-
- void forwardfunc()
- {
- ...
- }
-
-
-The D Way- - The program is looked at as a whole, and so not only is it not - necessary to code forward declarations, it is not even allowed! - D avoids the tedium and errors associated with writing forward - referenced function declarations twice. - Functions can be defined in any order. - -
- void myfunc()
- {
- forwardfunc();
- }
-
- void forwardfunc()
- {
- ...
- }
-
-
-- - Functions that have no arguments- -The C Way- -- void function(void); -- - The D Way- - D is a strongly typed language, so there is no need to explicitly - say a function takes no arguments, just don't declare it has having - arguments. - -
- void function()
- {
- ...
- }
-
-
-- - Labelled break's and continue's.- -The C Way- - Break's and continue's only apply to the innermost nested loop or - switch, so a multilevel break must use a goto: - -
- for (i = 0; i < 10; i++)
- {
- for (j = 0; j < 10; j++)
- {
- if (j == 3)
- goto Louter;
- if (j == 4)
- goto L2;
- }
- L2:
- ;
- }
- Louter:
- ;
-
-
-The D Way- - Break and continue statements can be followed by a label. The label - is the label for an enclosing loop or switch, and the break applies - to that loop. - -
- Louter:
- for (i = 0; i < 10; i++)
- {
- for (j = 0; j < 10; j++)
- {
- if (j == 3)
- break Louter;
- if (j == 4)
- continue Louter;
- }
- }
- // break Louter goes here
-
-
-- - Goto Statements- -The C Way- - The much maligned goto statement is a staple for professional C coders. It's - necessary to make up for sometimes inadequate control flow statements. - -The D Way- - Many C-way goto statements can be eliminated with the D feature of labelled - break and continue statements. But D is a practical language for practical - programmers who know when the rules need to be broken. So of course D supports - the goto! - -- Struct tag name space- -The C Way- - It's annoying to have to put the struct keyword every time a type is specified, - so a common idiom is to use: - -
- typedef struct ABC { ... } ABC;
-
-
-The D Way- - Struct tag names are not in a separate name space, they are in the same name - space as ordinary names. Hence: - -
- struct ABC { ... };
-
-
-- - Looking up strings- -The C Way- - Given a string, compare the string against a list of possible - values and take action based on which one it is. A typical use - for this might be command line argument processing. - -
- #include <string.h>
- void dostring(char *s)
- {
- enum Strings { Hello, Goodbye, Maybe, Max };
- static char *table[] = { "hello", "goodbye", "maybe" };
- int i;
-
- for (i = 0; i < Max; i++)
- {
- if (strcmp(s, table[i]) == 0)
- break;
- }
- switch (i)
- {
- case Hello: ...
- case Goodbye: ...
- case Maybe: ...
- default: ...
- }
- }
-
-
- The problem with this is trying to maintain 3 parallel data
- structures, the enum, the table, and the switch cases. If there
- are a lot of values, the connection between the 3 may not be so
- obvious when doing maintenance, and so the situation is ripe for
- bugs.
-
- Additionally, if the number of values becomes large, a binary or
- hash lookup will yield a considerable performance increase over
- a simple linear search. But coding these can be time consuming,
- and they need to be debugged. It's typical that such just never
- gets done.
-
-The D Way- - D extends the concept of switch statements to be able to handle - strings as well as numbers. Then, the way to code the string - lookup becomes straightforward: - -
- void dostring(char[] s)
- {
- switch (s)
- {
- case "hello": ...
- case "goodbye": ...
- case "maybe": ...
- default: ...
- }
- }
-
-
- Adding new cases becomes easy. The compiler can be relied on
- to generate a fast lookup scheme for it, eliminating the bugs
- and time required in hand-coding one.
-
-- - Setting struct member alignment- -The C Way- - It's done through a command line switch which affects the entire - program, and woe results if any modules or libraries didn't get - recompiled. To address this, #pragma's are used: - -
- #pragma pack(1)
- struct ABC
- {
- ...
- };
- #pragma pack()
-
-
- But #pragmas are nonportable both in theory and in practice from
- compiler to compiler.
-
-The D Way- - Clearly, since much of the point to setting alignment is for - portability of data, a portable means of expressing it is necessary. - -
- struct ABC
- {
- int z; // z is aligned to the default
-
- align (1) int x; // x is byte aligned
- align (4)
- {
- ... // declarations in {} are dword aligned
- }
- align (2): // switch to word alignment from here on
-
- int y; // y is word aligned
- }
-
-
-- - Anonymous Structs and Unions- -Sometimes, it's nice to control the layout of a struct with nested structs and unions. - -The C Way- - C doesn't allow anonymous structs or unions, which means that dummy tag names - and dummy members are necessary: - -
- struct Foo
- { int i;
- union Bar
- {
- struct Abc { int x; long y; } _abc;
- char *p;
- } _bar;
- };
-
- #define x _bar._abc.x
- #define y _bar._abc.y
- #define p _bar.p
-
- struct Foo f;
-
- f.i;
- f.x;
- f.y;
- f.p;
-
-
- Not only is it clumsy, but using macros means a symbolic debugger won't understand
- what is being done, and the macros have global scope instead of struct scope.
-
-The D Way- - Anonymous structs and unions are used to control the layout in a - more natural manner: - -
- struct Foo
- { int i;
- union
- {
- struct { int x; long y; }
- char* p;
- }
- }
-
- Foo f;
-
- f.i;
- f.x;
- f.y;
- f.p;
-
-
-- - Declaring struct types and variables.- -The C Way- - Is to do it in one statement ending with a semicolon: - -
- struct Foo { int x; int y; } foo;
-
-
- Or to separate the two:
-
-
- struct Foo { int x; int y; }; // note terminating ;
- struct Foo foo;
-
-
-The D Way- - Struct definitions and declarations can't be done in the same statement: - -
- struct Foo { int x; int y; } // note there is no terminating ;
- Foo foo;
-
-
- which means that the terminating ; can be dispensed with, eliminating the
- confusing difference between struct {} and function & block {} in how semicolons
- are used.
-
-- - Getting the offset of a struct member.- -The C Way- - Naturally, another macro is used: - -
- #include <stddef>
- struct Foo { int x; int y; };
-
- off = offsetof(Foo, y);
-
-
-The D Way- - An offset is just another property: - -
- struct Foo { int x; int y; }
-
- off = Foo.y.offset;
-
-
-- - Union initializations.- -The C Way- - Unions are initialized using the "first member" rule: - -
- union U { int a; long b; };
- union U x = { 5 }; // initialize member 'a' to 5
-
-
- Adding union members or rearranging them can have disastrous consequences
- for any initializers.
-
-The D Way- - In D, which member is being initialized is mentioned explicitly: - -
- union U { int a; long b; }
- U x = { a:5 }
-
-
- avoiding the confusion and maintenance problems.
-
-- - Struct initializations.- -The C Way- - Members are initialized by their position within the {}'s: - -
- struct S { int a; int b; };
- struct S x = { 5, 3 };
-
-
- This isn't much of a problem with small structs, but when there
- are numerous members, it becomes tedious to get the initializers
- carefully lined up with the field declarations. Then, if members are
- added or rearranged, all the initializations have to be found and
- modified appropriately. This is a minefield for bugs.
-
-The D Way- - Member initialization can be done explicitly: - -
- struct S { int a; int b; }
- S x = { b:3, a:5 }
-
-
- The meaning is clear, and there no longer is a positional dependence.
-
-- - Array initializations.- -The C Way- - C initializes array by positional dependence: -
- int a[3] = { 3,2,2 };
-
- Nested arrays may or may not have the { }:
-
- int b[3][2] = { 2,3, {6,5}, 3,4 };
-
-The D Way- - D does it by positional dependence too, but an index can be used as well: - The following all produce the same result: -- int[3] a = [ 3, 2, 0 ]; - int[3] a = [ 3, 2 ]; // unsupplied initializers are 0, just like in C - int[3] a = [ 2:0, 0:3, 1:2 ]; - int[3] a = [ 2:0, 0:3, 2 ]; // if not supplied, the index is the - // previous one plus one. -- This can be handy if the array will be indexed by an enum, and the order of - enums may be changed or added to: -
- enum color { black, red, green }
- int[3] c = [ black:3, green:2, red:5 ];
-
- Nested array initializations must be explicit:
-- int[2][3] b = [ [2,3], [6,5], [3,4] ]; - - int[2][3] b = [[2,6,3],[3,5,4]]; // error -- - - - Escaped String Literals- -The C Way- - C has problems with the DOS file system because a \ is an escape in a string. To specifiy file c:\root\file.c: -- char file[] = "c:\\root\\file.c"; --This gets even more unpleasant with regular expressions. -Consider the escape sequence to match a quoted string: - - /"[^\\]*(\\.[^\\]*)*"/ -- In C, this horror is expressed as: - - char quoteString[] = "\"[^\\\\]*(\\\\.[^\\\\]*)*\""; -- The D Way- - Within strings, it is WYSIWYG (what you see is what you get). - Escapes are in separate strings. So: -- char[] file = 'c:\root\file.c'; - char[] quoteString = \" r"[^\\]*(\\.[^\\]*)*" \"; -- The famous hello world string becomes: - - char[] hello = "hello world" \n; -- - - - Ascii vs Wide Characters- -Modern programming requires that wchar strings be supported in an easy way, for internationalization of the programs. - - The C Way- - C uses the wchar_t and the L prefix on strings: -- #include <wchar.h> - char foo_ascii[] = "hello"; - wchar_t foo_wchar[] = L"hello"; --Things get worse if code is written to be both ascii and wchar compatible. -A macro is used to switch strings from ascii to wchar: -
- #include <tchar.h>
- tchar string[] = TEXT("hello");
-
-The D Way- -The type of a string is determined by semantic analysis, so there is no need to wrap strings in a macro call: -- char[] foo_ascii = "hello"; // string is taken to be ascii - wchar[] foo_wchar = "hello"; // string is taken to be wchar -- - - - Arrays that parallel an enum- -The C Way- - Consider: -
- enum COLORS { red, blue, green, max };
- char *cstring[max] = {"red", "blue", "green" };
-
- This is fairly easy to get right because the number of entries is small. But suppose it gets to be fairly large. Then it can get difficult to maintain correctly when new entries are added.
-
-The D Way-
- enum COLORS { red, blue, green }
-
- char[][COLORS.max + 1] cstring =
- [
- COLORS.red : "red",
- COLORS.blue : "blue",
- COLORS.green : "green",
- ];
-
-
-Not perfect, but better.
-
-- - Creating a new typedef'd type- -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. - -- typedef void *Handle; - void foo(void *); - void bar(Handle); - - Handle h; - 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. - -
- struct Handle__ { void *value; }
- typedef struct Handle__ *Handle;
- void foo(void *);
- void bar(Handle);
-
- Handle h;
- foo(h); // syntax error
- bar(h); // ok
-
-
- Having a default value for the type involves defining a macro,
- a naming convention, and then pedantically following that convention:
-
- - #define HANDLE_INIT ((Handle)-1) - - Handle h = HANDLE_INIT; - h = func(); - if (h != HANDLE_INIT) - ... -- - For the struct solution, things get even more complex: - -
- struct Handle__ HANDLE_INIT;
-
- void init_handle() // call this function upon startup
- {
- HANDLE_INIT.value = (void *)-1;
- }
-
- Handle h = HANDLE_INIT;
- h = func();
- if (memcmp(&h,&HANDLE_INIT,sizeof(Handle)) != 0)
- ...
-
-
- There are 4 names to remember: Handle, HANDLE_INIT,
- struct Handle__, value.
-
-The D Way- - No need for idiomatic constructions like the above. Just write: - -- typedef void* Handle; - void foo(void*); - void bar(Handle); - - Handle h; - foo(h); - bar(h); -- - To handle a default value, add an initializer to the typedef, - and refer to it with the .init property: - - - typedef void* Handle = cast(void*)(-1); - Handle h; - h = func(); - if (h != Handle.init) - ... -- - There's only one name to remember: Handle. - - - - Comparing structs- -The C Way- - While C defines struct assignment in a simple, convenient manner: - -- struct A x, y; - ... - x = y; -- - it does not for struct comparisons. Hence, to compare two struct - instances for equality: - - - #include <string.h> - - struct A x, y; - ... - if (memcmp(&x, &y, sizeof(struct A)) == 0) - ... -- - Note the obtuseness of this, coupled with the lack of any kind - of help from the language with type checking. - - - There's a nasty bug lurking in the memcmp(). - The layout of a struct, due to alignment, can have '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. - - The D Way- - D does it the obvious, straightforward way: - -- A x, y; - ... - if (x == y) - ... -- - - - Comparing strings- -The C Way- - The library function strcmp() is used: -- char string[] = "hello"; - - if (strcmp(string, "betty") == 0) // do strings match? - ... -- - C uses 0 terminated strings, so the C way has an inherent - inefficiency in constantly scanning for the terminating 0. - - The D Way- - Why not use the == operator? - -- char[] string = "hello"; - - if (string == "betty") - ... -- - D strings have the length stored separately from the string. - Thus, the implementation of string compares can be much faster - than in C (the difference being equivalent to the difference - in speed between the C memcmp() and strcmp()). - - - D supports comparison operators on strings, too: - - - char[] string = "hello"; - - if (string < "betty") - ... -- - which is useful for sorting/searching. - - - Sorting arrays- -The C Way- - Although many C programmers tend to reimplmement bubble sorts - over and over, the right way to sort in C is to use qsort(): - -
- int compare(const void *p1, const void *p2)
- {
- type *t1 = (type *)p1;
- type *t1 = (type *)p2;
-
- return *t1 - *t2;
- }
-
- type array[10];
- ...
- qsort(array, sizeof(array)/sizeof(array[0]),
- sizeof(array[0]), compare);
-
-
- A compare() must be written for each type, and much careful
- typo-prone code needs to be written to make it work.
-
-
-The D Way- - Sorting couldn't be easier: - -- type[] array; - ... - array.sort; // sort array in-place -- - - Volatile memory access- -The C Way- - To access volatile memory, such as shared memory - or memory mapped I/O, a pointer to volatile is created: -- volatile int *p = address; - - i = *p; -- - The D Way- - D has volatile as a statement type, not as a type modifier: - -
- int* p = address;
-
- volatile { i = *p; }
-
-
-- String literals- -The C Way- - String literals in C cannot span multiple lines, so to have - a block of text it is necessary to use \ line splicing: - -- "This text spans\n - multiple\n - lines\n" -- - If there is a lot of text, this can wind up being tedious. - - The D Way- - String literals can span multiple lines, as in: - -- "This text spans - multiple - lines - " -- - So blocks of text can just be cut and pasted into the D - source. - - - Data Structure Traversal- -The C Way- - Consider a function to traverse a recursive data structure. - In this example, there's a simple symbol table of strings. - The data structure is an array of binary trees. - The code needs to do an exhaustive search of it to find - a particular string in it, and determine if it is a unique - instance. -- - To make this work, a helper function membersearchx - is needed to recursively - walk the trees. The helper function needs to read and write - some context outside of the trees, so a custom struct Paramblock - is created and a pointer to it is used to maximize efficiency. - -
- struct Symbol
- { char *id;
- struct Symbol *left;
- struct Symbol *right;
- };
-
- struct Paramblock
- { char *id;
- struct Symbol *sm;
- };
-
- static void membersearchx(struct Paramblock *p, struct Symbol *s)
- {
- while (s)
- {
- if (strcmp(p->id,s->id) == 0)
- {
- if (p->sm)
- error("ambiguous member %s\n",p->id);
- p->sm = s;
- }
-
- if (s->left)
- membersearchx(p,s->left);
- s = s->right;
- }
- }
-
- struct Symbol *symbol_membersearch(Symbol *table[], int tablemax, char *id)
- {
- struct Paramblock pb;
- int i;
-
- pb.id = id;
- pb.sm = NULL;
- for (i = 0; i < tablemax; i++)
- {
- membersearchx(pb, table[i]);
- }
- return pb.sm;
- }
-
-
-The D Way- - This is the same algorithm in D, and it shrinks dramatically. - Since nested functions have access to the lexically enclosing - function's variables, there's no need for a Paramblock or - to deal with its bookkeeping details. The nested helper function - is contained wholly within the function that needs it, - improving locality and maintainability. -- - The performance of the two versions is indistinguishable. - -
- class Symbol
- { char[] id;
- Symbol left;
- Symbol right;
- }
-
- Symbol symbol_membersearch(Symbol[] table, char[] id)
- { Symbol sm;
-
- void membersearchx(Symbol s)
- {
- while (s)
- {
- if (id == s.id)
- {
- if (sm)
- error("ambiguous member %s\n", id);
- sm = s;
- }
-
- if (s.left)
- membersearchx(s.left);
- s = s.right;
- }
- }
-
- for (int i = 0; i < table.length; i++)
- {
- membersearchx(table[i]);
- }
- return sm;
- }
-
-
-- Unsigned Right Shift- -The C Way- - The right shift operators >> and >>= are signed - shifts if the left operand is a signed integral type, and - are unsigned right shifts if the left operand is an unsigned - integral type. To produce an unsigned right shift on an int, - a cast is necessary: - -- int i, j; - ... - j = (unsigned)i >> 3; -- - If i is an int, this works fine. But if i is - of a typedef'd type, - - - myint i, j; - ... - j = (unsigned)i >> 3; -- - and myint happens to be a long int, then the cast to - unsigned - will silently throw away the most significant bits, corrupting - the answer. - - The D Way- - D has the right shift operators >> and >>= which - behave as they do in C. But D also has explicitly unsigned - right shift operators >>> and >>>= which will - do an unsigned right shift regardless of the sign of the left - operand. Hence, - -- myint i, j; - ... - j = i >>> 3; -- - avoids the unsafe cast and will work as expected with any integral - type. - - - Dynamic Closures- -The C Way- - Consider a reusable container type. In order to be reusable, - it must support a way to apply arbitrary code to each element - of the container. This is done by creating an apply function - that accepts a function pointer to which is passed each - element of the container contents. -- - A generic context pointer is also needed, represented here by - void *p. The example here is of a trivial container - class that holds an array of int's, and a user of that container - that computes the maximum of those int's. - -
- struct Collection
- {
- int array[10];
-
- void apply(void *p, void (*fp)(void *, int))
- {
- for (int i = 0; i < sizeof(array)/sizeof(array[0]); i++)
- fp(p, array[i]);
- }
- };
-
- void comp_max(void *p, int i)
- {
- int *pmax = (int *)p;
-
- if (i > *pmax)
- *pmax = i;
- }
-
- void func(Collection *c)
- {
- int max = INT_MIN;
-
- c->apply(&max, comp_max);
- }
-
-
- The C way makes heavy use of pointers and casting.
- The casting is tedious, error prone, and loses all type safety.
-
-The D Way- - The D version makes use of delegates to transmit - context information for the apply function, - and nested functions both to capture context - information and to improve locality. - - class Collection
- {
- int[10] array;
-
- void apply(void delegate(int) fp)
- {
- for (int i = 0; i < array.length; i++)
- fp(array[i]);
- }
- }
-
- void func(Collection c)
- {
- int max = int.min;
-
- void comp_max(int i)
- {
- if (i > max)
- max = i;
- }
-
- c.apply(comp_max);
- }
-
- Pointers are eliminated, as well as casting and generic
- pointers. The D version is fully type safe.
- An alternate method in D makes use of function literals:
-
- void func(Collection c)
- {
- int max = int.min;
-
- c.apply(delegate(int i) { if (i > max) max = i; } );
- }
-
- eliminating the need to create irrelevant function names.
-
-- Variadic Function Parameters- - The task is to write a function that takes a varying - number of arguments, such as a function that sums - its arguments. - -The C Way- -
- #include <stdio.h>
- #include <stdarg.h>
-
- int sum(int dim, ...)
- { int i;
- int s = 0;
- va_list ap;
-
- va_start(ap, dim);
- for (i = 0; i < dim; i++)
- s += va_arg(ap, int);
- va_end(ap);
- return s;
- }
-
- int main()
- {
- int i;
-
- i = sum(3, 8,7,6);
- printf("sum = %d\n", i);
-
- return 0;
- }
-
-
- There are two problems with this. The first is that the
- sum function needs to know how many arguments were
- supplied. It has to be explicitly written, and it can get
- out of sync with respect to the actual number of arguments
- written.
- The second is that there's no way to check that the
- types of the arguments provided really were ints, and not
- doubles, strings, structs, etc.
-
-The D Way- - The ... following an array parameter declaration means that - the trailing arguments are collected together to form - an array. The arguments are type checked against the array - type, and the number of arguments becomes a property - of the array: - -
- int sum(int[] values ...)
- {
- int s = 0;
-
- foreach (int x; values)
- s += x;
- return s;
- }
-
- int main()
- {
- int i;
-
- i = sum(8,7,6);
- printf("sum = %d\n", i);
-
- return 0;
- }
-
-
-
- |
Add feedback and comments regarding this - page.
- -|
+
+
+
+
+ + +· D vs C/C++/C#/Java + + +· Rationale for Builtins + + +· Converting C to D + + +· Converting C++ to D + + +· The C Preprocessor vs D + + +· D strings vs C++ std::string + + +· D complex vs C++ std::complex + + +· D Contract Programming vs C++ + + + + |
+
+
+Programming in D for C Programmers+ +
+
+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.
++ +Since C does not have object-oriented features, there's a separate section +for object-oriented issues +Programming in D for C++ Programmers. + + +The C preprocessor is covered in +The C Preprocessor vs D. + +
+ Getting the Size of a Type+ +The C Way+ +sizeof(int) + sizeof(char *) + sizeof(double) + sizeof(struct Foo) ++ + The D Way+ +Use the size property: + +int.size + (char *).size + double.size + Foo.size ++ + + + Get the max and min values of a type+ +The C Way+ +#include <limits.h> + #include <math.h> + + CHAR_MAX + CHAR_MIN + ULONG_MAX + DBL_MIN ++ + + The D Way+ +char.max + char.min + ulong.max + double.min ++ + + + Primitive Types+ +C to D types+ +bool => bit + char => char + signed char => byte + unsigned char => ubyte + short => short + unsigned short => ushort + wchar_t => wchar + int => int + unsigned => uint + long => int + unsigned long => uint + long long => long + unsigned long long => ulong + float => float + double => double + long double => real + _Imaginary long double => imaginary + _Complex long double => complex + ++ + + Although char is an unsigned 8 bit type, and + wchar is an unsigned 16 bit type, they have their own separate types + in order to aid overloading and type safety. + + Ints and unsigneds in C are of varying size; not so in D. + + + + Special Floating Point Values+ +The C Way+ +#include <fp.h> + + NAN + INFINITY + + #include <float.h> + + DBL_DIG + DBL_EPSILON + DBL_MANT_DIG + DBL_MAX_10_EXP + DBL_MAX_EXP + DBL_MIN_10_EXP + DBL_MIN_EXP ++ + + The D Way+ +double.nan + double.infinity + double.dig + double.epsilon + double.mant_dig + double.max_10_exp + double.max_exp + double.min_10_exp + double.min_exp ++ + + Remainder after division of floating point numbers+ +The C Way+ ++ #include <math.h> + + float f = fmodf(x,y); + double d = fmod(x,y); + long double r = fmodl(x,y); ++ + + The D Way+ +D supports the remainder ('%') operator on floating point operands: + +float f = x % y; + double d = x % y; + real r = x % y; ++ + + + Dealing with NAN's in floating point compares+ +The C Way+ + C doesn't define what happens if an operand to a compare + is NAN, and few C compilers check for it (the Digital Mars + C compiler is an exception, DM's compilers do check for NAN operands). + ++ #include <math.h> + + if (isnan(x) || isnan(y)) + result = FALSE; + else + result = (x < y); ++ + + The D Way++ D offers a full complement of comparisons and operators + that work with NAN arguments. + + result = (x < y); // false if x or y is nan
+
+
++ + Assert's are a necessary part of any good defensive coding strategy.+ +The C Way++C doesn't directly support assert, but does support __FILE__ +and __LINE__ from which an assert macro can be built. In fact, +there appears to be practically no other use for __FILE__ and __LINE__. + + + #include <assert.h> + + assert(e == 0); ++ + + The D Way+ +D simply builds assert into the language: + + assert(e == 0);
+
+
++ + Initializing all elements of an array+ +The C Way+ ++ #define ARRAY_LENGTH 17 + int array[ARRAY_LENGTH]; + for (i = 0; i < ARRAY_LENGTH; i++) + array[i] = value; ++ + + The D Way+ + int array[17];
+ array[] = value;
+
+
++ + Looping through an array+ +The C Way++ The array length is defined separately, or a clumsy + sizeof() expression is used to get the length. + + + #define ARRAY_LENGTH 17 + int array[ARRAY_LENGTH]; + for (i = 0; i < ARRAY_LENGTH; i++) + func(array[i]); ++ + +or: + + + int array[17]; + for (i = 0; i < sizeof(array) / sizeof(array[0]); i++) + func(array[i]); ++ + + The D Way+ +The length of an array is accessible the property "length". + +int array[17]; + for (i = 0; i < array.length; i++) + func(array[i]); ++ +or even better: + + int array[17]; + foreach (int value; array) + func(value); ++ + + + + Creating an array of variable size+ +The C Way+ + C cannot do this with arrays. It is necessary to create a separate + variable for the length, and then explicitly manage the size of + the array: + +
+ #include <stdlib.h>
+
+ int array_length;
+ int *array;
+ int *newarray;
+
+ newarray = (int *)
+ realloc(array, (array_length + 1) * sizeof(int));
+ if (!newarray)
+ error("out of memory");
+ array = newarray;
+ array[array_length++] = x;
+
+
+
+The D Way+ + D supports dynamic arrays, which can be easily resized. D supports + all the requisite memory management. + + int[] array;
+
+ array.length = array.length + 1;
+ array[array.length - 1] = x;
+
+
++ + String Concatenation+ +The C Way+ + There are several difficulties to be resolved, like + when can storage be free'd, dealing with null pointers, + finding the length of the strings, and memory allocation: + +
+ #include <string.h>
+
+ char *s1;
+ char *s2;
+ char *s;
+
+ // Concatenate s1 and s2, and put result in s
+ free(s);
+ s = (char *)malloc((s1 ? strlen(s1) : 0) +
+ (s2 ? strlen(s2) : 0) + 1);
+ if (!s)
+ error("out of memory");
+ if (s1)
+ strcpy(s, s1);
+ else
+ *s = 0;
+ if (s2)
+ strcpy(s + strlen(s), s2);
+
+ // Append "hello" to s
+ char hello[] = "hello";
+ char *news;
+ size_t lens = s ? strlen(s) : 0;
+ news = (char *)
+ realloc(s, (lens + sizeof(hello) + 1) * sizeof(char));
+ if (!news)
+ error("out of memory");
+ s = news;
+ memcpy(s + lens, hello, sizeof(hello));
+
+
+
+The D Way+ + D overloads the operators ~ and ~= for char and wchar arrays to mean + concatenate and append, respectively: + +char[] s1; + char[] s2; + char[] s; + + s = s1 ~ s2; + s ~= "hello"; ++ + + + Formatted printing+ +The C Way+ + printf() is the general purpose formatted print routine: + +
+ #include <stdio.h>
+
+ printf("Calling all cars %d times!\n", ntimes);
+
+
+
+The D Way+ + What can we say? printf() rules: + +import stdio; + + printf("Calling all cars %d times!\n", ntimes); ++ + + + Forward referencing functions+ +The C Way+ + Functions cannot be forward referenced. Hence, to call a function + not yet encountered in the source file, it is necessary to insert + a function declaration lexically preceding the call. + +
+ void forwardfunc();
+
+ void myfunc()
+ {
+ forwardfunc();
+ }
+
+ void forwardfunc()
+ {
+ ...
+ }
+
+
+
+The D Way+ + The program is looked at as a whole, and so not only is it not + necessary to code forward declarations, it is not even allowed! + D avoids the tedium and errors associated with writing forward + referenced function declarations twice. + Functions can be defined in any order. + +void myfunc() + { + forwardfunc(); + } + + void forwardfunc() + { + ... + } ++ + + + Functions that have no arguments+ +The C Way+ ++ void function(void); ++ + + The D Way+ + D is a strongly typed language, so there is no need to explicitly + say a function takes no arguments, just don't declare it has having + arguments. + +void function() + { + ... + } ++ + + + Labelled break's and continue's.+ +The C Way+ + Break's and continue's only apply to the innermost nested loop or + switch, so a multilevel break must use a goto: + +
+ for (i = 0; i < 10; i++)
+ {
+ for (j = 0; j < 10; j++)
+ {
+ if (j == 3)
+ goto Louter;
+ if (j == 4)
+ goto L2;
+ }
+ L2:
+ ;
+ }
+ Louter:
+ ;
+
+
+
+The D Way+ + Break and continue statements can be followed by a label. The label + is the label for an enclosing loop or switch, and the break applies + to that loop. + +Louter: + for (i = 0; i < 10; i++) + { + for (j = 0; j < 10; j++) + { + if (j == 3) + break Louter; + if (j == 4) + continue Louter; + } + } + // break Louter goes here ++ + + + Goto Statements+ +The C Way+ + The much maligned goto statement is a staple for professional C coders. It's + necessary to make up for sometimes inadequate control flow statements. + +The D Way+ + Many C-way goto statements can be eliminated with the D feature of labelled + break and continue statements. But D is a practical language for practical + programmers who know when the rules need to be broken. So of course D supports + the goto! + ++ Struct tag name space+ +The C Way+ + It's annoying to have to put the struct keyword every time a type is specified, + so a common idiom is to use: + +
+ typedef struct ABC { ... } ABC;
+
+
+
+The D Way+ + Struct tag names are not in a separate name space, they are in the same name + space as ordinary names. Hence: + + struct ABC { ... };
+
+
++ + Looking up strings+ +The C Way+ + Given a string, compare the string against a list of possible + values and take action based on which one it is. A typical use + for this might be command line argument processing. + +
+ #include <string.h>
+ void dostring(char *s)
+ {
+ enum Strings { Hello, Goodbye, Maybe, Max };
+ static char *table[] = { "hello", "goodbye", "maybe" };
+ int i;
+
+ for (i = 0; i < Max; i++)
+ {
+ if (strcmp(s, table[i]) == 0)
+ break;
+ }
+ switch (i)
+ {
+ case Hello: ...
+ case Goodbye: ...
+ case Maybe: ...
+ default: ...
+ }
+ }
+
+
+
+ The problem with this is trying to maintain 3 parallel data
+ structures, the enum, the table, and the switch cases. If there
+ are a lot of values, the connection between the 3 may not be so
+ obvious when doing maintenance, and so the situation is ripe for
+ bugs.
+
+ Additionally, if the number of values becomes large, a binary or
+ hash lookup will yield a considerable performance increase over
+ a simple linear search. But coding these can be time consuming,
+ and they need to be debugged. It's typical that such just never
+ gets done.
+
+The D Way+ + D extends the concept of switch statements to be able to handle + strings as well as numbers. Then, the way to code the string + lookup becomes straightforward: + +void dostring(char[] s) + { + switch (s) + { + case "hello": ... + case "goodbye": ... + case "maybe": ... + default: ... + } + } ++ + Adding new cases becomes easy. The compiler can be relied on + to generate a fast lookup scheme for it, eliminating the bugs + and time required in hand-coding one. + + + + Setting struct member alignment+ +The C Way+ + It's done through a command line switch which affects the entire + program, and woe results if any modules or libraries didn't get + recompiled. To address this, #pragma's are used: + +
+ #pragma pack(1)
+ struct ABC
+ {
+ ...
+ };
+ #pragma pack()
+
+
+
+ But #pragmas are nonportable both in theory and in practice from
+ compiler to compiler.
+
+The D Way+ + Clearly, since much of the point to setting alignment is for + portability of data, a portable means of expressing it is necessary. + +struct ABC + { + int z; // z is aligned to the default + + align (1) int x; // x is byte aligned + align (4) + { + ... // declarations in {} are dword aligned + } + align (2): // switch to word alignment from here on + + int y; // y is word aligned + } ++ + + + Anonymous Structs and Unions+ +Sometimes, it's nice to control the layout of a struct with nested structs and unions. + +The C Way+ + C doesn't allow anonymous structs or unions, which means that dummy tag names + and dummy members are necessary: + +
+ struct Foo
+ { int i;
+ union Bar
+ {
+ struct Abc { int x; long y; } _abc;
+ char *p;
+ } _bar;
+ };
+
+ #define x _bar._abc.x
+ #define y _bar._abc.y
+ #define p _bar.p
+
+ struct Foo f;
+
+ f.i;
+ f.x;
+ f.y;
+ f.p;
+
+
+
+ Not only is it clumsy, but using macros means a symbolic debugger won't understand
+ what is being done, and the macros have global scope instead of struct scope.
+
+The D Way+ + Anonymous structs and unions are used to control the layout in a + more natural manner: + +struct Foo + { int i; + union + { + struct { int x; long y; } + char* p; + } + } + + Foo f; + + f.i; + f.x; + f.y; + f.p; ++ + + + Declaring struct types and variables.+ +The C Way+ + Is to do it in one statement ending with a semicolon: + +
+ struct Foo { int x; int y; } foo;
+
+
+
+ Or to separate the two:
+
+
+ struct Foo { int x; int y; }; // note terminating ;
+ struct Foo foo;
+
+
+
+The D Way+ + Struct definitions and declarations can't be done in the same statement: + +struct Foo { int x; int y; } // note there is no terminating ; + Foo foo; ++ + which means that the terminating ; can be dispensed with, eliminating the + confusing difference between struct {} and function & block {} in how semicolons + are used. + + + + Getting the offset of a struct member.+ +The C Way+ + Naturally, another macro is used: + +
+ #include <stddef>
+ struct Foo { int x; int y; };
+
+ off = offsetof(Foo, y);
+
+
+
+The D Way+ + An offset is just another property: + +struct Foo { int x; int y; } + + off = Foo.y.offset; ++ + + + Union initializations.+ +The C Way+ + Unions are initialized using the "first member" rule: + +
+ union U { int a; long b; };
+ union U x = { 5 }; // initialize member 'a' to 5
+
+
+
+ Adding union members or rearranging them can have disastrous consequences
+ for any initializers.
+
+The D Way+ + In D, which member is being initialized is mentioned explicitly: + +union U { int a; long b; } + U x = { a:5 } ++ + avoiding the confusion and maintenance problems. + + + + Struct initializations.+ +The C Way+ + Members are initialized by their position within the {}'s: + +
+ struct S { int a; int b; };
+ struct S x = { 5, 3 };
+
+
+
+ This isn't much of a problem with small structs, but when there
+ are numerous members, it becomes tedious to get the initializers
+ carefully lined up with the field declarations. Then, if members are
+ added or rearranged, all the initializations have to be found and
+ modified appropriately. This is a minefield for bugs.
+
+The D Way+ + Member initialization can be done explicitly: + +struct S { int a; int b; } + S x = { b:3, a:5 } ++ + The meaning is clear, and there no longer is a positional dependence. + + + + Array initializations.+ +The C Way+ + C initializes array by positional dependence: +
+ int a[3] = { 3,2,2 };
+
+
+ Nested arrays may or may not have the { }:
+
+ int b[3][2] = { 2,3, {6,5}, 3,4 };
+
+
+
+The D Way+ + D does it by positional dependence too, but an index can be used as well: + The following all produce the same result: + +int[3] a = [ 3, 2, 0 ]; + int[3] a = [ 3, 2 ]; // unsupplied initializers are 0, just like in C + int[3] a = [ 2:0, 0:3, 1:2 ]; + int[3] a = [ 2:0, 0:3, 2 ]; // if not supplied, the index is the + // previous one plus one. ++ This can be handy if the array will be indexed by an enum, and the order of + enums may be changed or added to: + + enum color { black, red, green } + int[3] c = [ black:3, green:2, red:5 ]; ++ Nested array initializations must be explicit: + int[2][3] b = [ [2,3], [6,5], [3,4] ]; + + int[2][3] b = [[2,6,3],[3,5,4]]; // error ++ + + + Escaped String Literals+ +The C Way+ + C has problems with the DOS file system because a \ is an escape in a string. To specifiy file c:\root\file.c: ++ char file[] = "c:\\root\\file.c"; ++ +This gets even more unpleasant with regular expressions. +Consider the escape sequence to match a quoted string: + + /"[^\\]*(\\.[^\\]*)*"/ ++ + In C, this horror is expressed as: + + char quoteString[] = "\"[^\\\\]*(\\\\.[^\\\\]*)*\""; ++ + The D Way+ + Within strings, it is WYSIWYG (what you see is what you get). + Escapes are in separate strings. So: + +char[] file = `c:\root\file.c`; + char[] quoteString = \" r"[^\\]*(\\.[^\\]*)*" \"; ++ + The famous hello world string becomes: + char[] hello = "hello world" \n; ++ + + + Ascii vs Wide Characters+ +Modern programming requires that wchar strings be supported in an easy way, for internationalization of the programs. + + The C Way+ + C uses the wchar_t and the L prefix on strings: ++ #include <wchar.h> + char foo_ascii[] = "hello"; + wchar_t foo_wchar[] = L"hello"; ++ +Things get worse if code is written to be both ascii and wchar compatible. +A macro is used to switch strings from ascii to wchar: +
+ #include <tchar.h>
+ tchar string[] = TEXT("hello");
+
+
+The D Way+ +The type of a string is determined by semantic analysis, so there is no need to wrap strings in a macro call: +char[] foo_ascii = "hello"; // string is taken to be ascii + wchar[] foo_wchar = "hello"; // string is taken to be wchar ++ + + + Arrays that parallel an enum+ +The C Way+ + Consider: +
+ enum COLORS { red, blue, green, max };
+ char *cstring[max] = {"red", "blue", "green" };
+
+
+ This is fairly easy to get right because the number of entries is small. But suppose it gets to be fairly large. Then it can get difficult to maintain correctly when new entries are added.
+
+The D Way+enum COLORS { red, blue, green } + + char[][COLORS.max + 1] cstring = + [ + COLORS.red : "red", + COLORS.blue : "blue", + COLORS.green : "green", + ]; ++ +Not perfect, but better. + + + + Creating a new typedef'd type+ +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. + ++ typedef void *Handle; + void foo(void *); + void bar(Handle); + + Handle h; + 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. + +
+ struct Handle__ { void *value; }
+ typedef struct Handle__ *Handle;
+ void foo(void *);
+ void bar(Handle);
+
+ Handle h;
+ foo(h); // syntax error
+ bar(h); // ok
+
+
+
+ Having a default value for the type involves defining a macro,
+ a naming convention, and then pedantically following that convention:
+
+ + #define HANDLE_INIT ((Handle)-1) + + Handle h = HANDLE_INIT; + h = func(); + if (h != HANDLE_INIT) + ... ++ + + For the struct solution, things get even more complex: + +
+ struct Handle__ HANDLE_INIT;
+
+ void init_handle() // call this function upon startup
+ {
+ HANDLE_INIT.value = (void *)-1;
+ }
+
+ Handle h = HANDLE_INIT;
+ h = func();
+ if (memcmp(&h,&HANDLE_INIT,sizeof(Handle)) != 0)
+ ...
+
+
+
+ There are 4 names to remember: Handle, HANDLE_INIT,
+ struct Handle__, value.
+
+The D Way+ + No need for idiomatic constructions like the above. Just write: + +typedef void* Handle; + void foo(void*); + void bar(Handle); + + Handle h; + foo(h); + bar(h); ++ + To handle a default value, add an initializer to the typedef, + and refer to it with the .init property: + + typedef void* Handle = cast(void*)(-1); + Handle h; + h = func(); + if (h != Handle.init) + ... ++ + There's only one name to remember: Handle. + + + + Comparing structs+ +The C Way+ + While C defines struct assignment in a simple, convenient manner: + ++ struct A x, y; + ... + x = y; ++ + + it does not for struct comparisons. Hence, to compare two struct + instances for equality: + + + #include <string.h> + + struct A x, y; + ... + if (memcmp(&x, &y, sizeof(struct A)) == 0) + ... ++ + + Note the obtuseness of this, coupled with the lack of any kind + of help from the language with type checking. + + + There's a nasty bug lurking in the memcmp(). + The layout of a struct, due to alignment, can have '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. + + The D Way+ + D does it the obvious, straightforward way: + + A x, y;
+ ...
+ if (x == y)
+ ...
+
+
+
++ Comparing strings+ +The C Way+ + The library function strcmp() is used: ++ char string[] = "hello"; + + if (strcmp(string, "betty") == 0) // do strings match? + ... ++ + + C uses 0 terminated strings, so the C way has an inherent + inefficiency in constantly scanning for the terminating 0. + + The D Way+ + Why not use the == operator? + +char[] string = "hello"; + + if (string == "betty") + ... ++ + D strings have the length stored separately from the string. + Thus, the implementation of string compares can be much faster + than in C (the difference being equivalent to the difference + in speed between the C memcmp() and strcmp()). + + + D supports comparison operators on strings, too: + + char[] string = "hello"; + + if (string < "betty") + ... ++ + which is useful for sorting/searching. + + + Sorting arrays+ +The C Way+ + Although many C programmers tend to reimplmement bubble sorts + over and over, the right way to sort in C is to use qsort(): + +
+ int compare(const void *p1, const void *p2)
+ {
+ type *t1 = (type *)p1;
+ type *t1 = (type *)p2;
+
+ return *t1 - *t2;
+ }
+
+ type array[10];
+ ...
+ qsort(array, sizeof(array)/sizeof(array[0]),
+ sizeof(array[0]), compare);
+
+
+
+ A compare() must be written for each type, and much careful
+ typo-prone code needs to be written to make it work.
+
+
+The D Way+ + Sorting couldn't be easier: + + type[] array;
+ ...
+ array.sort; // sort array in-place
+
+
++ Volatile memory access+ +The C Way+ + To access volatile memory, such as shared memory + or memory mapped I/O, a pointer to volatile is created: ++ volatile int *p = address; + + i = *p; ++ + + The D Way+ + D has volatile as a statement type, not as a type modifier: + +int* p = address; + + volatile { i = *p; } ++ + + String literals+ +The C Way+ + String literals in C cannot span multiple lines, so to have + a block of text it is necessary to use \ line splicing: + ++ "This text spans\n\ + multiple\n\ + lines\n" ++ + + If there is a lot of text, this can wind up being tedious. + + The D Way+ + String literals can span multiple lines, as in: + + "This text spans
+ multiple
+ lines
+ "
+
+
+ So blocks of text can just be cut and pasted into the D
+ source.
+
++ Data Structure Traversal+ +The C Way+ + Consider a function to traverse a recursive data structure. + In this example, there's a simple symbol table of strings. + The data structure is an array of binary trees. + The code needs to do an exhaustive search of it to find + a particular string in it, and determine if it is a unique + instance. ++ + To make this work, a helper function membersearchx + is needed to recursively + walk the trees. The helper function needs to read and write + some context outside of the trees, so a custom struct Paramblock + is created and a pointer to it is used to maximize efficiency. + +
+ struct Symbol
+ { char *id;
+ struct Symbol *left;
+ struct Symbol *right;
+ };
+
+ struct Paramblock
+ { char *id;
+ struct Symbol *sm;
+ };
+
+ static void membersearchx(struct Paramblock *p, struct Symbol *s)
+ {
+ while (s)
+ {
+ if (strcmp(p->id,s->id) == 0)
+ {
+ if (p->sm)
+ error("ambiguous member %s\n",p->id);
+ p->sm = s;
+ }
+
+ if (s->left)
+ membersearchx(p,s->left);
+ s = s->right;
+ }
+ }
+
+ struct Symbol *symbol_membersearch(Symbol *table[], int tablemax, char *id)
+ {
+ struct Paramblock pb;
+ int i;
+
+ pb.id = id;
+ pb.sm = NULL;
+ for (i = 0; i < tablemax; i++)
+ {
+ membersearchx(pb, table[i]);
+ }
+ return pb.sm;
+ }
+
+
+
+The D Way+ + This is the same algorithm in D, and it shrinks dramatically. + Since nested functions have access to the lexically enclosing + function's variables, there's no need for a Paramblock or + to deal with its bookkeeping details. The nested helper function + is contained wholly within the function that needs it, + improving locality and maintainability. ++ + The performance of the two versions is indistinguishable. + + class Symbol + { char[] id; + Symbol left; + Symbol right; + } + + Symbol symbol_membersearch(Symbol[] table, char[] id) + { Symbol sm; + + void membersearchx(Symbol s) + { + while (s) + { + if (id == s.id) + { + if (sm) + error("ambiguous member %s\n", id); + sm = s; + } + + if (s.left) + membersearchx(s.left); + s = s.right; + } + } + + for (int i = 0; i < table.length; i++) + { + membersearchx(table[i]); + } + return sm; + } ++ + + Unsigned Right Shift+ +The C Way+ + The right shift operators >> and >>= are signed + shifts if the left operand is a signed integral type, and + are unsigned right shifts if the left operand is an unsigned + integral type. To produce an unsigned right shift on an int, + a cast is necessary: + ++ int i, j; + ... + j = (unsigned)i >> 3; ++ + + If i is an int, this works fine. But if i is + of a typedef'd type, + + + myint i, j; + ... + j = (unsigned)i >> 3; ++ + + and myint happens to be a long int, then the cast to + unsigned + will silently throw away the most significant bits, corrupting + the answer. + + The D Way+ + D has the right shift operators >> and >>= which + behave as they do in C. But D also has explicitly unsigned + right shift operators >>> and >>>= which will + do an unsigned right shift regardless of the sign of the left + operand. Hence, + +myint i, j; + ... + j = i >>> 3; ++ + avoids the unsafe cast and will work as expected with any integral + type. + + + Dynamic Closures+ +The C Way+ + Consider a reusable container type. In order to be reusable, + it must support a way to apply arbitrary code to each element + of the container. This is done by creating an apply function + that accepts a function pointer to which is passed each + element of the container contents. ++ + A generic context pointer is also needed, represented here by + void *p. The example here is of a trivial container + class that holds an array of int's, and a user of that container + that computes the maximum of those int's. + +
+ struct Collection
+ {
+ int array[10];
+
+ void apply(void *p, void (*fp)(void *, int))
+ {
+ for (int i = 0; i < sizeof(array)/sizeof(array[0]); i++)
+ fp(p, array[i]);
+ }
+ };
+
+ void comp_max(void *p, int i)
+ {
+ int *pmax = (int *)p;
+
+ if (i > *pmax)
+ *pmax = i;
+ }
+
+ void func(Collection *c)
+ {
+ int max = INT_MIN;
+
+ c->apply(&max, comp_max);
+ }
+
+
+
+ The C way makes heavy use of pointers and casting.
+ The casting is tedious, error prone, and loses all type safety.
+
+The D Way+ + The D version makes use of delegates to transmit + context information for the apply function, + and nested functions both to capture context + information and to improve locality. + +class Collection + { + int[10] array; + + void apply(void delegate(int) fp) + { + for (int i = 0; i < array.length; i++) + fp(array[i]); + } + } + + void func(Collection c) + { + int max = int.min; + + void comp_max(int i) + { + if (i > max) + max = i; + } + + c.apply(comp_max); + } ++ + Pointers are eliminated, as well as casting and generic + pointers. The D version is fully type safe. + An alternate method in D makes use of function literals: + + void func(Collection c) + { + int max = int.min; + + c.apply(delegate(int i) { if (i > max) max = i; } ); + } ++ + eliminating the need to create irrelevant function names. + + + Variadic Function Parameters+ + The task is to write a function that takes a varying + number of arguments, such as a function that sums + its arguments. + +The C Way+ +
+ #include <stdio.h>
+ #include <stdarg.h>
+
+ int sum(int dim, ...)
+ { int i;
+ int s = 0;
+ va_list ap;
+
+ va_start(ap, dim);
+ for (i = 0; i < dim; i++)
+ s += va_arg(ap, int);
+ va_end(ap);
+ return s;
+ }
+
+ int main()
+ {
+ int i;
+
+ i = sum(3, 8,7,6);
+ printf("sum = %d\n", i);
+
+ return 0;
+ }
+
+
+
+ There are two problems with this. The first is that the
+ sum function needs to know how many arguments were
+ supplied. It has to be explicitly written, and it can get
+ out of sync with respect to the actual number of arguments
+ written.
+ The second is that there's no way to check that the
+ types of the arguments provided really were ints, and not
+ doubles, strings, structs, etc.
+
+The D Way+ + The ... following an array parameter declaration means that + the trailing arguments are collected together to form + an array. The arguments are type checked against the array + type, and the number of arguments becomes a property + of the array: + +int sum(int[] values ...) + { + int s = 0; + + foreach (int x; values) + s += x; + return s; + } + + int main() + { + int i; + + i = sum(8,7,6); + printf("sum = %d\n", i); + + return 0; + } ++ + |
-
-Home
-| Search
-| D
-| Comments
-
-|
-
- - -· Lexical -· Modules -· Declarations -· Types -· Properties -· Attributes -· Pragmas -· Expressions -· Statements -· Arrays -· Structs & Unions -· Classes -· Interfaces -· Enums -· Functions -· Operator Overloading -· Templates -· Mixins -· Contracts -· Conditional Compilation -· Handling errors -· Garbage Collection -· Memory Management -· Floating Point -· Inline Assembler -· Documentation Comments -· Interfacing To C -· Portability Guide -· Embedding D in HTML -· Named Character Entities -· Application Binary Interface - - |
-
-
-
-
-Contract Programming- - Contracts are a breakthrough technique to reduce the programming effort - for large projects. Contracts are the concept of preconditions, postconditions, - errors, and invariants. - Contracts can be done in C++ without modification to the language, - but the result is - clumsy and inconsistent. -- - Building contract support into the language makes for: - -
-
- The idea of a contract is simple - it's just an expression that must evaluate to true.
- If it does not, the contract is broken, and by definition, the program has a bug in it.
- Contracts form part of the specification for a program, moving it from the documentation
- to the code itself. And as every programmer knows, documentation tends to be incomplete,
- out of date, wrong, or non-existent. Moving the contracts into the code makes them
- verifiable against the program.
-
-Assert Contract- - The most basic contract is the - assert. - An assert inserts a checkable expression into - the code, and that expression must evaluate to true: -- assert(expression); -- C programmers will find it familiar. Unlike C, however, an assert
- in function bodies
- works by throwing an AssertException,
- which can be caught and handled. Catching the contract violation is useful
- when the code must deal with errant uses by other code, when it must be
- failure proof, and as a useful tool for debugging.
-
-Pre and Post Contracts- - The pre contracts specify the preconditions before a statement is executed. The most - typical use of this would be in validating the parameters to a function. The post - contracts validate the result of the statement. The most typical use of this - would be in validating the return value of a function and of any side effects it has. - The syntax is: - -
- in
- {
- ...contract preconditions...
- }
- out (result)
- {
- ...contract postconditions...
- }
- body
- {
- ...code...
- }
-
- By definition, if a pre contract fails, then the body received bad parameters.
- An InException is thrown. If a post contract fails,
- then there is a bug in the body. An OutException is thrown.
-
- Either the
- long square_root(long x)
- in
- {
- assert(x >= 0);
- }
- out (result)
- {
- assert((result * result) == x);
- }
- body
- {
- return math.sqrt(x);
- }
-
- The assert's in the in and out bodies are called contracts.
- Any other D
- statement or expression is allowed in the bodies, but it is important
- to ensure that the
- code has no side effects, and that the release version of the code
- will not depend on any effects of the code.
- For a release build of the code, the in and out code is not
- inserted.
-- If the function returns a void, there is no result, and so there can be no - result declaration in the out clause. - In that case, use: -
- void func()
- out
- {
- ...contracts...
- }
- body
- {
- ...
- }
-
- In an out statement, result is initialized and set to the
- return value of the function.
-
- The compiler can be adjusted to verify that every in and inout parameter is referenced
- in the - The in-out statement can also be used inside a function, for example, it can be used - to check the results of a loop: -
- in
- {
- assert(j == 0);
- }
- out
- {
- assert(j == 10);
- }
- body
- {
- for (i = 0; i < 10; i++)
- j++;
- }
-
- This is not implemented at this time.
-
-In, Out and Inheritance- - If a function in a derived class overrides a function in its - super class, then only one of - the in contracts of the base functions must be satisified - Overriding - functions then becomes a process of loosening the in - contracts. -- Conversely, all of the out contracts needs to be satisified, so - overriding functions becomes a processes of tightening the out - contracts. - - - - Class Invariants- - Class invariants are used to specify characteristics of a class that always - must be true (except while executing a member function). - They are described in Classes. - - -References-Contracts Reading List-Adding Contracts to Java - - |
Add feedback and comments regarding this - page.
- -|
+
+
+
+
+ + +· Lexical + + +· Modules + + +· Declarations + + +· Types + + +· Properties + + +· Attributes + + +· Pragmas + + +· Expressions + + +· Statements + + +· Arrays + + +· Structs & Unions + + +· Classes + + +· Interfaces + + +· Enums + + +· Functions + + +· Operator Overloading + + +· Templates + + +· Mixins + + +· Contracts + + +· Conditional Compilation + + +· Handling errors + + +· Garbage Collection + + +· Memory Management + + +· Floating Point + + +· Inline Assembler + + +· Documentation Comments + + +· Interfacing To C + + +· Portability Guide + + +· Embedding D in HTML + + +· Named Character Entities + + +· Application Binary Interface + + + + |
+
+
+
+Contract Programming+ + Contracts are a breakthrough technique to reduce the programming effort + for large projects. Contracts are the concept of preconditions, postconditions, + errors, and invariants. + Contracts can be done in C++ without modification to the language, + but the result is + clumsy and inconsistent. ++ + Building contract support into the language makes for: + +
+
+ The idea of a contract is simple - it's just an expression that must evaluate to true.
+ If it does not, the contract is broken, and by definition, the program has a bug in it.
+ Contracts form part of the specification for a program, moving it from the documentation
+ to the code itself. And as every programmer knows, documentation tends to be incomplete,
+ out of date, wrong, or non-existent. Moving the contracts into the code makes them
+ verifiable against the program.
+
+Assert Contract+ + The most basic contract is the + assert. + An assert inserts a checkable expression into + the code, and that expression must evaluate to true: +assert(expression);
+
+ C programmers will find it familiar. Unlike C, however, an assert
+ in function bodies
+ works by throwing an AssertException,
+ which can be caught and handled. Catching the contract violation is useful
+ when the code must deal with errant uses by other code, when it must be
+ failure proof, and as a useful tool for debugging.
+
+Pre and Post Contracts+ + The pre contracts specify the preconditions before a statement is executed. The most + typical use of this would be in validating the parameters to a function. The post + contracts validate the result of the statement. The most typical use of this + would be in validating the return value of a function and of any side effects it has. + The syntax is: + +in +{ + ...contract preconditions... +} +out (result) +{ + ...contract postconditions... +} +body +{ + ...code... +} ++ By definition, if a pre contract fails, then the body received bad parameters. + An InException is thrown. If a post contract fails, + then there is a bug in the body. An OutException is thrown. +
+ Either the long square_root(long x) + in + { + assert(x >= 0); + } + out (result) + { + assert((result * result) == x); + } + body + { + return math.sqrt(x); + } ++ The assert's in the in and out bodies are called contracts. + Any other D + statement or expression is allowed in the bodies, but it is important + to ensure that the + code has no side effects, and that the release version of the code + will not depend on any effects of the code. + For a release build of the code, the in and out code is not + inserted. + + If the function returns a void, there is no result, and so there can be no + result declaration in the out clause. + In that case, use: + void func() + out + { + ...contracts... + } + body + { + ... + } ++ In an out statement, result is initialized and set to the + return value of the function. +
+ The compiler can be adjusted to verify that every in and inout parameter is referenced
+ in the + The in-out statement can also be used inside a function, for example, it can be used + to check the results of a loop: + in +{ + assert(j == 0); +} +out +{ + assert(j == 10); +} +body +{ + for (i = 0; i < 10; i++) + j++; +} ++ This is not implemented at this time. + + In, Out and Inheritance+ + If a function in a derived class overrides a function in its + super class, then only one of + the in contracts of the base functions must be satisified + Overriding + functions then becomes a process of loosening the in + contracts. ++ Conversely, all of the out contracts needs to be satisified, so + overriding functions becomes a processes of tightening the out + contracts. + + + + Class Invariants+ + Class invariants are used to specify characteristics of a class that always + must be true (except while executing a member function). + They are described in Classes. + + +References+ +Contracts Reading List+Adding Contracts to Java + + + |
-
-Home
-| Search
-| D
-| Comments
-
-|
-
- - - · Overview - · D for Win32 - · Win32 DLLs in D - · C .h to D Modules - · FAQ - · Style Guide - · Example: wc - · Future - · D Change Log - · Tech Tips - · Glossary - · Acknowledgements - - Tools - - · DMD D Compiler - · GDC D Compiler - · Linker - · Profiler - - Community - - · News Digest - · News - · Forum - · Announcements - · Learn - · D links - - Archives - - · digitalmars.D - · digitalmars.D.dtl - · digitalmars.D.announce - · digitalmars.D.learn - · digitalmars.D.bugs - · D.gnu - · Old D - - - |
-
-
-
-
-Compiler for D Programming Language- - - -Files Common to Win32 and Linux- -
- Win32 D Compiler- -Files- -
Requirements- -
Installation- - Open a console window (for Windows XP this is done by - clicking on [Start][Command Prompt]). - All the tools are command line tools, which means - they are run from a console window. - Switch to the root directory. - Unzip the files in the root directory. - dmd.zip will create - a \dmd directory with all the files in it. - dmc.zip will create - a \dm directory with all the files in it. -- - A typical session might look like: - - - C:\Documents and Settings\Your Name>cd - C:\>unzip dmd.zip - C:\>unzip dmc.zip -- - Example- - Run: -- \dmd\bin\shell all.sh -- in the \dmd\samples\d directory for several small examples. - - Compiler Arguments and Switches- -
Linking- - Linking is done directly by the dmd compiler after a successful - compile. To prevent dmd from running the linker, use the - -c switch. -- - The programs must be linked with the D runtime library phobos.lib, - followed by the C runtime library snn.lib. - This is done automatically as long as the directories for the - libraries are on the LIB environment variable path. A typical - way to set LIB would be: - - set LIB=\dmd\lib;\dm\lib -- - Environment Variables- - The D compiler dmd uses the following environment variables: - -
SC.INI Initialization File- - dmd will look for the initialization file sc.ini in the same - directory dmd.exe resides in. If found, environment variable - settings in the file will override any existing settings. - This is handy to make dmd independent of programs with - conflicting use of environment variables. -- - Environment variables follow the [Environment] section - heading, in name=value pairs. Comments are lines that start with ;. - For example: - - ; sc.ini file for dmd - ; Names enclosed by %% are searched for in the existing environment - ; and inserted. The special name %@P% is replaced with the path - ; to this file. - [Environment] - LIB="%@P%\..\lib";\dm\lib - DFLAGS="-I%@P%\..\src\phobos" - LINKCMD="%@P%\..\..\dm\bin" - DDOCFILE=mysettings.ddoc -- - Common Installation Problems- -
- Linux D Compiler- -Files- -
Requirements- -
Installation- -
Compiler Arguments and Switches- -
Linking- - Linking is done directly by the dmd compiler after a successful - compile. To prevent dmd from running the linker, use the - -c switch. -- - The actual linking is done by running gcc. - This ensures compatibility with modules compiled with gcc. - - Environment Variables- - The D compiler dmd uses the following environment variables: - -
dmd.conf Initialization File- - dmd will look for the initialization file dmd.conf - in the directory /etc. If found, environment variable - settings in the file will override any existing settings. - This is handy to make dmd independent of programs with - conflicting use of environment variables. -- - Environment variables follow the [Environment] section - heading, in name=value pairs. Comments are lines that start with ;. - For example: - - ; dmd.conf file for dmd - ; Names enclosed by %% are searched for in the existing environment - ; and inserted. The special name %@P% is replaced with the path - ; to this file. - [Environment] - DFLAGS="-I%@P%\..\src\phobos" -- - Differences from Win32 version- -
Linux Bugs- -
- General- -Bugs- - These are some of the major bugs: - -
Questions?- - We welcome all feedback - kudos, flames, bugs, suggestions, hints, - and most especially donated code! Join the fray in the - D forum. - - |
Add feedback and comments regarding this - page.
- -|
+
+
+
+
+ + + · Overview + + + · D for Win32 + + + · Win32 DLLs in D + + + · C .h to D Modules + + + · FAQ + + + · Style Guide + + + · Example: wc + + + · Future + + + · D Change Log + + + · Tech Tips + + + · Rationale + + + · Glossary + + + · Acknowledgements + + + + Tools + + · DMD D Compiler + + + · GDC D Compiler + + + · Linker + + + · Profiler + + + + Community + + · News Digest + + + · News + + + · Forum + + + · Announcements + + + · Learn + + + · D links + + + + Archives + + · digitalmars.D + + + · digitalmars.D.dtl + + + · digitalmars.D.announce + + + · digitalmars.D.learn + + + · digitalmars.D.bugs + + + · D.gnu + + + · Old D + + + + + |
+
+
+
+Compiler for D Programming Language+ + + +Files Common to Win32 and Linux+ +
+ Win32 D Compiler+ +Files+ +
Requirements+ +
Installation+ + Open a console window (for Windows XP this is done by + clicking on [Start][Command Prompt]). + All the tools are command line tools, which means + they are run from a console window. + Switch to the root directory. + Unzip the files in the root directory. + dmd.zip will create + a \dmd directory with all the files in it. + dmc.zip will create + a \dm directory with all the files in it. ++ + A typical session might look like: + + C:\Documents and Settings\Your Name>cd \ +C:\>unzip dmd.zip +C:\>unzip dmc.zip ++ + Example+ + Run: + +\dmd\bin\shell all.sh ++ + in the \dmd\samples\d directory for several small examples. + + Compiler Arguments and Switches+ +
Linking+ + Linking is done directly by the dmd compiler after a successful + compile. To prevent dmd from running the linker, use the + -c switch. ++ + The programs must be linked with the D runtime library phobos.lib, + followed by the C runtime library snn.lib. + This is done automatically as long as the directories for the + libraries are on the LIB environment variable path. A typical + way to set LIB would be: + + set LIB=\dmd\lib;\dm\lib ++ + Environment Variables+ + The D compiler dmd uses the following environment variables: + +
SC.INI Initialization File+ + dmd will look for the initialization file sc.ini in the same + directory dmd.exe resides in. If found, environment variable + settings in the file will override any existing settings. + This is handy to make dmd independent of programs with + conflicting use of environment variables. ++ + Environment variables follow the [Environment] section + heading, in name=value pairs. Comments are lines that start with ;. + For example: + + ; sc.ini file for dmd +; Names enclosed by %% are searched for in the existing environment +; and inserted. The special name %@P% is replaced with the path +; to this file. +[Environment] +LIB="%@P%\..\lib";\dm\lib +DFLAGS="-I%@P%\..\src\phobos" +LINKCMD="%@P%\..\..\dm\bin" +DDOCFILE=mysettings.ddoc ++ + Common Installation Problems+ +
+ Linux D Compiler+ +Files+ +
Requirements+ +
Installation+ +
Compiler Arguments and Switches+ +
Linking+ + Linking is done directly by the dmd compiler after a successful + compile. To prevent dmd from running the linker, use the + -c switch. ++ + The actual linking is done by running gcc. + This ensures compatibility with modules compiled with gcc. + + Environment Variables+ + The D compiler dmd uses the following environment variables: + +
dmd.conf Initialization File+ + dmd will look for the initialization file dmd.conf + in the directory /etc. If found, environment variable + settings in the file will override any existing settings. + This is handy to make dmd independent of programs with + conflicting use of environment variables. ++ + Environment variables follow the [Environment] section + heading, in name=value pairs. Comments are lines that start with ;. + For example: + + ; dmd.conf file for dmd +; Names enclosed by %% are searched for in the existing environment +; and inserted. The special name %@P% is replaced with the path +; to this file. +[Environment] +DFLAGS="-I%@P%\..\src\phobos" ++ + Differences from Win32 version+ +
Linux Bugs+ +
+ General+ +Bugs+ + These are some of the major bugs: + +
Questions?+ + We welcome all feedback - kudos, flames, bugs, suggestions, hints, + and most especially donated code! Join the fray in the + D forum. + + + |
-
-Home
-| Search
-| D
-| Comments
-
-|
-
- - - · Overview - · D for Win32 - · Win32 DLLs in D - · C .h to D Modules - · FAQ - · Style Guide - · Example: wc - · Future - · D Change Log - · Tech Tips - · Glossary - · Acknowledgements - - Tools - - · DMD D Compiler - · GDC D Compiler - · Linker - · Profiler - - Community - - · News Digest - · News - · Forum - · Announcements - · Learn - · D links - - Archives - - · digitalmars.D - · digitalmars.D.dtl - · digitalmars.D.announce - · digitalmars.D.learn - · digitalmars.D.bugs - · D.gnu - · Old D - - - |
-
-
-
-Embedded Documentation- - -The D programming language enables embedding both contracts and test -code along side the actual code, which helps to keep them all -consistent with each other. One thing lacking is the documentation, -as ordinary comments are usually unsuitable for automated extraction -and formatting into manual pages. -Embedding the user documentation into the source code has important -advantages, such as not having to write the documentation twice, and -the likelihood of the documentation staying consistent with the code. -- -Some existing approaches to this are: - -
Specification- -The specification for the form of embedded documentation comments only -specifies how information is to be presented to the compiler. -It is implementation-defined how that information is used and the form -of the final presentation. Whether the final presentation form is an -HTML web page, a man page, a PDF file, etc. is not specified as part of the -D Programming Language. -- - Phases of Processing- -Embedded documentation comments are processed in a series of phases: - -
Lexical- -Embedded documentation comments are one of the following forms: - -
-/// This is a one line documentation comment. - -/** So is this. */ - -/++ And this. +/ - -/** - This is a brief documentation comment. - */ - -/** - * The leading * on this line is not part of the documentation comment. - */ - -/********************************* - The extra *'s immediately following the /** are not - part of the documentation comment. - */ - -/++ - This is a brief documentation comment. - +/ - -/++ - + The leading + on this line is not part of the documentation comment. - +/ - -/+++++++++++++++++++++++++++++++++ - The extra +'s immediately following the /++ are not - part of the documentation comment. - +/ - -/**************** Closing *'s are not part *****************/ - -- -The extra *'s and +'s on the comment opening, closing and left margin are -ignored and are not part -of the embedded documentation. -Comments not following one of those forms are not documentation comments. - - - Parsing- -Each documentation comment is associated with a declaration. -If the documentation comment is on a line by itself or with only whitespace -to the left, it refers to the next -declaration. -Multiple documentation comments applying to the same declaration -are concatenated. -Documentation comments not associated with a declaration are ignored. -Documentation comments preceding the ModuleDeclaration apply to the -entire module. -If the documentation comment appears on the same line to the right of a -declaration, it applies to that. -- -If a documentation comment for a declaration consists only of the -identifier ditto -then the documentation comment for the previous declaration at the same -declaration scope is applied to this declaration as well. - - -If there is no documentation comment for a declaration, that declaration -may not appear in the output. To ensure it does appear in the output, -put an empty declaration comment for it. - -
-int a; /// documentation for a; b has no documentation
-int b;
-
-/** documentation for c and d */
-/** more documentation for c and d */
-int c;
-/** ditto */
-int d;
-
-/** documentation for e and f */ int e;
-int f; /// ditto
-
-/** documentation for g */
-int g; /// more documentation for g
-
-/// documentation for C and D
-class C
-{
- int x; /// documentation for C.x
-
- /** documentation for C.y and C.z */
- int y;
- int z; /// ditto
-}
-
-/// ditto
-class D
-{
-}
-
-
-
-
-Sections- -The document comment is a series of Sections. -A Section is a name that is the first non-blank character on -a line immediately followed by a ':'. This name forms the section name. -The section name is not case sensitive. -- - Summary- -The first section is the Summary, and does not have a section name. -It is first paragraph, up to a blank line or a section name. -While the summary can be any length, try to keep it to one line. -The Summary section is optional. - -Description- -The next unnamed section is the Description. -It consists of all the paragraphs following the Summary until -a section name is encountered or the end of the comment. -- -While the Description section is optional, -there cannot be a Description without a Summary section. - - -
-/***********************************
- * Brief summary of what
- * myfunc does, forming the summary section.
- *
- * First paragraph of synopsis description.
- *
- * Second paragraph of
- * synopsis description.
- */
-
-void myfunc() { }
-
-
-Named sections follow the Summary and Description unnamed sections.
-
-
-
-Standard Sections- -For consistency and predictability, there are several standard sections. -None of these are required to be present. - -
Special Sections- -Some sections have specialized meanings and syntax. - -
Highlighting- - - -Embedded Comments- - The documentation comments can themselves be commented using - the <!-- comment text --> syntax. These comments do not - nest. - -Embedded Code- - D code can be embedded using lines with at least three -'s in - them to delinate the code section: - -
-/++++++++++++++++++++++++
- + Our function.
- + Example:
- + --------------------------
- + #include <stdio.h>
- +
- + void foo()
- + {
- + printf("foo!\n"); /* print the string */
- + }
- + --------------------------
- +/
-
-
- Note that documentation comment uses the /++ ... +/ form
- so that /* ... */ can be used inside the code section.
-
-Embedded HTML- -HTML can be embedded into the documentation comments, and it will -be passed through to the HTML output unchanged. -However, since it is not necessarily true that HTML will be the desired -output format of the embedded documentation comment extractor, it is -best to avoid using it where practical. - --/** Example of embedded HTML: - * <ol> - * <li> <a href="www.digitalmars.com">Digital Mars</a> - * <li> <a href="www.classicempire.com">Empire</a> - * </ol> - */ -- - - Emphasis- -Identifiers in documentation comments that are function parameters or are -names that are in scope at the associated declaration are emphasized in -the output. -This emphasis can take the form of italics, boldface, a hyperlink, etc. -How it is emphasized depends on what it is - a function parameter, type, -D keyword, etc. -To prevent unintended emphasis of an identifier, it can be preceded by -an underscore (_). The underscore will be stripped from the output. - - -Character Entities- - Because the characters <, > and & have special meaning - to the documentation processor, to avoid confusion it can be best - to replace them with their corresponding character entities: -- -
- - It is not necessary to do this inside a code section, or if the - special character is not immediately followed by a # or a letter. - - Macros- - The documentation comment processor includes a simple macro - text preprocessor. - When a appears - in section text it is replaced with NAME's corresponding - replacment text. - The replacement text is then recursively scanned for more macros. - If a macro is recursively encountered, with no argument or with - the same argument text as the enclosing macro, it is replaced - with no text. - Macro invocations that cut across replacement text boundaries are - not expanded. - If the macro name is undefined, the replacement text has no characters - in it. - If a is desired to exist in the output without being macro - expanded, add an extra $ as in: $(NAME). The extra $ will be elided. -- - Macros can have arguments. Any text from the end of the identifier - to the closing ')' is the $0 argument. A $0 in the replacement text is - replaced with the argument text. - If there are commas in the argument text, $1 will represent the - argument text up to the first comma, $2 from the first comma to - the second comma, etc., up to $9. - $+ represents the text from the first comma to the closing ')'. - The argument text can contain nested parentheses, "" or '' strings, - comments, or tags. - - - Macro definitions come from the following sources, - in the specified order: - -
- - Macro names beginning with "D_" and "DDOC_" are reserved. - - Predefined Macros- - These are hardwired into Ddoc, and represent the - minimal definitions needed by Ddoc to format and highlight - the presentation. - The definitions are for simple HTML. - --B = <b>$0</b> -I = <i>$0</i> -U = <u>$0</u> -P = <p>$0</p> -DL = <dl>$0</dl> -DT = <dt>$0</dt> -DD = <dd>$0</dd> -TABLE = <table>$0</table> -TR = <tr>$0</tr> -TH = <th>$0</th> -TD = <td>$0</td> -OL = <ol>$0</ol> -UL = <ul>$0</ul> -LI = <li>$0</li> -BIG = <big>$0</big> -SMALL = <small>$0</small> -BR = <br> -LINK = <a href="$0">$0</a> -LINK2 = <a href="$1">$+</a> - -RED = <font color=red>$0</font> -BLUE = <font color=blue>$0</font> -GREEN = <font color=green>$0</font> -YELLOW =<font color=yellow>$0</font> -BLACK = <font color=black>$0</font> -WHITE = <font color=white>$0</font> - -D_CODE = <pre class="d_code">$0</pre> - -DDOC = <html><head> - <META http-equiv="content-type" content="text/html; charset=utf-8"> - <title>$(TITLE)</title> - </head><body> - <h1>$(TITLE)</h1> - $(BODY) - </body></html> - -DDOC_DECL = $(DT $(BIG $0)) -DDOC_DECL_DD = $(DD $0) -DDOC_SECTIONS = $0 -DDOC_SUMMARY = $0$(BR)$(BR) -DDOC_DESCRIPTION = $0$(BR)$(BR) -DDOC_AUTHORS = $(B Authors:)$(BR) - $0$(BR)$(BR) -DDOC_BUGS = $(RED BUGS:)$(BR) - $0$(BR)$(BR) -DDOC_COPYRIGHT = $(B Copyright:)$(BR) - $0$(BR)$(BR) -DDOC_DATE = $(B Date:)$(BR) - $0$(BR)$(BR) -DDOC_DEPRECATED = $(RED Deprecated:)$(BR) - $0$(BR)$(BR) -DDOC_EXAMPLES = $(B Examples:)$(BR) - $0$(BR)$(BR) -DDOC_HISTORY = $(B History:)$(BR) - $0$(BR)$(BR) -DDOC_LICENSE = $(B License:)$(BR) - $0$(BR)$(BR) -DDOC_RETURNS = $(B Returns:)$(BR) - $0$(BR)$(BR) -DDOC_SEE_ALSO = $(B See Also:)$(BR) - $0$(BR)$(BR) -DDOC_STANDARDS = $(B Standards:)$(BR) - $0$(BR)$(BR) -DDOC_THROWS = $(B Throws:)$(BR) - $0$(BR)$(BR) -DDOC_VERSION = $(B Version:)$(BR) - $0$(BR)$(BR) -DDOC_SECTION_H = $(B $0)$(BR)$(BR) -DDOC_SECTION = $0$(BR)$(BR) -DDOC_MEMBERS = $(DL $0) -DDOC_PARAMS = $(TABLE $0)$(BR)$(BR) -DDOC_PARAM_ROW = $(TR $0) -DDOC_PARAM_ID = $(TD $0) -DDOC_PARAM_DESC = $(TD $0) - -DDOC_PSYMBOL = $(U $0) -DDOC_KEYWORD = $(B $0) -DDOC_PARAM = $(I $0) -- - Ddoc does not generate HTML code. It formats into the basic - formatting macros, which (in their predefined form) - are then expanded into HTML. - If output other than HTML is desired, then these macros - need to be redefined. - - -
- - DDOC is special in that it specifies the boilerplate into - which the entire generated text is inserted (represented by the - Ddoc generated macro BODY). For example, in order - to use a style sheet, DDOC would be redefined as: - - -DDOC = <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd"> - <html><head> - <META http-equiv="content-type" content="text/html; charset=utf-8"> - <title>$(TITLE)</title> - <link rel="stylesheet" type="text/css" href="style.css"> - </head><body> - <h1>$(TITLE)</h1> - $(BODY) - </body></html> -- - The highlighting macros start with DDOC_. - They control the formatting of individual parts of the presentation. - - -
- - For example, one could redefine DDOC_SUMMARY: - - -DDOC_SECTIONS = $(GREEN $0) -- - And all the description sections will now be green. - - Macro Definitions from sc.ini's DDOCFILE- - A text file of macro definitions can be created, - and specified in sc.ini: - --DDOCFILE=myproject.ddoc -- - Macro Definitions from .ddoc Files on the Command Line- - File names on the DMD command line with the extension - .ddoc are text files that are read and processed in order. - -Macro Definitions Generated by Ddoc- -
|
Add feedback and comments regarding this - page.
- -|
+
+
+
+
+ + +· Lexical + + +· Modules + + +· Declarations + + +· Types + + +· Properties + + +· Attributes + + +· Pragmas + + +· Expressions + + +· Statements + + +· Arrays + + +· Structs & Unions + + +· Classes + + +· Interfaces + + +· Enums + + +· Functions + + +· Operator Overloading + + +· Templates + + +· Mixins + + +· Contracts + + +· Conditional Compilation + + +· Handling errors + + +· Garbage Collection + + +· Memory Management + + +· Floating Point + + +· Inline Assembler + + +· Documentation Comments + + +· Interfacing To C + + +· Portability Guide + + +· Embedding D in HTML + + +· Named Character Entities + + +· Application Binary Interface + + + + |
+
+Embedded Documentation+ +The D programming language enables embedding both contracts and test +code along side the actual code, which helps to keep them all +consistent with each other. One thing lacking is the documentation, +as ordinary comments are usually unsuitable for automated extraction +and formatting into manual pages. +Embedding the user documentation into the source code has important +advantages, such as not having to write the documentation twice, and +the likelihood of the documentation staying consistent with the code. + + +Some existing approaches to this are: + + +
D's goals for embedded documentation are: + + +
Specification+ +The specification for the form of embedded documentation comments only +specifies how information is to be presented to the compiler. +It is implementation-defined how that information is used and the form +of the final presentation. Whether the final presentation form is an +HTML web page, a man page, a PDF file, etc. is not specified as part of the +D Programming Language. + + +Phases of Processing+ +Embedded documentation comments are processed in a series of phases: + + +
Lexical+ +Embedded documentation comments are one of the following forms: + + +
The following are all embedded documentation comments: + +/// This is a one line documentation comment. + +/** So is this. */ + +/++ And this. +/ + +/** + This is a brief documentation comment. + */ + +/** + * The leading * on this line is not part of the documentation comment. + */ + +/********************************* + The extra *'s immediately following the /** are not + part of the documentation comment. + */ + +/++ + This is a brief documentation comment. + +/ + +/++ + + The leading + on this line is not part of the documentation comment. + +/ + +/+++++++++++++++++++++++++++++++++ + The extra +'s immediately following the / ++ are not + part of the documentation comment. + +/ + +/**************** Closing *'s are not part *****************/ ++ + The extra *'s and +'s on the comment opening, closing and left margin are +ignored and are not part +of the embedded documentation. +Comments not following one of those forms are not documentation comments. + + +Parsing+ +Each documentation comment is associated with a declaration. +If the documentation comment is on a line by itself or with only whitespace +to the left, it refers to the next +declaration. +Multiple documentation comments applying to the same declaration +are concatenated. +Documentation comments not associated with a declaration are ignored. +Documentation comments preceding the ModuleDeclaration apply to the +entire module. +If the documentation comment appears on the same line to the right of a +declaration, it applies to that. + + +If a documentation comment for a declaration consists only of the +identifier ditto +then the documentation comment for the previous declaration at the same +declaration scope is applied to this declaration as well. + + +If there is no documentation comment for a declaration, that declaration +may not appear in the output. To ensure it does appear in the output, +put an empty declaration comment for it. + + +int a; /// documentation for a; b has no documentation +int b; + +/** documentation for c and d */ +/** more documentation for c and d */ +int c; +/** ditto */ +int d; + +/** documentation for e and f */ int e; +int f; /// ditto + +/** documentation for g */ +int g; /// more documentation for g + +/// documentation for C and D +class C +{ + int x; /// documentation for C.x + + /** documentation for C.y and C.z */ + int y; + int z; /// ditto +} + +/// ditto +class D +{ +} ++ + Sections+ +The document comment is a series of Sections. +A Section is a name that is the first non-blank character on +a line immediately followed by a ':'. This name forms the section name. +The section name is not case sensitive. + + +Summary+ +The first section is the Summary, and does not have a section name. +It is first paragraph, up to a blank line or a section name. +While the summary can be any length, try to keep it to one line. +The Summary section is optional. + + +Description+ +The next unnamed section is the Description. +It consists of all the paragraphs following the Summary until +a section name is encountered or the end of the comment. + + +While the Description section is optional, +there cannot be a Description without a Summary section. + + +/*********************************** + * Brief summary of what + * myfunc does, forming the summary section. + * + * First paragraph of synopsis description. + * + * Second paragraph of + * synopsis description. + */ + +void myfunc() { } ++ + Named sections follow the Summary and Description unnamed sections. + + +Standard Sections+ +For consistency and predictability, there are several standard sections. +None of these are required to be present. + + +
Special Sections+ +Some sections have specialized meanings and syntax. + + +
Highlighting+ +Embedded Comments+ +The documentation comments can themselves be commented using + the <!-- comment text --> syntax. These comments do not + nest. + + +Embedded Code+ +D code can be embedded using lines with at least three -'s in + them to delineate the code section: + + + /++++++++++++++++++++++++
+ + Our function.
+ + Example:
+ + --------------------------
+ + #include <stdio.h>
+ +
+ + void foo()
+ + {
+ + printf("foo!\n"); /* print the string */
+ + }
+ + --------------------------
+ +/
+
+
+Note that documentation comment uses the /++ ... +/ form + so that /* ... */ can be used inside the code section. + + +Embedded HTML+ +HTML can be embedded into the documentation comments, and it will +be passed through to the HTML output unchanged. +However, since it is not necessarily true that HTML will be the desired +output format of the embedded documentation comment extractor, it is +best to avoid using it where practical. + + +/** Example of embedded HTML:
+ * <ol>
+ * <li> <a href="www.digitalmars.com">Digital Mars</a>
+ * <li> <a href="www.classicempire.com">Empire</a>
+ * </ol>
+ */
+
+
+Emphasis+ +Identifiers in documentation comments that are function parameters or are +names that are in scope at the associated declaration are emphasized in +the output. +This emphasis can take the form of italics, boldface, a hyperlink, etc. +How it is emphasized depends on what it is - a function parameter, type, +D keyword, etc. +To prevent unintended emphasis of an identifier, it can be preceded by +an underscore (_). The underscore will be stripped from the output. + + +Character Entities+ +Some characters have special meaning + to the documentation processor, to avoid confusion it can be best + to replace them with their corresponding character entities: + + +
It is not necessary to do this inside a code section, or if the + special character is not immediately followed by a # or a letter. + + +Macros+ +The documentation comment processor includes a simple macro + text preprocessor. + When a $(NAME) appears + in section text it is replaced with NAME's corresponding + replacment text. + The replacement text is then recursively scanned for more macros. + If a macro is recursively encountered, with no argument or with + the same argument text as the enclosing macro, it is replaced + with no text. + Macro invocations that cut across replacement text boundaries are + not expanded. + If the macro name is undefined, the replacement text has no characters + in it. + If a $(NAME) is desired to exist in the output without being + macro expanded, the $ should be replaced with $. + + +Macros can have arguments. Any text from the end of the identifier + to the closing ')' is the $0 argument. + A $0 in the replacement text is + replaced with the argument text. + If there are commas in the argument text, $1 will represent the + argument text up to the first comma, $2 from the first comma to + the second comma, etc., up to $9. + $+ represents the text from the first comma to the closing ')'. + The argument text can contain nested parentheses, "" or '' strings, + comments, or tags. + If stray, unnested parentheses are used, they can be replaced with + the entity ( for ( and ) for ). + + +Macro definitions come from the following sources, + in the specified order: + + +
Macro redefinitions replace previous definitions of the same name. + This means that the sequence of macro definitions from the various + sources forms a heirarchy. + + +Macro names beginning with "D_" and "DDOC_" are reserved. + + +Predefined Macros+ +These are hardwired into Ddoc, and represent the + minimal definitions needed by Ddoc to format and highlight + the presentation. + The definitions are for simple HTML. + + +B = <b>$0</b> +I = <i>$0</i> +U = <u>$0</u> +P = <p>$0</p> +DL = <dl>$$0</dl> +DT = <dt>$0</dt> +DD = <dd>$0</dd> +TABLE = <table>$0</table> +TR = <tr>$0</tr> +TH = <th>$0</th> +TD = <td>$0</td> +OL = <ol>$0</ol> +UL = <ul>$0</ul> +LI = <li>$0</li> +BIG = <big>$0</big> +SMALL = <small>$0</small> +BR = <br> +LINK = <a href="$0">$0</a> +LINK2 = <a href="$1">$+</a> + +RED = <font color=red>$0</font> +BLUE = <font color=blue>$0</font> +GREEN = <font color=green>$0</font> +YELLOW =<font color=yellow>$0</font> +BLACK = <font color=black>$0</font> +WHITE = <font color=white>$0</font> + +D_CODE = <pre class="d_code">$0</pre> +D_COMMENT = $(GREEN $0) +D_STRING = $(RED $0) +D_KEYWORD = $(BLUE $0) +D_PSYMBOL = $(U $0) +D_PARAM = $(I $0) + +DDOC = <html><head> + <META http-equiv="content-type" content="text/html; charset=utf-8"> + <title>$(TITLE)</title> + </head><body> + <h1>$(TITLE)</h1> + $(BODY) + </body></html> + +DDOC_DECL = $(DT $(BIG $0)) +DDOC_DECL_DD = $(DD $0) +DDOC_SECTIONS = $0 +DDOC_SUMMARY = $0$(BR)$(BR) +DDOC_DESCRIPTION = $0$(BR)$(BR) +DDOC_AUTHORS = $(B Authors:)$(BR) + $0$(BR)$(BR) +DDOC_BUGS = $(RED BUGS:)$(BR) + $0$(BR)$(BR) +DDOC_COPYRIGHT = $(B Copyright:)$(BR) + $0$(BR)$(BR) +DDOC_DATE = $(B Date:)$(BR) + $0$(BR)$(BR) +DDOC_DEPRECATED = $(RED Deprecated:)$(BR) + $0$(BR)$(BR) +DDOC_EXAMPLES = $(B Examples:)$(BR) + $0$(BR)$(BR) +DDOC_HISTORY = $(B History:)$(BR) + $0$(BR)$(BR) +DDOC_LICENSE = $(B License:)$(BR) + $0$(BR)$(BR) +DDOC_RETURNS = $(B Returns:)$(BR) + $0$(BR)$(BR) +DDOC_SEE_ALSO = $(B See Also:)$(BR) + $0$(BR)$(BR) +DDOC_STANDARDS = $(B Standards:)$(BR) + $0$(BR)$(BR) +DDOC_THROWS = $(B Throws:)$(BR) + $0$(BR)$(BR) +DDOC_VERSION = $(B Version:)$(BR) + $0$(BR)$(BR) +DDOC_SECTION_H = $(B $0)$(BR)$(BR) +DDOC_SECTION = $0$(BR)$(BR) +DDOC_MEMBERS = $(DL $0) +DDOC_PARAMS = $(TABLE $0)$(BR)$(BR) +DDOC_PARAM_ROW = $(TR $0) +DDOC_PARAM_ID = $(TD $0) +DDOC_PARAM_DESC = $(TD $0) +DDOC_BLANKLINE = $(BR)$(BR) + +DDOC_PSYMBOL = $(U $0) +DDOC_KEYWORD = $(B $0) +DDOC_PARAM = $(I $0) ++ + Ddoc does not generate HTML code. It formats into the basic + formatting macros, which (in their predefined form) + are then expanded into HTML. + If output other than HTML is desired, then these macros + need to be redefined. + + +
+ + DDOC is special in that it specifies the boilerplate into + which the entire generated text is inserted (represented by the + Ddoc generated macro BODY). For example, in order + to use a style sheet, DDOC would be redefined as: + + +DDOC = <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd"> + <html><head> + <META http-equiv="content-type" content="text/html; charset=utf-8"> + <title>$(TITLE)</title> + <link rel="stylesheet" type="text/css" href="style.css"> + </head><body> + <h1>$(TITLE)</h1> + $(BODY) + </body></html> ++ + Highlighting of D code is performed by the following macros: + + +
The highlighting macros start with DDOC_. + They control the formatting of individual parts of the presentation. + + +
+ + For example, one could redefine DDOC_SUMMARY: + + +DDOC_SUMMARY = $(GREEN $0) ++ + And all the summary sections will now be green. + + +Macro Definitions from sc.ini's DDOCFILE+ +A text file of macro definitions can be created, + and specified in sc.ini: + + +DDOCFILE=myproject.ddoc ++ + Macro Definitions from .ddoc Files on the Command Line+ +File names on the DMD command line with the extension + .ddoc are text files that are read and processed in order. + + +Macro Definitions Generated by Ddoc+ +
Using Ddoc for other Documentation+ +Ddoc is primarilly designed for use in producing documentation + from embedded comments. It can also, however, be used for + processing other general documentation. + The reason for doing this would be to take advantage of the + macro capability of Ddoc and the D code syntax highlighting + capability. + + +If the .d source file starts with the string "Ddoc" then it + is treated as general purpose documentation, not as a D + code source file. From immediately after the "Ddoc" string + to the end of the file or any "Macros:" section forms + the document. No automatic highlighting is done to that text, + other than highlighting of D code embedded between lines + delineated with --- lines. Only macro processing is done. + + +Much of the D documentation itself is generated this way, + including this page. + Such documentation is marked at the bottom as being + generated by Ddoc. + + + |
-
-Home
-| Search
-| D
-| Comments
-
-|
-
- - -· Lexical -· Modules -· Declarations -· Types -· Properties -· Attributes -· Pragmas -· Expressions -· Statements -· Arrays -· Structs & Unions -· Classes -· Interfaces -· Enums -· Functions -· Operator Overloading -· Templates -· Mixins -· Contracts -· Conditional Compilation -· Handling errors -· Garbage Collection -· Memory Management -· Floating Point -· Inline Assembler -· Documentation Comments -· Interfacing To C -· Portability Guide -· Embedding D in HTML -· Named Character Entities -· Application Binary Interface - - |
-
-
-
-Declarations- --Declaration: - typedef Decl - alias Decl - Decl - -Decl: - StorageClass Decl - BasicType Declarators ; - BasicType Declarator FunctionBody - -Declarators: - DeclaratorInitializer - DeclaratorInitializer , DeclaratorIdentifierList - -DeclaratorInitializer: - Declarator - Declarator = Initializer - -DeclaratorIdentifierList: - DeclaratorIdentifier - DeclaratorIdentifier , DeclaratorIdentifierList - -DeclaratorIdentifier: - Identifier - Identifier = Initializer - -BasicType: - bit - byte - ubyte - short - ushort - int - uint - long - ulong - char - wchar - dchar - float - double - real - ifloat - idouble - ireal - cfloat - cdouble - creal - void - .IdentifierList - IdentifierList - Typeof - Typeof . IdentifierList - -BasicType2: - * - [ ] - [ Expression ] - [ Type ] - delegate ( ParameterList ) - function ( ParameterList ) - -Declarator: - BasicType2 Declarator - Identifier - ( Declarator ) - Identifier DeclaratorSuffixes - ( Declarator ) DeclaratorSuffixes - -DeclaratorSuffixes: - DeclaratorSuffix - DeclaratorSuffix DeclaratorSuffixes - -DeclaratorSuffix: - [ ] - [ Expression ] - [ Type ] - ( ParameterList ) - -IdentifierList: - Identifier - Identifier . IdentifierList - TemplateInstance - TemplateInstance . IdentifierList - -Typeof: - typeof ( Expression ) - -StorageClass: - abstract - auto - const - deprecated - final - override - static - synchronized - -Type: - BasicType - BasicType Declarator2 - -Declarator2: - BasicType2 Declarator2 - ( Declarator2 ) - ( Declarator2 ) DeclaratorSuffixes - -ParameterList: - Parameter - Parameter , ParameterList - ... - -Parameter: - Declarator - Declarator = AssignExpression - InOut Declarator - InOut Declarator = AssignExpression - -InOut: - in - out - inout - -Initializer: - void - AssignExpression - ArrayInitializer - StructInitializer - -- - Declaration Syntax- -Declaration syntax generally reads right to left: - --int x; // x is an int -int* x; // x is a pointer to int -int** x; // x is a pointer to a pointer to int -int[] x; // x is an array of ints -int*[] x; // x is an array of pointers to ints -int[]* x; // x is a pointer to an array of ints -- - Arrays read right to left as well: - --int[3] x; // x is an array of 3 ints -int[3][5] x; // x is an array of 5 arrays of 3 ints -int[3]*[5] x; // x is an array of 5 pointers to arrays of 3 ints -- - -Pointers to functions are declared using the function keyword: - - --int function(char) x; // x is a pointer to a function taking a char argument - // and returning an int -int function(char)[] x; // x is an array of pointers to functions - // taking a char argument and returning an int -- - -C-style array declarations may be used as an alternative: - - --int x[3]; // x is an array of 3 ints -int x[3][5]; // x is an array of 3 arrays of 5 ints -int (*x[5])[3]; // x is an array of 5 pointers to arrays of 3 ints -int (*x)(char); // x is a pointer to a function taking a char argument - // and returning an int -int (*[] x)(char); // x is an array of pointers to functions - // taking a char argument and returning an int -- - -In a declaration declaring multiple symbols, all the declarations -must be of the same type: - - --int x,y; // x and y are ints -int* x,y; // x and y are pointers to ints -int x,*y; // error, multiple types -int[] x,y; // x and y are arrays of ints -int x[],y; // error, multiple types -- - Type Defining- -- Strong types can be introduced with the typedef. Strong types are semantically a - distinct type to the type checking system, for function overloading, and for the debugger. - - -
-typedef int myint;
-
-void foo(int x) { . }
-void foo(myint m) { . }
-
- .
-myint b;
-foo(b); // calls foo(myint)
-
-
-Typedefs can specify a default initializer different from the
-default initializer of the underlying type:
-
--typedef int myint = 7; -myint m; // initialized to 7 -- - - Type Aliasing- -- It's sometimes convenient to use an alias for a type, such as a shorthand for typing - out a long, complex type like a pointer to a function. In D, this is done with the - alias declaration: - - --alias abc.Foo.bar myint; -- - - Aliased types are semantically identical to the types they are aliased to. The - debugger cannot distinguish between them, and there is no difference as far as function - overloading is concerned. For example: - - -
-alias int myint;
-
-void foo(int x) { . }
-void foo(myint m) { . } error, multiply defined function foo
-
-
- - Type aliases are equivalent to the C typedef. - - -Alias Declarations- -- A symbol can be declared as an alias of another symbol. - For example: - - -
-import string;
-
-alias string.strlen mylen;
- ...
-int len = mylen("hello"); // actually calls string.strlen()
-
-
- - The following alias declarations are valid: - - -
-template Foo2(T) { alias T t; }
-alias Foo2!(int) t1;
-alias Foo2!(int).t t2;
-alias t1.t t3;
-alias t2 t4;
-
-t1.t v1; // v1 is type int
-t2 v2; // v2 is type int
-t3 v3; // v3 is type int
-t4 v4; // v4 is type int
-
-
- - Aliased symbols are useful as a shorthand for a long qualified - symbol name, or as a way to redirect references from one symbol - to another: - - -
-version (Win32)
-{
- alias win32.foo myfoo;
-}
-version (linux)
-{
- alias linux.bar myfoo;
-}
-
-
- - Aliasing can be used to 'import' a symbol from an import into the - current scope: - - --alias string.strlen strlen; -- - - Aliases can also 'import' a set of overloaded functions, that can - be overloaded with functions in the current scope: - - -
-class A {
- int foo(int a) { return 1; }
-}
-
-class B : A {
- int foo( int a, uint b ) { return 2; }
-}
-
-class C : B {
- int foo( int a ) { return 3; }
- alias B.foo foo;
-}
-
-class D : C {
-}
-
-
-void test()
-{
- D b = new D();
- int i;
-
- i = b.foo(1, 2u); // calls B.foo
- i = b.foo(1); // calls C.foo
-}
-
-
- - Note: Type aliases can sometimes look indistinguishable from - alias declarations: - - --alias foo.bar abc; // is it a type or a symbol? -- - - The distinction is made in the semantic analysis pass. - - -typeof- -- Typeof is a way to specify a type based on the type - of an expression. For example: - - -
-void func(int i)
-{
- typeof(i) j; // j is of type int
- typeof(3 + 6.0) x; // x is of type double
- typeof(1)* p; // p is of type pointer to int
- int[typeof(p)] a; // a is of type int[int*]
-
- printf("%d\n", typeof('c').sizeof); // prints 1
- double c = cast(typeof(1.0))j; // cast j to double
-}
-
-
- - Expression is not evaluated, just the type of it is - generated: - - -
-void func()
-{ int i = 1;
- typeof(++i) j; // j is declared to be an int, i is not incremented
- printf("%d\n", i); // prints 1
-}
-
-
- - There are two special cases: - typeof(this) will generate the type of what this - would be in a non-static member function, even if not in a member - function. - Analogously, typeof(super) will generate the type of what - super would be in a non-static member function. - - -
-class A { }
-
-class B : A
-{
- typeof(this) x; // x is declared to be a B
- typeof(super) y; // y is declared to be an A
-}
-
-struct C
-{
- typeof(this) z; // z is declared to be a C*
- typeof(super) q; // error, no super struct for C
-}
-
-typeof(this) r; // error, no enclosing struct or class
-
-
- - Where Typeof is most useful is in writing generic - template code. - - - |
Add feedback and comments regarding this - page.
- -|
+
+
+
+
+ + +· Lexical + + +· Modules + + +· Declarations + + +· Types + + +· Properties + + +· Attributes + + +· Pragmas + + +· Expressions + + +· Statements + + +· Arrays + + +· Structs & Unions + + +· Classes + + +· Interfaces + + +· Enums + + +· Functions + + +· Operator Overloading + + +· Templates + + +· Mixins + + +· Contracts + + +· Conditional Compilation + + +· Handling errors + + +· Garbage Collection + + +· Memory Management + + +· Floating Point + + +· Inline Assembler + + +· Documentation Comments + + +· Interfacing To C + + +· Portability Guide + + +· Embedding D in HTML + + +· Named Character Entities + + +· Application Binary Interface + + + + |
+
+
+Declarations+ +Declaration: + typedef Decl + alias Decl + Decl + +Decl: + StorageClass Decl + BasicType Declarators ; + BasicType Declarator FunctionBody + +Declarators: + DeclaratorInitializer + DeclaratorInitializer , DeclaratorIdentifierList + +DeclaratorInitializer: + Declarator + Declarator = Initializer + +DeclaratorIdentifierList: + DeclaratorIdentifier + DeclaratorIdentifier , DeclaratorIdentifierList + +DeclaratorIdentifier: + Identifier + Identifier = Initializer + +BasicType: + bit + byte + ubyte + short + ushort + int + uint + long + ulong + char + wchar + dchar + float + double + real + ifloat + idouble + ireal + cfloat + cdouble + creal + void + .IdentifierList + IdentifierList + Typeof + Typeof . IdentifierList + +BasicType2: + * + [ ] + [ Expression ] + [ Type ] + delegate () ParameterList + function () ParameterList + +Declarator: + BasicType2 Declarator + Identifier + () Declarator + Identifier DeclaratorSuffixes + () Declarator DeclaratorSuffixes + +DeclaratorSuffixes: + DeclaratorSuffix + DeclaratorSuffix DeclaratorSuffixes + +DeclaratorSuffix: + [ ] + [ Expression ] + [ Type ] + () ParameterList + +IdentifierList: + Identifier + Identifier . IdentifierList + TemplateInstance + TemplateInstance . IdentifierList + +Typeof: + typeof () Expression + +StorageClass: + abstract + auto + const + deprecated + final + override + static + synchronized + +Type: + BasicType + BasicType Declarator2 + +Declarator2: + BasicType2 Declarator2 + () Declarator2 + () Declarator2 DeclaratorSuffixes + +ParameterList: + Parameter + Parameter , ParameterList + ... + +Parameter: + Declarator + Declarator = AssignExpression + InOut Declarator + InOut Declarator = AssignExpression + +InOut: + in + out + inout + +Initializer: + void + AssignExpression + ArrayInitializer + StructInitializer + ++ + Declaration Syntax+ +Declaration syntax generally reads right to left: + +int x; // x is an int +int* x; // x is a pointer to int +int** x; // x is a pointer to a pointer to int +int[] x; // x is an array of ints +int*[] x; // x is an array of pointers to ints +int[]* x; // x is a pointer to an array of ints ++ + Arrays read right to left as well: + +int[3] x; // x is an array of 3 ints +int[3][5] x; // x is an array of 5 arrays of 3 ints +int[3]*[5] x; // x is an array of 5 pointers to arrays of 3 ints ++ + +Pointers to functions are declared using the function keyword: + + +int function(char) x; // x is a pointer to a function taking a char argument + // and returning an int +int function(char)[] x; // x is an array of pointers to functions + // taking a char argument and returning an int ++ + +C-style array declarations may be used as an alternative: + + +int x[3]; // x is an array of 3 ints +int x[3][5]; // x is an array of 3 arrays of 5 ints +int (*x[5])[3]; // x is an array of 5 pointers to arrays of 3 ints +int (*x)(char); // x is a pointer to a function taking a char argument + // and returning an int +int (*[] x)(char); // x is an array of pointers to functions + // taking a char argument and returning an int ++ + +In a declaration declaring multiple symbols, all the declarations +must be of the same type: + + +int x,y; // x and y are ints +int* x,y; // x and y are pointers to ints +int x,*y; // error, multiple types +int[] x,y; // x and y are arrays of ints +int x[],y; // error, multiple types ++ + Type Defining+ ++ Strong types can be introduced with the typedef. Strong types are semantically a + distinct type to the type checking system, for function overloading, and for the debugger. + + +typedef int myint; + +void foo(int x) { . } +void foo(myint m) { . } + + . +myint b; +foo(b); // calls foo(myint) ++ +Typedefs can specify a default initializer different from the +default initializer of the underlying type: + + typedef int myint = 7; +myint m; // initialized to 7 ++ + + Type Aliasing+ ++ It's sometimes convenient to use an alias for a type, such as a shorthand for typing + out a long, complex type like a pointer to a function. In D, this is done with the + alias declaration: + + +alias abc.Foo.bar myint;
+
+
+ + Aliased types are semantically identical to the types they are aliased to. The + debugger cannot distinguish between them, and there is no difference as far as function + overloading is concerned. For example: + + +alias int myint; + +void foo(int x) { . } +void foo(myint m) { . } error, multiply defined function foo ++ + + Type aliases are equivalent to the C typedef. + + +Alias Declarations+ ++ A symbol can be declared as an alias of another symbol. + For example: + + +import string; + +alias string.strlen mylen; + ... +int len = mylen("hello"); // actually calls string.strlen() ++ + + The following alias declarations are valid: + + +template Foo2(T) { alias T t; } +alias Foo2!(int) t1; +alias Foo2!(int).t t2; +alias t1.t t3; +alias t2 t4; + +t1.t v1; // v1 is type int +t2 v2; // v2 is type int +t3 v3; // v3 is type int +t4 v4; // v4 is type int ++ + + Aliased symbols are useful as a shorthand for a long qualified + symbol name, or as a way to redirect references from one symbol + to another: + + +version (Win32) +{ + alias win32.foo myfoo; +} +version (linux) +{ + alias linux.bar myfoo; +} ++ + + Aliasing can be used to 'import' a symbol from an import into the + current scope: + + +alias string.strlen strlen;
+
+
+ + Aliases can also 'import' a set of overloaded functions, that can + be overloaded with functions in the current scope: + + +class A { + int foo(int a) { return 1; } +} + +class B : A { + int foo( int a, uint b ) { return 2; } +} + +class C : B { + int foo( int a ) { return 3; } + alias B.foo foo; +} + +class D : C { +} + + +void test() +{ + D b = new D(); + int i; + + i = b.foo(1, 2u); // calls B.foo + i = b.foo(1); // calls C.foo +} ++ + + Note: Type aliases can sometimes look indistinguishable from + alias declarations: + + +alias foo.bar abc; // is it a type or a symbol? ++ + + The distinction is made in the semantic analysis pass. + + +typeof+ ++ Typeof is a way to specify a type based on the type + of an expression. For example: + + +void func(int i) +{ + typeof(i) j; // j is of type int + typeof(3 + 6.0) x; // x is of type double + typeof(1)* p; // p is of type pointer to int + int[typeof(p)] a; // a is of type int[int*] + + printf("%d\n", typeof('c').sizeof); // prints 1 + double c = cast(typeof(1.0))j; // cast j to double +} ++ + + Expression is not evaluated, just the type of it is + generated: + + +void func() +{ int i = 1; + typeof(++i) j; // j is declared to be an int, i is not incremented + printf("%d\n", i); // prints 1 +} ++ + + There are two special cases: + typeof(this) will generate the type of what this + would be in a non-static member function, even if not in a member + function. + Analogously, typeof(super) will generate the type of what + super would be in a non-static member function. + + +class A { } + +class B : A +{ + typeof(this) x; // x is declared to be a B + typeof(super) y; // y is declared to be an A +} + +struct C +{ + typeof(this) z; // z is declared to be a C* + typeof(super) q; // error, no super struct for C +} + +typeof(this) r; // error, no enclosing struct or class ++ + + Where Typeof is most useful is in writing generic + template code. + + + + |
-
-Home
-| Search
-| D
-| Comments
-
-|
-
- - - · Overview - · D for Win32 - · Win32 DLLs in D - · C .h to D Modules - · FAQ - · Style Guide - · Example: wc - · Future - · D Change Log - · Tech Tips - · Glossary - · Acknowledgements - - Tools - - · DMD D Compiler - · GDC D Compiler - · Linker - · Profiler - - Community - - · News Digest - · News - · Forum - · Announcements - · Learn - · D links - - Archives - - · digitalmars.D - · digitalmars.D.dtl - · digitalmars.D.announce - · digitalmars.D.learn - · digitalmars.D.bugs - · D.gnu - · Old D - - - |
-
-
-
-D Links- - -Wikis- -
Tools- -
Libraries- -
Games- -
Media- -
Comparisons and Benchmarks- -
Miscellaneous- -
Japanese Language- -
Images- -Cut & paste the following free images on your web pages of interest to - D programmers: -
-
-
-
- Corto's
- D button images:
- Submissions- - If you have any D code, documents, - images or web pages of interest to D - programmers, - please - email - the links to Digital Mars. -- - |
Add feedback and comments regarding this - page.
- -|
+
+
+
+
+ + + · Overview + + + · D for Win32 + + + · Win32 DLLs in D + + + · C .h to D Modules + + + · FAQ + + + · Style Guide + + + · Example: wc + + + · Future + + + · D Change Log + + + · Tech Tips + + + · Rationale + + + · Glossary + + + · Acknowledgements + + + + Tools + + · DMD D Compiler + + + · GDC D Compiler + + + · Linker + + + · Profiler + + + + Community + + · News Digest + + + · News + + + · Forum + + + · Announcements + + + · Learn + + + · D links + + + + Archives + + · digitalmars.D + + + · digitalmars.D.dtl + + + · digitalmars.D.announce + + + · digitalmars.D.learn + + + · digitalmars.D.bugs + + + · D.gnu + + + · Old D + + + + + |
+
+
+D + Links+ +Wikis+ +
Tools+ +
Libraries+ +
Games+ +
Media+ +
Comparisons and Benchmarks+ +
Miscellaneous+ +
Japanese Language+ +
Images+ +Cut & paste the following free images on your web pages of interest to + D + programmers: +
+
+
+
+ Corto's
+ D
+ button images:
+ Submissions+ + If you have any D + code, documents, + images or web pages of interest to D + + programmers, + please + email + the links to Digital Mars. + + + |
-
-Home
-| Search
-| D
-| Comments
-
-|
-
- - - · Overview - · D for Win32 - · Win32 DLLs in D - · C .h to D Modules - · FAQ - · Style Guide - · Example: wc - · Future - · D Change Log - · Tech Tips - · Glossary - · Acknowledgements - - Tools - - · DMD D Compiler - · GDC D Compiler - · Linker - · Profiler - - Community - - · News Digest - · News - · Forum - · Announcements - · Learn - · D links - - Archives - - · digitalmars.D - · digitalmars.D.dtl - · digitalmars.D.announce - · digitalmars.D.learn - · digitalmars.D.bugs - · D.gnu - · Old D - - - |
-
-
-
-
-Writing Win32 DLLs in D- - DLLs (Dynamic Link Libraries) are one of the foundations - of system programming for Windows. The D programming - language enables the creation of several different types of - DLLs. -- - For background information on what DLLs are and how they work - Chapter 11 of Jeffrey Richter's book - - Advanced Windows is indispensible. - - - This guide will show how to create DLLs of various types with D. - - - -DLLs with a C Interface- - A DLL presenting a C interface can connect to any other code - in a language that supports calling C functions in a DLL. - - DLLs can be created in D in roughly the same way as in C. - A DllMain() - is required, looking like: - -
-import std.c.windows.windows;
-HINSTANCE g_hInst;
-
-extern (C)
-{
- void gc_init();
- void gc_term();
- void _minit();
- void _moduleCtor();
- void _moduleUnitTests();
-}
-
-extern (Windows)
-BOOL DllMain(HINSTANCE hInstance, ULONG ulReason, LPVOID pvReserved)
-{
- switch (ulReason)
- {
- case DLL_PROCESS_ATTACH:
- gc_init(); // initialize GC
- _minit(); // initialize module list
- _moduleCtor(); // run module constructors
- _moduleUnitTests(); // run module unit tests
- break;
-
- case DLL_PROCESS_DETACH:
- gc_term(); // shut down GC
- break;
-
- case DLL_THREAD_ATTACH:
- case DLL_THREAD_DETACH:
- // Multiple threads not supported yet
- return false;
- }
- g_hInst=hInstance;
- return true;
-}
-
-
- Notes:
-
- LIBRARY MYDLL - DESCRIPTION 'My DLL written in D' - - EXETYPE NT - CODE PRELOAD DISCARDABLE - DATA PRELOAD SINGLE - - EXPORTS - DllGetClassObject @2 - DllCanUnloadNow @3 - DllRegisterServer @4 - DllUnregisterServer @5 -- - The functions in the EXPORTS list are for illustration. - Replace them with the actual exported functions from MYDLL. - Alternatively, use implib. - Here's an example of a simple DLL with a function print() - which prints a string: - - mydll2.d:-
-module mydll;
-export void dllprint() { printf("hello dll world\n"); }
-
-
- mydll.def:--LIBRARY "mydll.dll" -EXETYPE NT -SUBSYSTEM WINDOWS -CODE SHARED EXECUTE -DATA WRITE -- - Put the code above that contains DllMain() into a file dll.d. - Compile and link the dll with the following command: - - -dmd -ofmydll.dll mydll2.d dll.d mydll.def -implib/system mydll.lib mydll.dll -- - which will create mydll.dll and mydll.lib. - Now for a program, test.d, which will use the dll: - - test.d:-
-import mydll;
-
-int main()
-{
- mydll.dllprint();
- return 0;
-}
-
-
- Create a clone of mydll2.d that doesn't have the function bodies:
-
- mydll.d:--export void dllprint(); -- - Compile and link with the command: - -dmd test.d mydll.lib -- - and run: - -C:>test -hello dll world -C:> -- - - - Memory Allocation- - D DLLs use garbage collected memory management. The question is what - happens when pointers to allocated data cross DLL boundaries? - If the DLL presents a C interface, one would assume the reason - for that is to connect with code written in other languages. - Those other languages will not know anything about D's memory - management. Thus, the C interface will have to shield the - DLL's callers from needing to know anything about it. -- - There are many approaches to solving this problem: - -
COM Programming- - Many Windows API interfaces are in terms of COM (Common Object Model) - objects (also called OLE or ActiveX objects). A COM object is an object - who's first field is a pointer to a vtbl[], and the first 3 entries - in that vtbl[] are for QueryInterface(), AddRef(), and Release(). -- - For understanding COM, Kraig Brockshmidt's - - Inside OLE - is an indispensible resource. - - - COM objects are analogous to D interfaces. Any COM object can be - expressed as a D interface, and every D object with an interface X - can be exposed as a COM object X. - This means that D is compatible with COM objects implemented - in other languages. - - - While not strictly necessary, the Phobos library provides an Object - useful as a super class for all D COM objects, called ComObject. - ComObject provides a default implementation for - QueryInterface(), AddRef(), and Release(). - - - Windows COM objects use the Windows calling convention, which is not - the default for D, so COM functions need to have the attribute - extern (Windows). - - So, to write a COM object: - -
-import std.c.windows.com;
-
-class MyCOMobject : ComObject
-{
- extern (Windows):
- ...
-}
-
-
- The sample code includes an example COM client program and server DLL.
-
-D code calling D code in DLLs- - Having DLLs in D be able to talk to each other as if they - were statically linked together is, of course, very desirable - as code between applications can be shared, and different - DLLs can be independently developed. -- - The underlying difficulty is what to do about garbage collection (gc). - Each EXE and DLL will have their own gc instance. While - these gc's can coexist without stepping on each other, - it's redundant and inefficient to have multiple gc's running. - The idea explored here is to pick one gc and have the DLLs - redirect their gc's to use that one. The one gc used here will be - the one in the EXE file, although it's also possible to make a - separate DLL just for the gc. - - - The example will show both how to statically load a DLL, and - to dynamically load/unload it. - - - Starting with the code for the DLL, mydll.d: -
-/*
- * MyDll demonstration of how to write D DLLs.
- */
-
-import std.c.stdio;
-import std.c.stdlib;
-import std.string;
-import std.c.windows.windows;
-import std.gc;
-
-HINSTANCE g_hInst;
-
-extern (C)
-{
- void _minit();
- void _moduleCtor();
- void _moduleDtor();
- void _moduleUnitTests();
-}
-
-extern (Windows)
- BOOL DllMain(HINSTANCE hInstance, ULONG ulReason, LPVOID pvReserved)
-{
- switch (ulReason)
- {
- case DLL_PROCESS_ATTACH:
- printf("DLL_PROCESS_ATTACH\n");
- break;
-
- case DLL_PROCESS_DETACH:
- printf("DLL_PROCESS_DETACH\n");
- std.c.stdio._fcloseallp = null; // so stdio doesn't get closed
- break;
-
- case DLL_THREAD_ATTACH:
- printf("DLL_THREAD_ATTACH\n");
- return false;
-
- case DLL_THREAD_DETACH:
- printf("DLL_THREAD_DETACH\n");
- return false;
- }
- g_hInst = hInstance;
- return true;
-}
-
-export void MyDLL_Initialize(void* gc)
-{
- printf("MyDLL_Initialize()\n");
- std.gc.setGCHandle(gc);
- _minit();
- _moduleCtor();
-// _moduleUnitTests();
-}
-
-export void MyDLL_Terminate()
-{
- printf("MyDLL_Terminate()\n");
- _moduleDtor(); // run module destructors
- std.gc.endGCHandle();
-}
-
-static this()
-{
- printf("static this for mydll\n");
-}
-
-static ~this()
-{
- printf("static ~this for mydll\n");
-}
-
-/* --------------------------------------------------------- */
-
-class MyClass
-{
- char[] concat(char[] a, char[] b)
- {
- return a ~ " " ~ b;
- }
-
- void free(char[] s)
- {
- delete s;
- }
-}
-
-export MyClass getMyClass()
-{
- return new MyClass();
-}
-
-
-
-import std.stdio;
-import std.gc;
-
-import mydll;
-
-//version=DYNAMIC_LOAD;
-
-version (DYNAMIC_LOAD)
-{
- import std.c.windows.windows;
-
- alias void function(void*) MyDLL_Initialize_fp;
- alias void function() MyDLL_Terminate_fp;
- alias MyClass function() getMyClass_fp;
-
- int main()
- { HMODULE h;
- FARPROC fp;
- MyDLL_Initialize_fp mydll_initialize;
- MyDLL_Terminate_fp mydll_terminate;
-
- getMyClass_fp getMyClass;
- MyClass c;
-
- printf("Start Dynamic Link...\n");
-
- h = LoadLibraryA("mydll.dll");
- if (h == null)
- { printf("error loading mydll.dll\n");
- return 1;
- }
-
- fp = GetProcAddress(h, "D5mydll16MyDLL_InitializeFPvZv");
- if (fp == null)
- { printf("error loading symbol MyDLL_Initialize()\n");
- return 1;
- }
-
- mydll_initialize = cast(MyDLL_Initialize_fp) fp;
- (*mydll_initialize)(std.gc.getGCHandle());
-
- fp = GetProcAddress(h, "D5mydll10getMyClassFZC5mydll7MyClass");
- if (fp == null)
- { printf("error loading symbol getMyClass()\n");
- return 1;
- }
-
- getMyClass = cast(getMyClass_fp) fp;
- c = (*getMyClass)();
- foo(c);
-
- fp = GetProcAddress(h, "D5mydll15MyDLL_TerminateFZv");
- if (fp == null)
- { printf("error loading symbol MyDLL_Terminate()\n");
- return 1;
- }
-
- mydll_terminate = cast(MyDLL_Terminate_fp) fp;
- (*mydll_terminate)();
-
- if (FreeLibrary(h) == FALSE)
- { printf("error freeing mydll.dll\n");
- return 1;
- }
-
- printf("End...\n");
- return 0;
- }
-}
-else
-{ // static link the DLL
-
- int main()
- {
- printf("Start Static Link...\n");
- MyDLL_Initialize(std.gc.getGCHandle());
- foo(getMyClass());
- MyDLL_Terminate();
- printf("End...\n");
- return 0;
- }
-}
-
-void foo(MyClass c)
-{
- char[] s;
-
- s = c.concat("Hello", "world!");
- writefln(s);
- c.free(s);
- delete c;
-}
-
-
- Let's start with the statically linked version, which is simpler.
- It's compiled and linked with the command:
- dmd test mydll.lib -g- Note how it is linked with mydll.lib, the import library - for mydll.dll. - The code is straightforward, it initializes mydll.lib with - a call to MyDLL_Initialize(), passing the handle - to test.exe's gc. - Then, we can use the DLL and call its functions just as if - it were part of test.exe. In foo(), gc memory - is allocated and freed both by test.exe and mydll.dll. - When we're done using the DLL, it is terminated with - MyDLL_Terminate(). - - - Running it looks like this: - -DLL_PROCESS_ATTACH -Start Static Link... -MyDLL_Initialize() -static this for mydll -Hello world! -MyDLL_Terminate() -static ~this for mydll -End...- - The dynamically linked version is a little harder to set up. - Compile and link it with the command: - dmd test -version=DYNAMIC_LOAD -g- The import library mydll.lib is not needed. - The DLL is loaded with a call to - LoadLibraryA(), - and each exported function has to be retrieved via - a call to - GetProcAddress(). - An easy way to get the decorated name to pass to GetProcAddress() - is to copy and paste it from the generated mydll.map file - under the Export heading. - Once this is done, we can use the member functions of the - DLL classes as if they were part of test.exe. - When done, release the DLL with - FreeLibrary(). - - - Running it looks like this: - -Start Dynamic Link... -DLL_PROCESS_ATTACH -MyDLL_Initialize() -static this for mydll -Hello world! -MyDLL_Terminate() -static ~this for mydll -DLL_PROCESS_DETACH -End...- - |
Add feedback and comments regarding this - page.
- -|
+
+
+
+
+ + + · Overview + + + · D for Win32 + + + · Win32 DLLs in D + + + · C .h to D Modules + + + · FAQ + + + · Style Guide + + + · Example: wc + + + · Future + + + · D Change Log + + + · Tech Tips + + + · Rationale + + + · Glossary + + + · Acknowledgements + + + + Tools + + · DMD D Compiler + + + · GDC D Compiler + + + · Linker + + + · Profiler + + + + Community + + · News Digest + + + · News + + + · Forum + + + · Announcements + + + · Learn + + + · D links + + + + Archives + + · digitalmars.D + + + · digitalmars.D.dtl + + + · digitalmars.D.announce + + + · digitalmars.D.learn + + + · digitalmars.D.bugs + + + · D.gnu + + + · Old D + + + + + |
+
+
+
+Writing Win32 DLLs in D+ + DLLs (Dynamic Link Libraries) are one of the foundations + of system programming for Windows. The D programming + language enables the creation of several different types of + DLLs. ++ + For background information on what DLLs are and how they work + Chapter 11 of Jeffrey Richter's book + + Advanced Windows is indispensible. + + + This guide will show how to create DLLs of various types with D. + + + +DLLs with a C Interface+ + A DLL presenting a C interface can connect to any other code + in a language that supports calling C functions in a DLL. + + DLLs can be created in D in roughly the same way as in C. + A DllMain() + is required, looking like: + +import std.c.windows.windows; +HINSTANCE g_hInst; + +extern (C) +{ + void gc_init(); + void gc_term(); + void _minit(); + void _moduleCtor(); + void _moduleUnitTests(); +} + +extern (Windows) +BOOL DllMain(HINSTANCE hInstance, ULONG ulReason, LPVOID pvReserved) +{ + switch (ulReason) + { + case DLL_PROCESS_ATTACH: + gc_init(); // initialize GC + _minit(); // initialize module list + _moduleCtor(); // run module constructors + _moduleUnitTests(); // run module unit tests + break; + + case DLL_PROCESS_DETACH: + gc_term(); // shut down GC + break; + + case DLL_THREAD_ATTACH: + case DLL_THREAD_DETACH: + // Multiple threads not supported yet + return false; + } + g_hInst=hInstance; + return true; +} ++ + Notes: +
LIBRARY MYDLL +DESCRIPTION 'My DLL written in D' + +EXETYPE NT +CODE PRELOAD DISCARDABLE +DATA PRELOAD SINGLE + +EXPORTS + DllGetClassObject @2 + DllCanUnloadNow @3 + DllRegisterServer @4 + DllUnregisterServer @5 ++ + + The functions in the EXPORTS list are for illustration. + Replace them with the actual exported functions from MYDLL. + Alternatively, use implib. + Here's an example of a simple DLL with a function print() + which prints a string: + + mydll2.d:+module mydll; +export void dllprint() { printf("hello dll world\n"); } ++ + mydll.def:+ +LIBRARY "mydll.dll" +EXETYPE NT +SUBSYSTEM WINDOWS +CODE SHARED EXECUTE +DATA WRITE ++ + + Put the code above that contains DllMain() into a file dll.d. + Compile and link the dll with the following command: + + C:>dmd -ofmydll.dll mydll2.d dll.d mydll.def +C:>implib/system mydll.lib mydll.dll +C:> ++ + which will create mydll.dll and mydll.lib. + Now for a program, test.d, which will use the dll: + + test.d:+import mydll; + +int main() +{ + mydll.dllprint(); + return 0; +} ++ + Create a clone of mydll2.d that doesn't have the function bodies: + + mydll.d:+export void dllprint(); ++ + Compile and link with the command: + + C:>dmd test.d mydll.lib +C:> ++ + and run: + C:>test +hello dll world +C:> ++ + + + Memory Allocation+ + D DLLs use garbage collected memory management. The question is what + happens when pointers to allocated data cross DLL boundaries? + If the DLL presents a C interface, one would assume the reason + for that is to connect with code written in other languages. + Those other languages will not know anything about D's memory + management. Thus, the C interface will have to shield the + DLL's callers from needing to know anything about it. ++ + There are many approaches to solving this problem: + +
COM Programming+ + Many Windows API interfaces are in terms of COM (Common Object Model) + objects (also called OLE or ActiveX objects). A COM object is an object + who's first field is a pointer to a vtbl[], and the first 3 entries + in that vtbl[] are for QueryInterface(), AddRef(), and Release(). ++ + For understanding COM, Kraig Brockshmidt's + + Inside OLE + is an indispensible resource. + + + COM objects are analogous to D interfaces. Any COM object can be + expressed as a D interface, and every D object with an interface X + can be exposed as a COM object X. + This means that D is compatible with COM objects implemented + in other languages. + + + While not strictly necessary, the Phobos library provides an Object + useful as a super class for all D COM objects, called ComObject. + ComObject provides a default implementation for + QueryInterface(), AddRef(), and Release(). + + + Windows COM objects use the Windows calling convention, which is not + the default for D, so COM functions need to have the attribute + extern (Windows). + + So, to write a COM object: + + import std.c.windows.com; + +class MyCOMobject : ComObject +{ + extern (Windows): + ... +} ++ + The sample code includes an example COM client program and server DLL. + + D code calling D code in DLLs+ + Having DLLs in D be able to talk to each other as if they + were statically linked together is, of course, very desirable + as code between applications can be shared, and different + DLLs can be independently developed. ++ + The underlying difficulty is what to do about garbage collection (gc). + Each EXE and DLL will have their own gc instance. While + these gc's can coexist without stepping on each other, + it's redundant and inefficient to have multiple gc's running. + The idea explored here is to pick one gc and have the DLLs + redirect their gc's to use that one. The one gc used here will be + the one in the EXE file, although it's also possible to make a + separate DLL just for the gc. + + + The example will show both how to statically load a DLL, and + to dynamically load/unload it. + + + Starting with the code for the DLL, mydll.d: + /* + * MyDll demonstration of how to write D DLLs. + */ + +import std.c.stdio; +import std.c.stdlib; +import std.string; +import std.c.windows.windows; +import std.gc; + +HINSTANCE g_hInst; + +extern (C) +{ + void _minit(); + void _moduleCtor(); + void _moduleDtor(); + void _moduleUnitTests(); +} + +extern (Windows) + BOOL DllMain(HINSTANCE hInstance, ULONG ulReason, LPVOID pvReserved) +{ + switch (ulReason) + { + case DLL_PROCESS_ATTACH: + printf("DLL_PROCESS_ATTACH\n"); + break; + + case DLL_PROCESS_DETACH: + printf("DLL_PROCESS_DETACH\n"); + std.c.stdio._fcloseallp = null; // so stdio doesn't get closed + break; + + case DLL_THREAD_ATTACH: + printf("DLL_THREAD_ATTACH\n"); + return false; + + case DLL_THREAD_DETACH: + printf("DLL_THREAD_DETACH\n"); + return false; + } + g_hInst = hInstance; + return true; +} + +export void MyDLL_Initialize(void* gc) +{ + printf("MyDLL_Initialize()\n"); + std.gc.setGCHandle(gc); + _minit(); + _moduleCtor(); +// _moduleUnitTests(); +} + +export void MyDLL_Terminate() +{ + printf("MyDLL_Terminate()\n"); + _moduleDtor(); // run module destructors + std.gc.endGCHandle(); +} + +static this() +{ + printf("static this for mydll\n"); +} + +static ~this() +{ + printf("static ~this for mydll\n"); +} + +/* --------------------------------------------------------- */ + +class MyClass +{ + char[] concat(char[] a, char[] b) + { + return a ~ " " ~ b; + } + + void free(char[] s) + { + delete s; + } +} + +export MyClass getMyClass() +{ + return new MyClass(); +} ++ +
import std.stdio; +import std.gc; + +import mydll; + +//version=DYNAMIC_LOAD; + +version (DYNAMIC_LOAD) +{ + import std.c.windows.windows; + + alias void function(void*) MyDLL_Initialize_fp; + alias void function() MyDLL_Terminate_fp; + alias MyClass function() getMyClass_fp; + + int main() + { HMODULE h; + FARPROC fp; + MyDLL_Initialize_fp mydll_initialize; + MyDLL_Terminate_fp mydll_terminate; + + getMyClass_fp getMyClass; + MyClass c; + + printf("Start Dynamic Link...\n"); + + h = LoadLibraryA("mydll.dll"); + if (h == null) + { printf("error loading mydll.dll\n"); + return 1; + } + + fp = GetProcAddress(h, "D5mydll16MyDLL_InitializeFPvZv"); + if (fp == null) + { printf("error loading symbol MyDLL_Initialize()\n"); + return 1; + } + + mydll_initialize = cast(MyDLL_Initialize_fp) fp; + (*mydll_initialize)(std.gc.getGCHandle()); + + fp = GetProcAddress(h, "D5mydll10getMyClassFZC5mydll7MyClass"); + if (fp == null) + { printf("error loading symbol getMyClass()\n"); + return 1; + } + + getMyClass = cast(getMyClass_fp) fp; + c = (*getMyClass)(); + foo(c); + + fp = GetProcAddress(h, "D5mydll15MyDLL_TerminateFZv"); + if (fp == null) + { printf("error loading symbol MyDLL_Terminate()\n"); + return 1; + } + + mydll_terminate = cast(MyDLL_Terminate_fp) fp; + (*mydll_terminate)(); + + if (FreeLibrary(h) == FALSE) + { printf("error freeing mydll.dll\n"); + return 1; + } + + printf("End...\n"); + return 0; + } +} +else +{ // static link the DLL + + int main() + { + printf("Start Static Link...\n"); + MyDLL_Initialize(std.gc.getGCHandle()); + foo(getMyClass()); + MyDLL_Terminate(); + printf("End...\n"); + return 0; + } +} + +void foo(MyClass c) +{ + char[] s; + + s = c.concat("Hello", "world!"); + writefln(s); + c.free(s); + delete c; +} ++ + Let's start with the statically linked version, which is simpler. + It's compiled and linked with the command: + + C:>dmd test mydll.lib -g ++ + Note how it is linked with mydll.lib, the import library + for mydll.dll. + The code is straightforward, it initializes mydll.lib with + a call to MyDLL_Initialize(), passing the handle + to test.exe's gc. + Then, we can use the DLL and call its functions just as if + it were part of test.exe. In foo(), gc memory + is allocated and freed both by test.exe and mydll.dll. + When we're done using the DLL, it is terminated with + MyDLL_Terminate(). + + + Running it looks like this: + + C:>test +DLL_PROCESS_ATTACH +Start Static Link... +MyDLL_Initialize() +static this for mydll +Hello world! +MyDLL_Terminate() +static ~this for mydll +End... +C:> ++ + The dynamically linked version is a little harder to set up. + Compile and link it with the command: + + C:>dmd test -version=DYNAMIC_LOAD -g ++ The import library mydll.lib is not needed. + The DLL is loaded with a call to + LoadLibraryA(), + and each exported function has to be retrieved via + a call to + GetProcAddress(). + An easy way to get the decorated name to pass to GetProcAddress() + is to copy and paste it from the generated mydll.map file + under the Export heading. + Once this is done, we can use the member functions of the + DLL classes as if they were part of test.exe. + When done, release the DLL with + FreeLibrary(). + + + Running it looks like this: + C:>test +Start Dynamic Link... +DLL_PROCESS_ATTACH +MyDLL_Initialize() +static this for mydll +Hello world! +MyDLL_Terminate() +static ~this for mydll +DLL_PROCESS_DETACH +End... +C:> ++ + + |
-
-Home
-| Search
-| D
-| Comments
-
-|
-
- - - · Overview - · D for Win32 - · Win32 DLLs in D - · C .h to D Modules - · FAQ - · Style Guide - · Example: wc - · Future - · D Change Log - · Tech Tips - · Glossary - · Acknowledgements - - Tools - - · DMD D Compiler - · GDC D Compiler - · Linker - · Profiler - - Community - - · News Digest - · News - · Forum - · Announcements - · Learn - · D links - - Archives - - · digitalmars.D - · digitalmars.D.dtl - · digitalmars.D.announce - · digitalmars.D.learn - · digitalmars.D.bugs - · D.gnu - · Old D - - - |
-
-
-
-
-The D Style- - The D Style is a set of style conventions for writing - D programs. The D Style is not enforced by the compiler, it is - purely cosmetic and a matter of choice. Adhering to the D Style, - however, will make it easier for others to work with your D - code and easier for you to work with others' D code. - The D Style can form the starting point for a D project - style guide customized for your project team. - -White Space- -
Comments- -
Naming Conventions- -
Meaningless Type Aliases- - Things like: - -- alias void VOID; - alias int INT; - alias int* pint; -- - should be avoided. - - Declaration Style- - Since in D the declarations are left-associative, left justify them: - -- int[] x, y; // makes it clear that x and y are the same type - int** p, q; // makes it clear that p and q are the same type -- - to emphasize their relationship. Do not use the C style: - - - int []x, y; // confusing since y is also an int[] - int **p, q; // confusing since q is also an int** -- - Operator Overloading- - Operator overloading is a powerful tool to extend the basic - types supported by the language. But being powerful, it has - great potential for creating obfuscated code. In particular, - the existing D operators have conventional meanings, such - as '+' means 'add' and '<<' means 'shift left'. - Overloading operator '+' with a meaning different from 'add' - is arbitrarily confusing and should be avoided. - -Hungarian Notation- - Just say no. - - |
Add feedback and comments regarding this - page.
- -|
+
+
+
+
+ + + · Overview + + + · D for Win32 + + + · Win32 DLLs in D + + + · C .h to D Modules + + + · FAQ + + + · Style Guide + + + · Example: wc + + + · Future + + + · D Change Log + + + · Tech Tips + + + · Rationale + + + · Glossary + + + · Acknowledgements + + + + Tools + + · DMD D Compiler + + + · GDC D Compiler + + + · Linker + + + · Profiler + + + + Community + + · News Digest + + + · News + + + · Forum + + + · Announcements + + + · Learn + + + · D links + + + + Archives + + · digitalmars.D + + + · digitalmars.D.dtl + + + · digitalmars.D.announce + + + · digitalmars.D.learn + + + · digitalmars.D.bugs + + + · D.gnu + + + · Old D + + + + + |
+
+
+
+The D Style+ + The D Style is a set of style conventions for writing + D programs. The D Style is not enforced by the compiler, it is + purely cosmetic and a matter of choice. Adhering to the D Style, + however, will make it easier for others to work with your D + code and easier for you to work with others' D code. + The D Style can form the starting point for a D project + style guide customized for your project team. + +White Space+ +
Comments+ +
Naming Conventions+ +
Meaningless Type Aliases+ + Things like: + +alias void VOID; +alias int INT; +alias int* pint; ++ + should be avoided. + + Declaration Style+ + Since in D the declarations are left-associative, left justify them: + +int[] x, y; // makes it clear that x and y are the same type +int** p, q; // makes it clear that p and q are the same type ++ + to emphasize their relationship. Do not use the C style: + + int []x, y; // confusing since y is also an int[] +int **p, q; // confusing since q is also an int** ++ + Operator Overloading+ + Operator overloading is a powerful tool to extend the basic + types supported by the language. But being powerful, it has + great potential for creating obfuscated code. In particular, + the existing D operators have conventional meanings, such + as '+' means 'add' and '<<' means 'shift left'. + Overloading operator '+' with a meaning different from 'add' + is arbitrarily confusing and should be avoided. + +Hungarian Notation+ + Just say no. + + + |
-
-Home
-| Search
-| D
-| Comments
-
-|
-
- - -· Lexical -· Modules -· Declarations -· Types -· Properties -· Attributes -· Pragmas -· Expressions -· Statements -· Arrays -· Structs & Unions -· Classes -· Interfaces -· Enums -· Functions -· Operator Overloading -· Templates -· Mixins -· Contracts -· Conditional Compilation -· Handling errors -· Garbage Collection -· Memory Management -· Floating Point -· Inline Assembler -· Documentation Comments -· Interfacing To C -· Portability Guide -· Embedding D in HTML -· Named Character Entities -· Application Binary Interface - - |
-
-
-
-
-Named Character Entities- - These are the character entity names supported by D. -- - Note: Not all will display properly in the Symbol - column in all browsers. - - - - -
|
Add feedback and comments regarding this - page.
- -|
+
+
+
+
+ + +· Lexical + + +· Modules + + +· Declarations + + +· Types + + +· Properties + + +· Attributes + + +· Pragmas + + +· Expressions + + +· Statements + + +· Arrays + + +· Structs & Unions + + +· Classes + + +· Interfaces + + +· Enums + + +· Functions + + +· Operator Overloading + + +· Templates + + +· Mixins + + +· Contracts + + +· Conditional Compilation + + +· Handling errors + + +· Garbage Collection + + +· Memory Management + + +· Floating Point + + +· Inline Assembler + + +· Documentation Comments + + +· Interfacing To C + + +· Portability Guide + + +· Embedding D in HTML + + +· Named Character Entities + + +· Application Binary Interface + + + + |
+
+
+
+Named Character Entities+ + These are the character entity names supported by D. ++ + Note: Not all will display properly in the Symbol + column in all browsers. + + + + +
|
-
-Home
-| Search
-| D
-| Comments
-
-|
-
- - -· Lexical -· Modules -· Declarations -· Types -· Properties -· Attributes -· Pragmas -· Expressions -· Statements -· Arrays -· Structs & Unions -· Classes -· Interfaces -· Enums -· Functions -· Operator Overloading -· Templates -· Mixins -· Contracts -· Conditional Compilation -· Handling errors -· Garbage Collection -· Memory Management -· Floating Point -· Inline Assembler -· Documentation Comments -· Interfacing To C -· Portability Guide -· Embedding D in HTML -· Named Character Entities -· Application Binary Interface - - |
-
-
-
-
-Enums - Enumerated Types- -
- EnumDeclaration:
- enum Identifier EnumBody
- enum EnumBody
- enum Identifier : EnumBaseType EnumBody
- enum : EnumBaseType EnumBody
-
- EnumBaseType:
- Type
-
- EnumBody:
- ;
- { EnumMembers }
-
- EnumMembers:
- EnumMember
- EnumMember ,
- EnumMember , EnumMembers
-
- EnumMember:
- Identifier
- Identifier = Expression
-
-
- Enums are used to define a group of related integral constants.
- - - If the enum Identifier is present, the EnumMembers - are declared in the scope of the enum Identifier. - The enum Identifier declares a new type. - - - If the enum Identifier is not present, then the enum - is an anonymous enum, and the EnumMembers are declared - in the scope the EnumDeclaration appears in. - No new type is created; the EnumMembers have the type of the - EnumBaseType. - - - The EnumBaseType is the underlying type of the enum. - It must be an integral type. - If omitted, it defaults to int. - -
- enum { A, B, C } // anonymous enum
-
-
- Defines the constants A=0, B=1, C=2 in a manner equivalent to:
-
- - const int A = 0; - const int B = 1; - const int C = 2; -- - Whereas: - -
- enum X { A, B, C } // named enum
-
-
- Define a new type X which has values X.A=0, X.B=1, X.C=2
- - - Named enum members can be implicitly cast to integral types, but integral types - cannot be implicitly cast to an enum type. - - - Enums must have at least one member. - - - If an Expression is supplied for an enum member, the value - of the member is set to the result of the Expression. - The Expression must be resolvable at compile time. - Subsequent enum members with no Expression are set to the - value of the previous member plus one: - -
- enum { A, B = 5+7, C, D = 8, E }
-
-
- Sets A=0, B=12, C=13, D=8, and E=9.
-
-Enum Properties- -- .min Smallest value of enum - .max Largest value of enum - .sizeof Size of storage for an enumerated value - - For example: - - X.min is X.A - X.max is X.C - X.sizeof is same as int.sizeof -- - Initialization of Enums- - In the absence of an explicit initializer, an enum variable - is initialized to the first enum value. - -
- enum X { A=3, B, C }
- X x; // x is initialized to 3
-
-
- |
Add feedback and comments regarding this - page.
- -|
+
+
+
+
+ + +· Lexical + + +· Modules + + +· Declarations + + +· Types + + +· Properties + + +· Attributes + + +· Pragmas + + +· Expressions + + +· Statements + + +· Arrays + + +· Structs & Unions + + +· Classes + + +· Interfaces + + +· Enums + + +· Functions + + +· Operator Overloading + + +· Templates + + +· Mixins + + +· Contracts + + +· Conditional Compilation + + +· Handling errors + + +· Garbage Collection + + +· Memory Management + + +· Floating Point + + +· Inline Assembler + + +· Documentation Comments + + +· Interfacing To C + + +· Portability Guide + + +· Embedding D in HTML + + +· Named Character Entities + + +· Application Binary Interface + + + + |
+
+
+
+Enums - Enumerated Types+ +EnumDeclaration:
+ enum Identifier EnumBody
+ enum EnumBody
+ enum Identifier : EnumBaseType EnumBody
+ enum : EnumBaseType EnumBody
+
+EnumBaseType:
+ Type
+
+EnumBody:
+ ;
+ { EnumMembers }
+
+EnumMembers:
+ EnumMember
+ EnumMember ,
+ EnumMember , EnumMembers
+
+EnumMember:
+ Identifier
+ Identifier = Expression
+
+
+ Enums are used to define a group of related integral constants.
+ + + If the enum Identifier is present, the EnumMembers + are declared in the scope of the enum Identifier. + The enum Identifier declares a new type. + + + If the enum Identifier is not present, then the enum + is an anonymous enum, and the EnumMembers are declared + in the scope the EnumDeclaration appears in. + No new type is created; the EnumMembers have the type of the + EnumBaseType. + + + The EnumBaseType is the underlying type of the enum. + It must be an integral type. + If omitted, it defaults to int. + + enum { A, B, C } // anonymous enum ++ + Defines the constants A=0, B=1, C=2 in a manner equivalent to: + + const int A = 0; +const int B = 1; +const int C = 2; ++ + Whereas: + + enum X { A, B, C } // named enum ++ + Define a new type X which has values X.A=0, X.B=1, X.C=2 + + + Named enum members can be implicitly cast to integral types, but integral types + cannot be implicitly cast to an enum type. + + + Enums must have at least one member. + + + If an Expression is supplied for an enum member, the value + of the member is set to the result of the Expression. + The Expression must be resolvable at compile time. + Subsequent enum members with no Expression are set to the + value of the previous member plus one: + + enum { A, B = 5+7, C, D = 8, E }
+
+
+ Sets A=0, B=12, C=13, D=8, and E=9.
+
+Enum Properties+ ++.min Smallest value of enum +.max Largest value of enum +.sizeof Size of storage for an enumerated value + +For example: + +X.min is X.A +X.max is X.C +X.sizeof is same as int.sizeof ++ + Initialization of Enums+ + In the absence of an explicit initializer, an enum variable + is initialized to the first enum value. + +enum X { A=3, B, C } +X x; // x is initialized to 3 ++ + + |
-
-Home
-| Search
-| D
-| Comments
-
-|
-
- - -· Lexical -· Modules -· Declarations -· Types -· Properties -· Attributes -· Pragmas -· Expressions -· Statements -· Arrays -· Structs & Unions -· Classes -· Interfaces -· Enums -· Functions -· Operator Overloading -· Templates -· Mixins -· Contracts -· Conditional Compilation -· Handling errors -· Garbage Collection -· Memory Management -· Floating Point -· Inline Assembler -· Documentation Comments -· Interfacing To C -· Portability Guide -· Embedding D in HTML -· Named Character Entities -· Application Binary Interface - - |
-
-
-
-
-Error Handling in D- -All programs have to deal with errors. Errors are unexpected conditions that -are not part of the normal operation of a program. Examples of common errors -are: - -
The Error Handling Problem- -The traditional C way of detecting and reporting errors is not traditional, -it is ad-hoc and varies from function to function, including: - -
- -Good error handling code tends to clutter up what otherwise would be a neat -and clean looking implementation. - - -Even worse, good error handling code is itself error prone, tends to be the -least tested (and therefore buggy) part of the project, and is frequently -simply omitted. The end result is likely a "blue screen of death" as the -program failed to deal with some unanticipated error. - - -Quick and dirty programs are not worth writing tedious error handling code -for, and so such utilities tend to be like using a table saw with no -blade guards. - - -What's needed is an error handling philosophy and methodology that is: - -
The D Error Handling Solution- -Let's first make some observations and assumptions about errors: - -
- -If code detects an error like "out of memory," then an Error is thrown -with a message saying "Out of memory". The function call stack is unwound, -looking for a handler for the Error. Finally blocks are executed as the -stack is unwound. If an error handler is found, execution resumes there. If -not, the default Error handler is run, which displays the message and -terminates the program. - - -How does this meet our criteria? - -
|
Add feedback and comments regarding this - page.
- -|
+
+
+
+
+ + +· Lexical + + +· Modules + + +· Declarations + + +· Types + + +· Properties + + +· Attributes + + +· Pragmas + + +· Expressions + + +· Statements + + +· Arrays + + +· Structs & Unions + + +· Classes + + +· Interfaces + + +· Enums + + +· Functions + + +· Operator Overloading + + +· Templates + + +· Mixins + + +· Contracts + + +· Conditional Compilation + + +· Handling errors + + +· Garbage Collection + + +· Memory Management + + +· Floating Point + + +· Inline Assembler + + +· Documentation Comments + + +· Interfacing To C + + +· Portability Guide + + +· Embedding D in HTML + + +· Named Character Entities + + +· Application Binary Interface + + + + |
+
+
+
+Error Handling in D+ +All programs have to deal with errors. Errors are unexpected conditions that +are not part of the normal operation of a program. Examples of common errors +are: + +
The Error Handling Problem+ +The traditional C way of detecting and reporting errors is not traditional, +it is ad-hoc and varies from function to function, including: + +
+ +Good error handling code tends to clutter up what otherwise would be a neat +and clean looking implementation. + + +Even worse, good error handling code is itself error prone, tends to be the +least tested (and therefore buggy) part of the project, and is frequently +simply omitted. The end result is likely a "blue screen of death" as the +program failed to deal with some unanticipated error. + + +Quick and dirty programs are not worth writing tedious error handling code +for, and so such utilities tend to be like using a table saw with no +blade guards. + + +What's needed is an error handling philosophy and methodology that is: + +
The D Error Handling Solution+ +Let's first make some observations and assumptions about errors: + +
+ +If code detects an error like "out of memory," then an Error is thrown +with a message saying "Out of memory". The function call stack is unwound, +looking for a handler for the Error. Finally blocks are executed as the +stack is unwound. If an error handler is found, execution resumes there. If +not, the default Error handler is run, which displays the message and +terminates the program. + + +How does this meet our criteria? + +
|
-
-Home
-| Search
-| D
-| Comments
-
-|
-
- - -· Lexical -· Modules -· Declarations -· Types -· Properties -· Attributes -· Pragmas -· Expressions |