C++Builder 2007, Compiler changes

In C++Builder 2007, we’re making changes which can be considered ‘breaking’ changes to certain previous projects. MSBuild has been introduced, so you may need to be careful on project imports (if you’ve manually edited your project - the importing will not be automatic in all cases), the compiler is stricter and we’re providing an updated and improved STL via Dinkumware. We also made significant progress in Boost compatibility - I’ll let David Dean discuss those results once we actually ship, since that’s his ‘baby’. 

 

The C++Builder 2007 IDE itself is looking really snappy (I’ll blog about some performance tests later), having included many of the upgrades provided in Delphi 2007, we want to help our C++ users move to using C++ Builder 2007, understanding in advance what changes we’ve been making and why.

C++Builder and C++ Compiler Notes

Possible Binary Incompatibility with Previous Version of C++ Compiler

The C++ runtime libraries have been enhanced and altered to be more current, and the standard C++ libraries from Dinkumware have been updated to version 5.01. Because of these changes, the binary objects generated by the compiler in C++Builder 2007 might differ in some cases from previously compiled versions. The incompatibilities are mainly restricted to the C++ standard libraries. There are no changes in the basic C++ application binary interface (ABI).

Delphi Interface Parameter with Default Value of nil Handled Differently
Assigning the Type void * to Delphi Interface Now Fails to Compile

The Delphi compiler (DCC32) previously generated the following code in the .hpp file when an interface parameter had the default value of nil:

  void methodName(_di_IIntf param = (void *)(0×0));

Previously the C++ compiler incorrectly accepted this syntax.

In this release, both the Delphi compiler and the C++ compiler handle this case differently.

The Delphi compiler now emits code like this in the .hpp file for an interface parameter with a nil default value:

  void methodName(_di_IIntf param = _di_IIntf());

The C++ compiler now gives an error for code that assigns the type "void *" to a Delphi interface. For instance, the line

  void methodName(_di_IIntf param = (void *)(0×0));

now fails to compile, emitting the error:

  Cannot convert ‘void *’ to ‘_di_IIntf’

If you have Delphi code containing an interface parameter with a nil default value, recompile it with DCC32. If you do not have the source, edit the .hpp file and modify all occurrences like this

  void methodName(_di_IIntf param = (void *)(0×0));

to this:

  void methodName(_di_IIntf param = _di_IIntf());

Selecting Both of the Symbol Reference Info Options for Delphi Code

If you are compiling Delphi code inside your C++Builder project, you currently can select only one of the Symbol Reference Info options (either Definitions or Reference Info) on the Project>Options>Delphi Compiler>Compiling dialog box. However, you can select both of these options if you use source-level directives ($xx) as follows:
  • Definition Info ($YD) option: the Delphi compiler records information about where each identifier is defined. For most identifiers–variables, constants, classes, and so forth–the compiler records the location of the declaration. For procedures, functions, and methods, the compiler records the location of the implementation. This enables Code editor browsing.
  • Reference Info ($Y+) option: the Delphi compiler records information about where every identifier is used as well as where it is defined. This enables the References page of the Project Browser.
These compiler options are only available when both the Debugging Information ($D) and the Local debug symbols ($L) options are ON.

Specifying WebSnap Directories

If you are using C++Builder with WebSnap, ensure that the executables are written to the same directory as the HTML files. On Project>Options>Paths and Defaults>Final Output, enter a dot (".") so that the executable is written to the project directory.

Location Change for Precompiled Headers

C++ precompiled header files (PCH) are placed in a different file location than BDS2006. If you import a project from BDS2006, it does not import the project’s PCH file location. You can set the PCH file in Project>Options>Precompiled headers>PCH filename.

Generating C++ Files from the Delphi Compiler

If you want to generate .hpp files (and the corresponding .obj) from a .pas file, you should use dcc32.exe with the -JPHNE switch. Or use -JL on the .dpk file containing the .pas file.

Using WinHelp in C++ Applications

To use WinHelp in a C++ application:

  1. Add #include <WinHelpViewer.hpp>.
  2. Refer to an object declared in the WinHelpViewer header file.

    void LinkWinHelp()

    {

      # pragma startup LinkWinHelp 66

      if (WinHelpTester != NULL)

            WinHelpTester->GetHelpPath();

    }

