diff --git a/ctod.dd b/ctod.dd index 6112b67316..634d25ef2d 100644 --- a/ctod.dd +++ b/ctod.dd @@ -208,14 +208,14 @@ $(H3 Dealing with NANs in floating point compares) $(H4 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). + 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). $(CCODE #include <math.h> if (isnan(x) || isnan(y)) - result = FALSE; + result = false; else result = (x < y); ) @@ -233,9 +233,10 @@ result = (x < y); // false if x or y is nan $(H3 Asserts are a necessary part of any good defensive coding strategy) $(H4 The C Way) -$(P 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__.) +$(P C doesn't directly support assert in the language, but does define a macro +in the standard library header assert.h. That macro writes a diagnostic message +on stderr when the condition given as parameter is not true. The message will +use __FILE__, __LINE__ and __func__ (C99) to localize the failing assertion.) $(CCODE #include <assert.h> @@ -275,8 +276,8 @@ $(H3 Looping through an array) $(H4 The C Way) $(P - The array length is defined separately, or a clumsy - sizeof() expression is used to get the length.) + The array length is defined separately, or a clumsy and error prone + `sizeof()` expression is used to get the length.) $(CCODE #define ARRAY_LENGTH 17 @@ -343,8 +344,7 @@ int array_length; int *array; int *newarray; -newarray = (int *) - realloc(array, (array_length + 1) * sizeof(int)); +newarray = realloc(array, (array_length + 1) * sizeof(int)); if (!newarray) error("out of memory"); array = newarray; @@ -380,9 +380,8 @@ 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); +s = malloc((s1 ? strlen(s1) : 0) + + (s2 ? strlen(s2) : 0) + 1); if (!s) error("out of memory"); if (s1) @@ -394,10 +393,8 @@ if (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)); +char *news = realloc(s, lens + sizeof(hello) + 1); if (!news) error("out of memory"); s = news; @@ -612,7 +609,9 @@ $(CCODE void dostring(char *s) { enum Strings { Hello, Goodbye, Maybe, Max }; - static char *table[] = { "hello", "goodbye", "maybe" }; + static char *table[] = { [Hello] ="hello", + [Goodbye]="goodbye", + [Maybe] ="maybe" }; int i; for (i = 0; i < Max; i++) @@ -634,7 +633,9 @@ void dostring(char *s) 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. + bugs. Designated initializers as were introduced with C99 allow + to link correctly 2 of the 3 data structures, but at the cost of + a lot of typing. Additionally, if the number of values becomes large, a binary or hash lookup will yield a considerable performance increase over @@ -718,23 +719,23 @@ Sometimes, it's nice to control the layout of a struct with nested structs and u $(H4 The C Way) - C doesn't allow anonymous structs or unions, which means that dummy tag names - and dummy members are necessary: + Before C11 C didn't allow for anonymous structs or unions, which meant that + dummy member names were necessary: $(CCODE struct Foo { int i; - union Bar + union { - struct Abc { int x; long y; } _abc; + struct { int x; long y; } abc; char *p; - } _bar; + } bar; }; -#define x _bar._abc.x -#define y _bar._abc.y -#define p _bar.p +#define x bar.abc.x +#define y bar.abc.y +#define p bar.p struct Foo f; @@ -839,10 +840,12 @@ $(H4 The C Way) $(CCODE union U { int a; long b; }; union U x = { 5 }; // initialize member 'a' to 5 +union U y = { .b = 42l }; // initialize member 'b' to 42 (C99) + ) Adding union members or rearranging them can have disastrous consequences - for any initializers. + for any initializers. Designated initializers in C99 fix that issue. $(H4 The D Way) @@ -865,13 +868,15 @@ $(H4 The C Way) $(CCODE struct S { int a; int b; }; struct S x = { 5, 3 }; +struct S y = { .b=3, .a=5 }; /* C99 */ ) 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. + modified appropriately. This is a minefield for bugs. Designated + initializers in C99 fix that issue. $(H4 The D Way) @@ -889,9 +894,11 @@ $(H3 Array Initializations) $(H4 The C Way) - C initializes array by positional dependence: + C initializes array by positional dependence. C99 fixes the issue: $(CCODE -int a[3] = { 3,2,2 }; +int a[3] = { 3,2,1 }; +int a[3] = { [2]=1, [0]=3, [1]=2 }; /* C99 designated initializer */ +int a[3] = { [2]=1, [0]=3, 2 }; /* C99 designated initializer */ ) Nested arrays may or may not have the { }: $(CCODE @@ -901,6 +908,7 @@ int b[3][2] = { 2,3, {6,5}, 3,4 }; $(H4 The D Way) D does it by positional dependence too, but an index can be used as well. + The D syntax is lighter than C99 designated initializers. The following all produce the same result: ---------------------------- @@ -981,9 +989,18 @@ $(CCODE #include <tchar.h> tchar string[] = TEXT("hello"); ) + Furthermore, in praxis `wchar_t` is not usable in portable code as its size + is implementation dependent. On POSIX conforming machines it generally + represents an UTF-32 codeunit, on Windows an UTF-16 codeunit. C11 introduced + C++11 types char16_t and char32_t to overcome this issue. + $(H4 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. Alternatively if type inference is used the string can have a $(B c), $(B w) or $(B d) suffix, representing UTF-8, UTF-16 and UTF-32 encoding, respectively. If no suffix is used the type is inferred to be a UTF-8 string: + The type of a string is determined by semantic analysis, so there is no need + to wrap strings in a macro call. Alternatively if type inference is used the + string can have a `c`, `w` or `d` suffix, representing UTF-8, + UTF-16 and UTF-32 encoding, respectively. If no suffix is used the type is + inferred to be a UTF-8 string: ----------------------------- string utf8 = "hello"; // UTF-8 string wstring utf16 = "hello"; // UTF-16 string @@ -1004,8 +1021,12 @@ $(H4 The C Way) $(CCODE enum COLORS { red, blue, green, max }; char *cstring[max] = {"red", "blue", "green" }; +char *cstring[max] = {[red]="red", [blue]="blue", [green]="green" }; /* C99 */ ) - 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. + 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. C99 added designated + initializers to solve that problem. $(H4 The D Way) ----------------------------- @@ -1071,7 +1092,7 @@ if (h != HANDLE_INIT) $(CCODE struct Handle__ HANDLE_INIT; -void init_handle() // call this function upon startup +void init_handle(void) // call this function upon startup { HANDLE_INIT.value = (void *)-1; } @@ -1087,9 +1108,9 @@ if (memcmp(&h,&HANDLE_INIT,sizeof(Handle)) != 0) $(H4 The D Way) - D has powerful metaprogramming abilties which allow it to implement - $(D typedef) as a library feature. Simply import $(B std.typecons) and - use the $(B Typedef) template: + D has powerful metaprogramming abilities which allow it to implement + $(D typedef) as a library feature. Simply import `std.typecons` and + use the `Typedef` template: ----------------------------- import std.typecons; @@ -1103,7 +1124,7 @@ foo(h); // syntax error bar(h); // ok ----------------------------- - To handle a default value, pass the initializer to the $(B Typedef) + To handle a default value, pass the initializer to the `Typedef` template as the second argument and refer to it with the $(D .init) property: @@ -1230,12 +1251,14 @@ qsort(array, sizeof(array)/sizeof(array[0]), ) A compare() must be written for each type, and much careful - typo-prone code needs to be written to make it work. + typo-prone code needs to be written to make it work. The indirect function + call required for each comparison limits the achievable performance of the + `qsort()` routine. $(H4 The D Way) - D has a powerful $(B std.algorithm) module with optimized + D has a powerful `std.algorithm` module with optimized sorting routines, which work for any built-in or user-defined type which can be compared: @@ -1258,6 +1281,14 @@ $(CCODE "This text spans\n\ multiple\n\ lines\n" +) + + C's string literal concatenation doesn't really solve the problem: + +$(CCODE +"This text spans\n" +"multiple\n" +"lines\n" ) If there is a lot of text, this can wind up being tedious.