=Imperfect C++ and more perfect D= ==Introduction== Matthew Wilson gives a list of imperfections and constraints of C++ in his book "Imperfect C++ Practical Solutions for Real-Life Programming". I found out Digitalmars D language gives the positive answer of those questions. This paper tries to make them practical. '''''Note that these comments were probably based on a pre-1.0 version of DMD.''''' ==Organization== Each item consists of three part, the Wilson's rule, the practice of D, the explanation ==Item 1== ''Imperfection:'' C++ does not provide direct support for constraints. (p. 4) ''Answer:'' template Constraint(T, U) { const bool sameType = is(T == U); const bool converable = is(T : U); const bool structType = is(T == struct) && is(T == struct); const bool classType = is(T == class) && is(T == class); const bool derived = converable && !sameType && classType; } class a { } class b : a { } mixin Constraint!(b, a) b_a; mixin Constraint!(a, b) a_b; static assert(Constraint!(b, a).derived); ''Explanation:'' The most correct answer is compile time reflection, in my opinion. D's built-in compile time constraint almost approaches the goal. ==Item 2== ''Imperfection:'' C++ does not provide suitable support for postconditions. (p. 15) ''Answer:'' void func() in { assert(input and precondition) } out { assert(output and postcondition) } body { the function body; } ''Explanation:'' DBC is builtin in D. ''Wilson suggests:'' Recommendation: Use assertions to assert truths about the structure of the code, not about runtime behavior. (p. 21) In D, using DBC for precondition and postcondition. using assert for runtime behavior and static assert for structure of the code. ==Item 3== ''Imperfection:'' The C++ standard does not define an ABI, leading to wholesale incompatibilities between many compilers on many platforms. (p. 92) ''Answer:'' D has a partial description of an ABI in the specification (but it's still in progress). See [DSpec1x:abi.html D 1.x] / [DSpec2x:abi.html D 2.x] ''Explain:'' Walter (the author of D) does a lot work to avoid the incompatibilities between compiler providers. He released the D front end under an open source license. I think each D compiler writer should use it as a reference implementation. ==Item 4== ''Imperfection:'' C++ is not a suitable module-interface language. (p. 121) ''Answer:'' D has modules. ==Item 5== ''Imperfection:'' C and C++ say nothing about threading. (p. 132) ''Answer:'' As Java, D has a synchronized keyword. ==Item 6== ''Imperfection:'' C++ does not provide a mechanism for controlling global object ordering. (p. 158) ''Answer:'' There are two parts to global object initialization, in module and inter module. in module: module scope static this(){} is a module constructor which initializes all static objects in the module. inter module: module is instanced by importation order. ''Explain:'' in module, the static this() constructor is fairly good for initializing. The order of object constructing can be managed well, but the importation order is not as controllable as in module global object. Recommendation: Don't rely on global object initialization ordering. Do utilize global object initialization ordering tracing mechanisms anyway. (p. 161) Recommendation: Don't create threads during global object initialization. (p. 166) ==Item 7== ''Imperfection:'' Function-local static instances of classes with nontrivial constructors are not thread-safe. (p. 171) ''Answer:'' Wouldn't this work: func(...) { static className instance=null; synchronized { if( instance == null ) { instance = new className(...); } } } -- MerlinMM ''Explain:'' instance exists as null reference at program start time (global static program data). The constructor call only happens at 'new className(...)' which is thread safe due to the global mutex setup by the synchronized keyword. Should we ask for an extension to the language to allow say: func(...) { synchonized static className instance; } to effectively given us this functionality. Please let me know if I'm missing something. -- MerlinMM ==Item 8== ''Imperfection:'' The support for Empty Base Optimization (EBO) flavors is not universal, and varies significantly between compilers. (p. 183) ==Item 9== ''Imperfection:'' C and C++ are missing a byte type. (p. 191) ''Answer:'' D has both byte and ubyte (unsigned byte) type. ==Item 10== ''Imperfection:'' C and C++ need fixed-sized integer types. (p. 195) ''Answer:'' All type sizes are fixed and initial values are defined. [[table]type, length/signal, initial value byte, signed 8 bits, 0 ubyte, unsigned 8 bits, 0 short, signed 16 bits, 0 ushort, unsigned 16 bits, 0 int, signed 32 bits, 0 uint, unsigned 32 bits, 0 long, signed 64 bits, 0L ulong, unsigned 64 bits, 0L float, 32 bit floating point, float.nan double, 64 bit floating point, double.nan ] ==Item 11== ''Imperfection:'' C++ needs fixed sized types that are different to, and not implicitly interconvertible with, its built-in integral types. (p. 196) ''Answer:'' import std.stdint; //for typedef int8_t ... class Serializer { // Operations public: void Write(int8_t i){} void Write(uint8_t i){} void Write(int16_t i){} void Write(uint16_t i){} void Write(int32_t i){} void Write(uint32_t i){} void Write(int64_t i){} void Write(uint64_t i){} } void fn() { auto Serializer s = new Serializer; int8_t i8 = 0; int64_t ui64 = 0; int i = 0; s.Write(i8); s.Write(ui64); s.Write(i); //Ambiguous call in C++, valid in D s.Write(0); //Ambiguous call in C++, valid in D } int main() { fn(); return 0; } ==Item 12== *Imperfection:* C and C++ do not provide large fixed-sized integer types. (p. 200) *Answer:* long and ulong is the 64-bit integer type; ==Item 13== ''Imperfection:'' bool should be the same size as int. (p. 205) ''Answer:'' D has a 1 byte bool type. ''Explain:'' Initially D used a bit to represent bool but as per the discussion [BooleanNotEquBit Boolean != Bit], D has bool. ==Item 14== ''Imperfection:'' C and C++ do not provide a dimensionof() operator (for array types). (p. 207) ''Answer:'' array.length is the dimensionof() operator. int[] a = ...; a.length; // return dimension of a; ==Item 15== ''Imperfection:'' Inability to distinguish between arrays and pointers can result in static array size determination being applied to pointers or class types, yielding incorrect results. (p. 211) ''Answer:'' Array and pointer are different types. int* p; int[] a; p = a; // ok a = p; // fail a = p[0 .. 3]; //specify dimension. static if(is(typeof(a) U == U[])) { static assert( is(U == int)); } static if(is(typeof(a) U == U*)) //fail { static assert(0); } static if(is(typeof(p) U == U*)) { static assert( is(U == int)); } static if(is(typeof(p) U == U[]))//fail { static assert(0); } ''Explain:'' ==Item 16== ''Imperfection:'' C and C++ arrays decay into pointers when passed to functions. (p. 214) ''Answer:'' void func(int[] b); void func(int* b); There are overloaded functions; ==Item 17== ''Imperfection:'' C++'s array/pointer duality, combined with its support for polymorphic handling of inherited types, is a hazard against which the compiler provides no assistance. (p. 218) ''Answer:'' D is good here! ==Item 18== ''Imperfection:'' C++ does not support multi-dimensional arrays. (p. 224) double[3][3] matrix; //Rectangular Array; (big array with two dimensional access) double[][] matrix; //two dimensional Array;(array of array) ==Item 19== ''Imperfection:'' C++ needs a null keyword, which can be assigned to, and equality compared with, any pointer type, and with no non-pointer types. (p. 229) ''Answer:'' Yes, null is a keyword in D, and it can initialize no primary type. ''Explain:'' ==Item 20== ''Imperfection:'' Having more than one discrete value for the Boolean type is dangerous. (p. 236) ''Answer:'' D has a bool type for true and false; int which used as boolean type in D, evaluated zero as false, and no-zero for true; ==Item 21== ''Imperfection:'' Specification of the size of integer types as implementation dependent in C/C++ reduces the portability of integer literals. (p. 238) ''Answer:'' integer literals evaluated as its smallest fixed-size integer; ==Item 22== ''Imperfection:'' C++ compilers do not agree on the syntax for suffixes of integer literals larger than those that will fit into (unsigned) long. (p. 239) ''Answer:'' this is error at debug mode; ==Item 23== ''Constraint:'' C and C++ do not guarantee to ensure that identical string literals will be folded within a single link unit, and cannot do so across separate link units. (p. 242) ''Answer:'' I know nothing about this in D. ==Item 24== ''Constraint:'' Casting away constness of a constant object is not legal, and will likely lead to unintended consequences. (p. 245) ''Answer:'' D allows both const and immutable to be cast away, this is because casting circumvents the type system, D just hopes you know what you are doing. ==Item 25== ''Constraint:'' Constants of class type are evaluated as immutable global objects, rather than as compile-time constants. Failure to be mindful of this distinction is hazardous. (p. 246) ''Answer:'' constants is the compile time constants. ''Recommendation:'' Avoid using enum values larger than those that will fit in an int. (p. 248) ==Item 26== ''Imperfection:'' C++ does not support member constants of floating-point type. (p. 249) ''Answer:'' no such constants; ==Item 27== ''Constraint:'' C++ does not support member constants of class type. (p. 250) ''Answer:'' no such constants; ''Recommendation:'' Never attempt to simulate class-type member constants with function local static objects. (p. 251) ==Item 28== ''Imperfection:'' C++ does not provide access control between types involved in a composition relationship. (p. 257) ''Answer:'' package is the access modifer, for module scope public; ==Item 29== ''Imperfection:'' The introduction of new keywords into the language requires contingency mechanisms to maintain portability. (p. 268) ''Answer:'' Maybe later, but not now; ==Item 30== ''Imperfection:'' Abuse of C++'s last-known access specifier mechanism leads to code that is hard to use and hard to maintain. (p. 271) ''Answer:'' public: void member(); public void member(); public{ void member(); ... } all above is legal, which would you like? go ahead. ==Item 31== ''Imperfection:'' Implicitly interpreting non-Boolean (sub-)expressions leads to failure to be mindful of the values of certain types, with a consequent propensity to over-engineer user-defined types. (p. 274) ''Answer'' ==Item 32== ''Imperfection:'' The implicit interpretation of scalar types as Boolean in conditional statements facilitates erroneous assignment caused by syntax errors. (p. 275) ==Item 33== ''Imperfection:'' The contradiction between the old and new for-statement rules results in non-portable code. (p. 278) ==Item 34== ''Imperfection:'' for statements requiring initializers of two or more types render the new for-scoping rule irrelevant. (p. 279) ==Item 35== ''Imperfection:'' C++ provides no type-safety for the use of conceptual defined types, except where such types have incompatible underlying types. (p. 294) ==Item 36== ''Imperfection:'' C++ does not support overloading on conceptually defined types, except where such types happen to have different underlying types on a particular platform. (p. 294) ''Answer'' ''Recommendation:'' Avoid making conceptual typedefs that are comprised of contextual typedefs. (p. 304) ==Item 37== ''Imperfection:'' Logically related types in C++ can, and usually do, have incompatible interfaces and operations, rendering a generalized approach to type manipulation sometimes difficult, and often impossible. (p. 341) ''Answer'' opCmp is only relationship operator overloading. opEqual is only identification operator overloading. ==Item 38== ''Imperfection:'' The C++ template mechanism instantiates a template based on the arguments to it, and takes no account of how, or in what form, those arguments are subsequently used within the templates. This can lead to the generation of inefficient and/or erroneous code since temporary instances of class type may be created during the forwarding of arguments through the template. (p. 387) ''Answer'' Maybe true or no true. ==Item 39== ''Imperfection:'' Overloading operator &() breaks encapsulation. (p. 425) ''Answer'' operator & can overload. ==Item 40== ''Imperfection:'' Provision of implicit conversion operator(s) to pointer type(s) along with subscript operator(s) is non-portable. (p. 434) ==Item 41== ''Imperfection:'' Some C++ compilers will substitute prefix increment/decrement overloaded operators at the call site in place of absent postfix equivalents. (p.440) ''Answer'' never ==Item 42== ''Imperfection:'' Overloading the && and || operators for class types represents an invisible breaking of short-circuit evaluation. (p. 454) Answer && and || can not overload. ==Item 43== ''Imperfection:'' C and C++ make you choose between performance and flexibility when allocating memory. (p. 476) ''Answer'' D has choosen a little of both for you. You can allocate items on stack with scope keyword. You can define your own class allocators if you like malloc/free, but by default you get garbage collector which adds flexibility (e.g. dynamic arrays). ==Item 44== ''Imperfection:'' C/C++ does not support dynamically dimensioned multidimensional arrays. (p. 491) ''Answer'' ''Recommendation:'' An array size shim should always be used when determining the sizes of arrays, as all other approaches do not generalize for both built-in and user defined array types. (p. 505) ==Item 45== ''Imperfection:'' C++ does not support local functor classes (to be used with template algorithms). (p. 514) ''Answer:'' local class is there. ==Item 46== ''Imperfection:'' C++ does not provide properties. (p. 531) ''Answer'' built-in properties: int.sizeof yields 4 float.nan yields the floating point nan (Not A Number) value (float).nan yields the floating point nan value (3).sizeof yields 4 (because 3 is an int) 2.sizeof syntax error, since "2." is a floating point number int.init default initializer for int' user properties is easy: struct Foo { int data() { return m_data; }// read property int data(int value) { return m_data = value; } // write property private: int m_data; } int test() { Foo f; f.data = 3;// same as f.data(3); return f.data + 3;// same as return f.data() + 3; }