MKoD - D Programming LanguageQuestions & Answers
D aka Mars FAQ
MKoD - D Programming Language Website Questions General D Programming Language Questions
? Why are nearly all the D's in red on the site's text? Walter was originally going to call his new language "Mars" partly after his company's web site name "DigitalMars", but many of the developers in DigitalMars' user group forums suggested constantly that "D>" was a more logically name. Afterall the language was meant to be an successor to C and C++ as many were quick to point out. Thus, everyone knows Mars is called the Red Planet, and so D is aka Mars...the D became red. :)) ? Why doesn't D have Multiple Class Inheritance? From Walter's post 16th August 2001: Experience suggests that single inheritance with interfaces will cover nearly all the bases. ? What are D's rules for overloading functions in Class and Module functions? From Walter's post 6th September 2002: With overloading, D deliberately dispenses with the C++ idea of some type conversions being better then others. In D, there are 3 results from an overload compare: exact match, match with conversions, and no match. This is a reaction to C++'s rules being pages to express, and can get difficult to figure out when looking at multiple arguments. ? Is there an opAssign in D to overload the = (equal-sign assignment) much like C++'s operator= ? No. From Walter's post 5th March 2004: Overloading of assignment operators is deliberately omitted. I think its benefits are swamped by the confusion and code bloat it leads to. Many of the benefits in C++ of = overloading is to keep track of memory pointers. This is irrelevant in D, which does automatic memory management. Additionally, class objects in D are by reference, not by value, so an = just copies the reference, not the members. ? Does the order of declarations in a file matter? From Walter's post 16th August 2001: The order does not matter. It is surprising how liberating that is. ? Is the only thing using C++ in DMD (D's Compiler) found in the recls.d file, which is a part of Phobos (D's runtime Library)? From Walter's post 18th January 2005: The DMD front end does not use recls. The D runtime library, Phobos, has recls included in it. Recls (a platform-independent recursive search library) is a demonstration of how to connect C++ code to D. DMD is most definitely written in C++, but a fairly simple subset of it. It doesn't rely on STL, iostreams, exception handling, templates, or even RTTI. (This makes the source portable to a much wider variety of C++ compilers.) It does use a garbage collector. It wouldn't be hard to translate the DMD front end into D, but then it would be very hard to connect it to the back ends like gcc's. ? Isn't it easier to write robust software in D than in C++, because D is more fault-tolerant? From Walter's post 15th April 2005: D is easier to write robust code in than C++ because it is more fault *intolerant* than C++ is. The idea is not to paper over bugs and soldier on, which would be fault-tolerant, but to set things up so that any faults cannot be ignored. Here's an example: C++: class Foo { ... void func(); ... }; ... void bar() { Foo *f; // oops, forgot to initialize it ... f->func(); }f is initialized with garbage. The f->foo() may or may not fail. If it doesn't fail, the bug may proceed unnoticed. Does this kind of problem happen in the wild? Happens all the time. D: class Foo { ... void func(); ... } ... void bar() { Foo f; // oops, forgot to initialize it ... f.func(); }D will provide a default initialization of f to null. Then, if f is dereference as in f.func(), you'll get a null pointer exception. Every time, in the same place. This helps with two main needs for writing robust code:
C++: Foo *f = new Foo; Foo *g = f; ... delete f; ... g->func();That'll appear to 'work' most of the time, but will rarely fail, and such problems are typically very hard to track down. D: Foo f = new Foo; Foo g = f; ... delete f; ... g.func();You're much more likely to get a null pointer exception with D, because when delete deletes a class reference, it nulls out the vptr. It's not perfect, as in the meantime a new class object could be allocated using that same chunk of memory, but in my own experience it has been very helpful in exposing bugs, much more so than C++'s method. ? Walter, do you plan on having a commercial D development environment, or will this be left up to others to do? From Walter's post 23rd November 2004: I'm leaving it up to others. There's just no way I can take on another big project. ? The Linker is adding an extra 6KB for some reason, how do I figure out what has been adding to the D executable? From Walter's post 26rd November 2005: The .map file (must add the /map command-switch when compiling code) will list all the sizes of the sections brought in. This should narrow down where the 6KB is coming from. ? How stable is the D's "Application Binary Interface" (ABI) right now? From Walter's post 20th December 2004: It's pretty well fixed now. I plan on extending classinfo, but that won't change the existing layout and meaning of its current members. ? Does D treat a non-suffixed floating point literal number as a double? ( 5.987 vs using 5.987f ) From Walter's post 8th September 2002: Yes, that's correct. ? How is it possible to get a different result for the same float-point number in the following statement? (Please note, that this question is based on results gotten with dmd v0.119 and below.) //roundingfloat1.d private import std.stdio; private import std.math; int main() { writefln("Using float literal: %12.6f %12.6f", (.5 + 1e6*0.0000195f), floor(.5 + 1e6*0.0000195f)); return 0; }Which produces the following output: With dmd v0.119 and below on 01.Apr.05 -------------------------------------- C:\dmd>dmd roundingfloat1.d C:\dmd\bin\..\..\dm\bin\link.exe roundingfloat1,,,user32+kernel32/noi; C:\dmd>roundingfloat1 Using float literal: 19.999999 20.000000 Now with the new changes made in dmd v0.120 done on 06.Apr.05 (*) ------------------------------------------------------------- C:\dmd>dmd roundingfloat1.d C:\dmd\bin\..\..\dm\bin\link.exe roundingfloat1,,,user32+kernel32/noi; C:\dmd>roundingfloat1 Using float literal: 20.000000 20.000000 C:\dmd>From Walter's post 1st April 2005: I suggest in general viewing how these things work (floating, chopping, rounding, precision, etc.) is to print things using the %a format (which prints out ALL the bits in hexadecimal format). As to the specific case above, let's break down each (using suffix 'd' to represent double): (.5 + 1e6*0.0000195f) => (.5d + 1e6d * cast(double)0.0000195f), result is double floor(.5 + 1e6*0.0000195f)) => floor(cast(real)(.5d + 1e6d * cast(double)0.0000195f)), result is real //roundingfloat2.d private import std.stdio; private import std.math; int main() { writefln("Using the same float-point literal: double=%a, real=%a", (.5 + 1e6 * cast(double)0.0000195f), floor(cast(real)(.5 + 1e6 * cast(double)0.0000195f))); writefln("Using the same float-point literal: double=%12.6f, real=%12.6f", (.5 + 1e6 * cast(double)0.0000195f), floor(cast(real)(.5 + 1e6 * cast(double)0.0000195f))); writefln(); writefln("Here we force the literals to be reals using the \"L\" suffix =%a (%12.6f)\n" "vs\n" "using doubles then casting to a real, and then using floor()=%a (%12.6f)", (.5L + 1e6L * 0.0000195L), (.5L + 1e6L * 0.0000195L), floor(cast(real)(.5 + 1e6 * cast(double)0.0000195f)), floor(cast(real)(.5 + 1e6 * cast(double)0.0000195f)) ); return 0; } With dmd v0.119 and below on 01.Apr.05 -------------------------------------- C:\dmd>dmd roundingfloat2.d C:\dmd\bin\..\..\dm\bin\link.exe roundingfloat2,,,user32+kernel32/noi; C:\dmd>roundingfloat2 Using the same float-point literal: double=0x1.3fffff4afp+4, real=0x1.4p+4 Using the same float-point literal: double= 19.999999, real= 20.000000 Here we force the literals to be reals using the "L" suffix =0x1.4000000000000002p+4 ( 20.000000) vs using doubles then casting to a real, and then using floor()=0x1.4p+4 ( 20.000000) C:\dmd> Now with the new changes made in dmd v0.120 done on 06.Apr.05 (*) ------------------------------------------------------------- C:\dmd>dmd roundingfloat2.d C:\dmd\bin\..\..\dm\bin\link.exe roundingfloat2,,,user32+kernel32/noi; C:\dmd>roundingfloat2 Using the same float-point literal: double=0x1.4p+4, real=0x1.4p+4 Using the same float-point literal: double= 20.000000, real= 20.000000 Here we force the literals to be reals using the "L" suffix =0x1.4000000000000002p+4 ( 20.000000) vs using doubles then casting to a real, and then using floor()=0x1.4p+4 ( 20.000000) C:\dmd>When writef prints a real, it adds ".5" to the last signficant decimal digit and chops. This will give DIFFERENT results for a double and for a real. It's also DIFFERENT from the binary rounding that goes on in intermediate floating point calculations, which adds "half a bit" (not .5) and chops. Also, realize that internally to the FPU, a "guard bit" and a "sticky bit" are maintained for a floating point value, these influence rounding, and are discarded when a value leaves the FPU and is written to memory. What is happening here is that you start with a value that is not exactly representable, then putting it through a series of precision changes and roundings, and comparing it with the result of a different series of precision changes and roundings, and expecting the results to match bit for bit. There's no way to make that happen. (*) From Walter's D v0.120 Changelog entry 6th April 2005: Compiler now permitted to retain floating point literals and do constant folding on them in a precision that is greater than the precision of its type. ? If the compiler offers an 80 bits real type (inserted note: on Intel x86 CPUs, real is 80 bits, but on most non-Intel a real will be 64 bits), and the FPU calculates only in 80 bits format (inserted note: but only on Intel x86 CPUs), then what's the (real) reason that floating-point literals (non-suffixed) are parsed by default as double precision (64 bits) values? From Walter's post 3rd April 2005: Actually, many languages, mathematical programs, and even C compilers have *dropped* support for 80 bit long doubles. At one point, Microsoft had even made it impossible to execute 80 bit floating instructions on their upcoming Win64 (I made some frantic phone calls to them and apparently was the only one who ever made a case to them in favor of 80 bit long doubles, they said they'd put the support back in). Intel doesn't support 80 bit reals on any of their new vector floating point instructions. The 64 bit chips only support it in a 'legacy' manner. Java, C#, VC, Javascript do not support 80 bit reals. I haven't done a comprehensive survey of computer languages, but as far as I can tell D stands pretty much alone in its support for 80 bits, along with a handful of C/C++ compilers (including DMC). Because of this shaky operating system and chip support for 80 bits, it would be a mistake to center D's floating point around 80 bits. Some systems may force a reversion to 64 bits. On the other hand, ongoing system support for 64 bit doubles is virtually guaranteed, and D generally follows C's rules with these. (BTW, this thread is a classic example of "build it, and they will come". D is almost single handedly rescuing 80 bit floating point from oblivion, since it makes such a big deal about it and has wound up interesting a lot of people in it. Before D, as far as I could tell, nobody cared a whit about it. I think it's great that this has struck such a responsive chord.) ? Does D support having a negative infinity, and / or a negative zero? From Walter's post 22nd September 2004: Yes. Negative infinity as well as negative zero are both fully supported in D. // NegZero.d // How to create and display a -inf and -0 private import std.stdio; int main() { writefln("Display a Neg Inf=%f and a Neg Zero=%f", -float.infinity, -0.0); return 0; } C:\dmd>dmd negzero.d C:\dmd\bin\..\..\dm\bin\link.exe negzero,,,user32+kernel32/noi; C:\dmd>negzero Display a Neg Inf=-inf and a Neg Zero=-0.000000 C:\dmd>Note: That to generate a negative 0 is to write it as -0.0 and not -0, because the negation happens before the conversion to a double. ? Doesn't "===" and "is" (both operators for comparing to a reference) have the same meaning, which should be used? From Walter's post 20th January 2005: Yes, use "is" from now on. The === (triple equals) turned out to be a problem distinguishing from == (double equals) with some fonts. Note: As of the 7th of June 2005 with D v0.126 both "===" and "!==" are now deprecated, replaced with the "is" and "!is" operators. ? Is it permitted to write commercial software products using the free D (dmd) compiler and its Phobos library? From Walter's post 30th January 2005: Yes. ? Every D program I've seen has the source code. Does this mean any distributions has to include the D source code? From Walter's post 29th March 2005: For code that you write, you can copyright it, put it in public domain, do closed source, whatever you want as it is your code. D most definitely does NOT require that any programs compiled with D and linked with the Phobos D runtime library be made open source. The D runtime library is open source, but is not GPL. However, the D compiler front end source is GPL and any program that incorporates the D compiler source code will need to be GPL or acquire a separate license from Digital Mars. ? What is the license on the D specification? From Walter's post 3rd April 2005: It's just plain old copyrighted. ? Is the D documentation under a re-distributable license? From Walter's post 3rd April 2005: For the time being, the docs are not redistributable. The reason is I want to keep them from getting too fragmented. ? Could someone demand to you to release all of the DMD source since it uses the GPL'd D compiler front end source? From Walter's post 3rd April 2005: Good question. No, since Digital Mars as the copyright holder has additional rights. ? Does the D license allow anyone, to create their very own version of the D compiler? From Walter's post 9th September 2005: You're free to write your own D implementation from scratch, on your terms. I disagree this will destroy D - I think it is a great source of strength for other languages to have competing implementations, why not for D? I encourage anyone who wants to create an independent D compiler. You're free to write your own D implementation using the DMD front end sources - use them freely if the result will be GPL'd, you'll need a license to use the DMD front end code for a non-GPL product. The D specification is copyrighted. It is not patented. Therefore, legally, you can write your own D specification as long as it doesn't copy verbatim from the Digital Mars one. That said, I'm pretty easy about giving permission to do wholesale copying if I feel it will be good for the D community. Some examples are the foreign language versions of the spec prepared by others. You may use the DMD compiler and Phobos library to create closed source, proprietary, commercial D programs without needing a further license from Digital Mars. You may not use DMD to create programs which, if they fail, will cause injury or significant property damage. If you want to create such programs, you'll need to send me a piece of paper indemnifying Digital Mars from all liability. ? Please clarify the licensing of the different areas of the D Compiler for the following questions. • Is the Front-end under a GPL / Artistic License? From Walter's post 27th November 2005: Yes. • What is the D Compiler's back-end licensed as? From Walter's post 27th November 2005: The DMD back end is proprietary. However, one can use GDC which is 100% GPL. • Is the Phobos Library under a GPL License? From Walter's post 27th November 2005: No, it's mostly public domain or under a free redistribution copyright. • Is there a No-Redistribution on some / all of it? From Walter's post 27th November 2005: No. (Inserted Note: Currently the docs is not redistributable, in order to keep them from getting too fragmented while D is still in a beta state). ? What kind of app could D be especially suitable for? From Walter's post 24th January 2005: I view D as serving the same space as C and C++ do. ? What is D's default implicit conversion for "string literals" (example: "abc") and for "whole number literals" (example: 123) without the "L" suffix indicating that it's a "long" type? Answer based on something Walter agreed with in a post on 28th February 2005: "String literals" in code, begin with no defined type, in other words, they're not defined as a char[], wchar[], or a dchar[] starting out...but as of D v0.129 they now have postfixes that can be added "char[]"c, "wchar[]"w, and "dchar[]"d, where as "whole number literals" without the "L" postfixe (meaning to cast as a "long") are defined as the "int" type. Unless of course, the whole number literal's value is greater than int.max, then it's promoted to a "long" type. // literal.d // a numeric literal example private import std.stdio; int main() { printvalue( 12 ); printvalue( 2147483648 ); // int.max + 1 printvalue( 12L ); // with the "L" suffix printvalue( 2147483648U ); // with the "U" suffix return 0; } void printvalue( in int i ) { writefln( "int value=%d", i ); } void printvalue( in uint ui ) { writefln( "uint value=%d", ui ); } void printvalue( in long l ) { writefln( "long value=%d", l ); } C:\dmd>bin\dmd literal.d C:\dmd\bin\..\..\dm\bin\link.exe literal,,,user32+kernel32/noi; C:\dmd>literal int value=12 long value=2147483648 long value=12 uint value=2147483648 C:\dmd> // strliteral.d // a string literal example private import std.stdio; int main() { // Because of the function's parameter overloading // the not defined typed string literal will // error. Stating that the parameter matchs both // the char[] and dchar[], thus a cast() statement // with be needed to choose which function is called. printvalue( cast(char[])"string literal without any postfix." ); // The above problem is solved by using the postfixes. printvalue( "char[] postfix was used."c ); printvalue( "wchar[] postfix was used."w ); printvalue( "dchar[] postfix was used."d ); return 0; } void printvalue( in char[] s ) { writefln( "char[] value=%s", s ); } void printvalue( in wchar[] ws ) { writefln( "wchar[] value=%s", ws ); } void printvalue( in dchar[] ds ) { writefln( "dchar[] value=%s", ds ); } C:\dmd>dmd strliteral.d C:\dmd\bin\..\..\dm\bin\link.exe strliteral,,,user32+kernel32/noi; C:\dmd>strliteral char[] value=string literal without any postfix. char[] value=char[] postfix was used. wchar[] value=wchar[] postfix was used. dchar[] value=dchar[] postfix was used. C:\dmd>. ? Are associative arrays (AA) using a hash table internally? From Walter's post 1st March 2005: Yes. They are general purpose and aren't as efficient as a full custom hash table, but they work better than what one could quickly or casually build, and they're available at your fingertips. (Foot-note: D implements AA as a hash table with a binary tree to handle hash collisions.) ? Is there a way to 'manually' allocate memory for an associative array (AA) in D? From Walter's post 8th April 2006: Not without reworking internal\aaA.d. ? What are the reasons / differences in using foreach over a for loop? Is it just performance or readability? From Walter's post 8th April 2006: By using foreach, you are letting the compiler decide on the optimization rather than worrying about it yourself. For example - are pointers or indices better? Should I cache the termination condition or not? Should I rotate the loop or not? The answers to these questions are not easy, and can vary from machine to machine. Like register assignment, let the compiler do the optimization. for (int i = 0; i < foo.length; i++) or: for (int i = 0; i < foo.length; ++i) or: for (T *p = &foo[0]; p < &foo[length]; p++) or: T *pend = &foo[length]; for (T *p = &foo[0]; p < pend; ++p) or: T *pend = &foo[length]; T *p = &foo[0]; if (p < pend) { do { ... } while (++p < pend); } and, of course, should I use size_t or int? for (size_t i = 0; i < foo.length; i++) Let the compiler pick: foreach (v; foo) { ... }Note that we don't even need to know what the type T needs to be, thus avoiding bugs when T changes. I don't even have to know if foo is an array, or an associative array, or a struct, or a collection class. This will also avoid the common fencepost bug: for (int i = 0; i <= foo.length; i++)And it also avoids the need to manually create a temporary if foo is a function call. The only reason to use a for loop is if your loop does not fit in the conventional form, like if you want to go through the array backwards or change the termination condition on the fly. ? Since D isn't backward compatible with C++, is it still possible for the two languages to talk to one another in some binary form? From Walter's post 2st March 2005: D can hook directly to C++ vtables, as long as the C++ class uses single inheritance. It becomes a COM interface. Don't actually need to worry about AddRef() and Release(). That said, I think the yoogest (too borrow from Trump) problem in interfacing with C++ is C++'s memory allocation strategies. A lot of those C++ libraries have their own MyFunkyStringClass, and it's use is all wrapped up in how they decided to do memory. Trying to hook that stuff up to D is such a mess it would be easier to reimplement it in D entirely. ? Will the following trick shown below continue to be supported in future D builds? And if so, shouldn't this be added to the specs? // trick1.d private import std.string; private import std.stdio; void main() { char[] s = "a b c d e"; char[][] sv; // normally you need to call the function with the character array as the first parameter sv = split( s, " " ); writefln( "normal use, sv.length=%d", sv.length ); for ( int i = 0; i < sv.length; i++ ) writefln( "sv[ %d ]=%s", i, sv[ i ] ); writefln(); // Clears the string array for reuse sv.length = 0; writefln( "Clearing the string array, sv.length=%d", sv.length ); writefln(); // But there's a trick to call the function as if it were a method or property sv = s.split( " " ); writefln( "using the trick, sv.length=%d", sv.length ); for ( int i = 0; i < sv.length; i++ ) writefln( "sv[ %d ]=%s", i, sv[ i ] ); writefln(); } C:\dmd>bin\dmd trick1.d C:\dmd\bin\..\..\dm\bin\link.exe trick1,,,user32+kernel32/noi; C:\dmd>trick1 normal use, sv.length=5 sv[ 0 ]=a sv[ 1 ]=b sv[ 2 ]=c sv[ 3 ]=d sv[ 4 ]=e Clearing the string array, sv.length=0 using the trick, sv.length=5 sv[ 0 ]=a sv[ 1 ]=b sv[ 2 ]=c sv[ 3 ]=d sv[ 4 ]=e C:\dmd>From Walter's post 3rd March 2005: I don't see any reason to take it out. I put it in so that arrays and basic types could be extended by the programmer to have generic 'property' like features. It should go in to spec, you're right. ? There are a lot of D's features that rely on way "C does it." So, why can't D improve upon these features, and still retain its backward C compatibility? An example of this might be, for D to use the FPU's highest level of precision, which on a Intel x86 is 80-bits, and then use that for all the non-suffixed floating-point literals (which D currently only sets as 64-bit doubles) for mathematical operations. After which, D could just fold the remaining result into a floating-point variable datasize...whether it's a float 32-bits, double 64-bits, or real 80-bits on Intels / real 64-bits on non-Intels without losing any accuracy! From Walter's posts 4th and 5th April 2005: 1. A big problem with subtly changing C semantics is that many programmers that D appeals to are longtime C programmers, and the semantics of C are burned into their brain. It would cause a lot of grief to change them. It's ok to change things in an obvious way, like how casts are done, but changing the subtle behaviors needs to be approached with a lot of caution. 2. I do know of several programs that are designed to "explore" the limits and characteristics of the floating point implementation that will produce incorrect results. I don't think it would be a problem if those programs broke. (*) C programs that provide a "back end" or VM to languages that require 64 bit floats, no more, no less, could break when ported to D. Another problem is that the program can produce different results when optimized - because optimization produces more opportunities for constant folding. This can already happen, though, because of the way the FPU handles intermediate results, and the only problem I know of that has caused is (*). And lastly there's the potential problem of using the D front end with a C optimizer/code generator that would be very difficult to upgrade to this new behavior with floating point constants. I know the DMD back end has this problem. I don't know if GDC does. Requiring this new behavior can retard the development of D compilers. (Note: As of D v0.126 (06.Apr.05) the compiler is now permitted to retain floating point literals, and do constant folding on them in a precision that is greater than the precision of its type. ? Does D have full binary compatibility for C? From Walter's post 27th December 2004: It does, for the C compiler it is designed to work with. Be sure and ask the vendor of any C library you wish to use to support DMC++ (the DigitalMars C++ compiler, D's back-end processor that will link in the C library). Note: DMD (the DigitalMars D Compiler) / DMC++ create OMF type object files, an older Microsoft / Intel format that isn't compatible with Microsoft's current COFF type object format (that VC++ and some other C\C++ compilers create). Also the linux version of DMD produces object files in the ELF format which is standard on linux. ? Why does the rhs of delete have to be lvalue? From Walter's post 11th March 2005:So the reference can be nulled out. You won't have to write: delete p; p = null; which is a common C++ idiom. ? Why doesn't an inout parameter in a struct's opcmp() not work? Here's an example: //opCmp_InOut.d private import std.stdio; struct A { int i; int opCmp(inout A a) { return i - a.i; } } void main() { A[] l = new A[6]; l[0].i = 2; l[1].i = 21; l[2].i = 12; l[3].i = 22; l[4].i = 1; l.sort; foreach(int i, A a; l) writefln(a.i); }From Walter's post 8th July 2005: Rewrite this line "int opcmp(inout A a) { return i -a.i; }" as "int opcmp(A* a) { return i - a.i; }" and you'll get the behavior you're looking for. The sorter looks for either an "in A" parameter or an "in A*" parameter for the opCmp. ? Can virtual functions be called inside the constructor? From Walter's post 12th July 2005: Yes. D constructors are passed a "fully formed" class object instance, with each of its fields set to its corresponding default value. This includes the pointer to the virtual function table pointer (vptr), which is set to the most derived class' vtbl[]. This is unlike C++, where the fields are garbage, and vptr is set to the vtbl[] of the constructor's class, rather than the vtbl[] of the most derived class. Below is a modified version of an example posted by Ben Hinkle. // vtable.d // by Ben Hinkle on the 11th of July 2005 <ben[dot]hinkle[at]gmail[dot]com> private import std.stdio; class A { this() { foo(); } void foo() { writefln("class A.foo() - hi"); } } class B : A { void foo() { writefln("class B.foo() - hi2"); super.foo(); } } class C { int x = 20; this() { writefln("class C.this() - %d", x); } } int main() { // The example statement below will first print "hi2" and then "hi", // because the object vtable is initialized before the ctor is called. B b = new B; // Initializers are also applied before the ctor is run, the example // statement below will print a "20". new C; return 0; } C:\dmd>dmd vtable.d C:\dmd\bin\..\..\dm\bin\link.exe vtable,,,user32+kernel32/noi; C:\dmd>vtable class B.foo() - hi2 class A.foo() - hi class C.this() - 20 C:\dmd>. ? How does D implement interfaces, isn't it a list of delegate pointers? From Walter's post 31st May 2005: No, in D, an interface is a pointer to a vtbl[]. Each interface implemented by a class gets a vptr allocated in the class instance, and that vptr points to a vtbl[] for the functions that implement it. Casting a class to an interface means adding an offset to the 'this' pointer to point to the vptr member for that interface. Casting from an interface back to the class means subtracting that offset. The subtract is a little tricky since the pointer doesn't know where it came from, so the first entry in the interface's vtbl[] is an instance of the class Interface with the necessary offset. The cast is handled by the library function _d_interface_cast(). So, an interface "instantiation" is a physical pointer that can get copied, passed as a function argument, etc. The problem with interface covariance with classes is that that pointer is not the same as the 'this' pointer to a class. There are ways to deal with this, such as putting out special vtbl[]s with 'this' adjustor thunks in them, but D doesn't do that now and since it's a complex implementation problem, it won't for now. Covariance for class inheritance works because the 'this' for the base class is exactly the same as the 'this' for a derived class. ? Why does D's casts "stick out like a sore thumb?" From Walter's post 7th July 2005: D's cast syntax was deliberately chosen to make it stand out. There's a school of thought that says that a cast is indicative either of poor program design or poor language design. But since D is meant to be a practical language, sometimes it's more practical to cast than redesign the whole program! Nevertheless, since casts are a blunt instrument, they should be used with caution and merit attention in a serious code review. Hence the standout syntax. ? Shouldn't the GC guarantee to run the destructors before freeing the space allocated by that object? From Walter's post 4th April 2005: It does. ? Why doesn't the GC free ALL memory at program termination? From Walter's post 4th April 2005: 1. It doesn't need to, since the operating system will do that automatically. 2. Because it's slow to run the gc at termination, and pointless since the operating system will recover any left over resources. ? Doesn't both the Associative Arrays (AA) and Dynamic Arrays rely on the Garbage Collector (GC)? From Walter's post 25th June 2005: Associative Arrays, yes. Dynamic Arrays, no. Dynamic Arrays are only involved with the GC when you 'new' them or 'delete' them or set the .length property or do a .dup. You can create Dynamic Arrays using malloc(), or you can set them to point to anything in memory. As an example, you can allocate a dynamic array without using the GC via: char* p = cast(char*)malloc(length); char[] a = p[0 .. length];Note: To also stay clear of using the '~' or '~=' string concatenate operators, because these will cause the GC to make allocations too. ? Is using a Garbage Collector (GC) a performance killer? From Walter's post 25th June 2005: I was on the "explicit memory allocation is obviously faster than gc" train for many years. Until I started working with gc's. Then I discovered to my amazement that gc'd apps can actually be faster. The reasons are: 1. the code doesn't have to keep track of who owns the memory. Reference counting, for example, can be surprisingly costly. 2. often, the "keep track of who owns the memory" is done by "when in doubt, make extra copies of the data". This of course means "slow". I've found that gc'd apps tend to make a lot fewer allocations, and nothing speeds up overall memory allocation more than doing fewer allocations. 3. C's (and C++'s) idea that strings are null-terminated means slicing doesn't work, and lots of extra copies are made. Of course, it's possible to work around these problems in C++, and I've seen it done, but it requires complicated custom code, and very few ever bother. ? Why didn't my D code catch the exception (this is what I had expected), instead of getting an uncatchable segfault? From Walter's post 18th April 2005: Seg faulting is throwing an exception - it's just done by the hardware. You can even catch them: //test.d int main() { int* p; try { *p = 3; } catch (Object o) { printf("caught the exception: %.*s\n", o.toString()); } return 0; }Running it gives: C:>test caught the exception: Access Violation. ? What is the cost (performance and memory wise) of throwing and catching an exception in D? From Walter's post 28th June 2005: D's exception handling mechanism is the same as C++'s is, as D and C++ share the same back end. It'll still be an order or magnitude or more slower than, say, returning an error code. |