To download the WinHelp viewer, visit the Microsoft website:

http://www.microsoft.com/downloads/details.aspx?familyid=6ebcfad9-d3f5-4365-8070-334cd175d4bb.

C++ Compiler Changes

To more closely obey the rules of the C++ ANSI Standard, the C++ compiler shipping with C++Builder 2007 is stricter than previous versions. Code that did not generate errors in earlier versions of C++Builder might fail to compile with C++Builder 2007. This section lists some of the common areas where the compiler is stricter. Each case is illustrated with an example showing the problem and how to update the code to compile with C++Builder 2007. Note that there are often many ways to bring offending code up to date. The appropriate method depends on the intent of the original code.

  1. Binding of References and Qualifiers

    There are many constructs that now generate error messages with the C++ compiler included with C++Builder 2007. The rules governing this are described in section 8.5.3 of the 2003 C++ ANSII standard. They fall in the following categories:

    1. Binding a non-const lvalue to to non-const reference. Use the compiler switch -Vbr to allow this.
    2. Binding a temporary to a non-const reference. Use the compiler switch -Vbr to allow this.
    3. Binding of const or volatile objects to non-const or non-volatile methods repectively. Use the compiler switch -Vbn to allow this.

    Previous versions of the Borland C++ compilers allowed various forms of binding to non-const reference parameters. In the following example, for instance, one was allowed to simply cast the psize parameter:

      int takesLongRef(long& l);

      int takesUnsignedPtr(unsigned long* psize) {

         return takesLongRef((long)*psize);
      }

    With C++Builder2007, the above code generates these errors:

      Error E2357 test.cpp 3: Reference initialized with ‘long’, needs lvalue of type ‘long’ in function takesUnsignedPtr(unsigned long *)

      Error E2342 test.cpp 3: Type mismatch in parameter ‘l’ (wanted ‘long &’, got ‘long’) in function takesUnsignedPtr(unsigned long *)

    To remedy this, you can cast psize before dereferencing, as in:

      int takesLongRef(long& l);

      int takesUnsignedPtr(unsigned long* psize) {
        return takesLongRef(*reinterpret_cast<long*>(psize));
      }

    Be aware of cases that involve temporaries in unobvious ways. For example, some binary operators imply a temporary:

      enum { zero, one, two } num;

      num |= two; // Not allowed
      num = num | two; // OK

    Another case that involves temporaries in an unobvious way is the return value of a property. The following example illustrates code that compiled with previous versions of the compiler:

      #include <vcl.h>

      class TTest {
        WideString FData ;
      public:
        __property WideString Data = {read = FData };
      };

      void Func(WideString& wref);

      void test() {
        TTest t;
        Func(t.Data);
      }

    With C++Builder 2007, the above generates two errors:

      Error E2357 test.cpp 14: Reference initialized with ‘const WideString’, needs lvalue of type ‘WideString’ in function test()
      Error E2342 test.cpp 14: Type mismatch in parameter ‘wref’ (wanted ‘WideString &’, got ‘WideString’) in function test()

    You can fix this by changing the reference to a const reference, as in:

      void Func(const WideString& wref);

    Here is an example of trying to bind a const object to a non-const method:

      struct X {
        void foo();
      };
      const X x;
      x.foo(); //error

  2. 1.1 Reconcile Error Dialog

    The temporaries and references issue referred to above is encountered in code generated by previous versions of the Reconcile Error Dialog Wizard. To remedy this, look for the VarToAnsiStr method

      AnsiString VarToAnsiStr (Variant &V, TFieldType DataType)

    and change it to take a const Variant&, as in:

      AnsiString VarToAnsiStr (const Variant &V, TFieldType DataType)

  3. String literals are now constants

    String literals are now considered to be of type ‘const char[]’ by default. This, combined with the stricter qualification binding of const values and types, can generate error messages in code that compiled before.

    You may enable the -Vbs switch to revert string literals to non-const. However, CodeGear recommends that you update the code instead.

    Note that the change in the type of string literals can also change how the compiler resolves calls to overloaded methods. The following example illustrates this:

      void foo(char *);
      void foo(const char *);
      foo("string"); // New Compiler picks foo(const char *)

  4. Template Changes

    The C++ compiler no longer allows an explicit template without the ‘template <>’ prefix. Use the compiler switch -Vbe to allow this. The following example shows this:

      template<class>
      class foo {
        foo();
      };
      foo<int>::foo(); // Error
      template<> foo<int>::foo(); // OK

    Also, the C++ compiler no longer allows explicit template specialization within a class. Use the compiler switch -Vbx to allow this. For example, this generates an error:

      struct S {};
      struct SP
      {
        template <typename T> void foo(const T &) {}
        template <> void foo(const S &) {} // Error
      };

      template <> void SP::foo(const S &) {} //OK
  5. Function Overload Resolution

    One of the areas where the C++Builder 2007 compiler differs the most from the previous version is in overload resolution, which includes the detection of ambiguity. The compiler now better conforms to the rules in section 13.3 of the 2003 C++ ANSI Standard. Several constructs that were previously allowed might now be reported as ambiguous or no match found, requiring that you modify code to clarify its intent.

    The compiler option to revert to the old behavior, not enforcing the new stricter behavior, is -Vbo. However, not all compiler changes can be controlled by this switch, so CodeGear recommends that you update the code instead.

    The following is an example of an ambiguity that was permitted by the previous compiler:

      class X{};
      void foo(X);
      void foo(const X&);
      void ambig() {
        X x;
        foo(x); //error-ambiguous-the previous compiler chose ‘void foo(x)’
      }

  6. 4.1 std::abs() Ambiguity

    The standard abs function might also generate an ambiguity message when invoked with a parameter that does not exactly match the types expected by the various overloaded versions of abs. Here is an example:

      #include <limits>
      bool test(long l) {
        return std::abs(l) > 0;
      }

    The code above generates an error and a warning:

      Error E2015 test.cpp 5: Ambiguity between ’std::abs(int) at C:\devtpscincludemath.h:208′ and ’std::abs(long double) at C:\devtpscincludemath.h:275′ in function test(long)
      Warning W8057 test.cpp 6: Parameter ‘l’ is never used in function test(long)

    To fix this, cast to the type of the overload you wish to invoke. For example,

      #include <limits>
      bool test(long l) {
        return std::abs(static_cast<int>(l)) > 0;
      }

  7. Initialization and Conversion

    The compiler now obeys the rules of 8.5.1 and 13.3.1 of the 2003 C++ ANSI Standard for initialization and conversion:

    1. Direct initialization now requires initialization by a constructor and no longer picks a user conversion sequence.
    2. Copy initialization for objects of the same or derived type now requires a constructor call.
    3. Copy initialization for objects of the different types no longer prefers user conversion over construction. If the compiler finds a suitable user conversion, it now continues to look for (possibly ambiguous) converting constructors. If the chosen conversion function is a converting constructor, the call initializes a temporary of the destination type. The result of the call (which is the temporary for the constructor case) is then used to directinitialize the object. Use the compiler switch -Vbo to revert to the previous behavior.
    4. For an explicit cast, the compiler now performs direct initialization on a temporary.

    This example illustrates the new behavior:

      // In this example, dst is destination type and src is source type
      class A { };
      class V {
      public:
        V() { };
        V( const V & ) { }
        V( const A & ) { }
      };
      class G {
      public:
        G() { }
        operator V() { }
        operator A() { }
      };
      G g; V v;
      // direct initialization
      // ==> constructors are considered.
      V v9(g);
      // Both of these statements previously compiled but now get the error:
      // Error E2015: Ambiguity between ‘V::V(const V &)’ and ‘V::V(const A &)’

      // casts
      // (V)g is treated as V tmp(g) which is direct initialization of ‘tmp’
      // ==> constructors are considered.
      (V)g;
      static_cast<V>(g);
      // Both of these statements previously compiled but now get the error:
      // Error E2015: Ambiguity between ‘V::V(const V &)’ and ‘V::V(const A &)’

      // copy initialization with dst=V src=G
      // ==> user-defined conversion sequences are considered.
      V v4 = g;
      V v5 = G();
      // Both of these statements now compile but previously got the error:
      // Error E2015: Ambiguity between ‘V::V(const A &)’ and ‘V::V(const V &)’

      // copy initialization with dst=V src=V
      // ==> converting constructors of V are considered.
      V v6 = (V)g;
      V v7 = V(g);
      // Both of these statements compiled previously but now get the error:
      // Error E2015: Ambiguity between ‘V::V(const V &)’ and ‘V::V(const A &)’

  8. 5.1 Conversion via user-defined operators

    The new C++Builder 2007 compiler often reports ambiguities for conversions that involve user-defined operators. An example is shown below:

      class AnsiString
      {
      public:
        bool operator ==(const AnsiString& other);
        AnsiString(const wchar_t* src);
      };

      class Variant
      {
      public:
        operator AnsiString() const;/br>     operator wchar_t*() const;
        bool operator ==(const AnsiString& rhs) const
        { return static_cast<AnsiString>(*this) == rhs;}
      };

    C++Builder users might notice that the above is a stripped down version of the VCL AnsiString and Variant classes. Previous versions of the compiler invoked the ‘Variant’ ‘operator AnsiString() const’ for ’static_cast<AnsiString>(*this)’, while C++Builder 2007 uses ‘conversion via constructor’. Since the Variant can be converted to multiple types for which there are AnsiString constructors, the compiler generates an ambiguity error.

    To correct this error, you must eliminate the cast as in:

      bool operator ==(const AnsiString& rhs) const
      { return (*this) == rhs;}

    You can also be explicit about the operator:

      bool operator ==(const AnsiString& rhs) const
      { return this->operator AnsiString() == rhs; }

    5.2 Variant/OleVariant/AnsiString/WideString/TDateTime

    The issue described above with a user-defined conversion operator vs. conversion via constructor might be encountered in several constructs involving the VCL classes Variant, OleVariant, AnsiString, WideString, TDateTime, Currency, and so forth. The following table lists constructs that now generate error messages and the updated syntax.

    Previous Construct Updated Construct Notes
    AnsiString test(OleVariant v) {
      AnsiString ret = (AnsiString) v;
      return ret;
    }
    AnsiString test(OleVariant v) {
      AnsiString ret = /*(AnsiString)*/ v;
      return ret;
    }
    Do not cast RHS when relying on conversion operator in an assignment.
    WideString test(OleVariant v) {
      WideString w(v);
      return w;
    }
    WideString test(OleVariant v) {
      WideString w = v;
      return w;
    }
    Use Copy Initialization instead of the more direct constructor.

    The underlying compiler change for the errors described above is related to the way the compiler now handles initialization and conversion.

8 Responses to “C++Builder 2007, Compiler changes”

  1. Hallvard Vassbotn Says:

    " improved RTL via Dinkumware"

    I think you mean STL (Standard Template Library), rather than RTL (RunTime Library)?

  2. Chris Pattinson Says:

    Ok, well the RTL uses the Dimkumware STL - so both statements are correct. In any case, I updated to avoid confusion.

  3. Dennis Cote Says:

    You have used static_cast without specifying the casted expression type throughout this document. AFAIK you must specify a type when using a static cast in standard C++, i.e. static_cast<type>(object).

  4. Andreas Hausladen Says:

    @Dennis Cote: Looks more like a HTML problem. The < and > are interpreted as tags. If you look at the HTML source code you see the "<int>".

  5. Remy Lebeau Says:

    The #include and static_cast statements in this blog’s examples do not appear correctly in a web browser because all of the angle brackets are not HTML-encoded.

  6. Chris Pattinson Says:

    Ok, I copied the raw HTML from the README file. Formatting changes I had made are lost, but I’ll take accuracy over formatting.

  7. Demeter Says:

    OK. Then how to convert Variant v to TDateTime d?

  8. Bruneau Says:

    Demeter,

    The following illustrates Variant to TDateTime conversion:

    #include <iostream>

    #include <vcl.h>

    void showDate(const TDateTime& dt)

    {

    std::cout << "DateTime = " << DateTimeToStr(dt) << std::endl;

    }

    int main()

    {

    TDateTime dt1 = Sysutils::Now();

    showDate(dt1);

    Variant v1(dt1);

    TDateTime dt2 = v1;

    showDate(dt2);

    TDateTime dt3;

    dt3 = Variants::VarToDateTime(v1);

    showDate(dt3);

    return 0;

    }

Leave a Reply