Stricter C++ Compilers (Clang-enhanced C++ Compilers)

From RAD Studio
Jump to: navigation, search

Go Up to Differences Between Clang-enhanced C++ Compilers and Previous-Generation C++ Compilers


Clang-enhanced C++ compilers are based on Clang. Clang-enhanced C++ compilers provide C++11 support, and are also stricter compilers than BCC32.

Structural Syntax

BCC32 allows one to mix __try and catch.

Clang-enhanced C++ compilers want try/catch (standard C++) or __try/__except/__finally (Microsoft extensions).

Type Conversions

Assigning literal C-strings to char * generates a warning unless that pointer is declared const.

BCC32 allows assignment between char * and unsigned char* without any warning; but in Clang-enhanced C++ compilers this is a simple type mismatch error.

Implicit conversion from int to (classic, not type-safe) enum is allowed in BCC32 with a warning. In Clang-enhanced C++ compilers, implicit conversion is not allowed, and requires a cast. A common case is with enums like TColor and TCursor:

TColor stones = (TColor) 0;  // instead of defaulting to random color, paint it black
Note: Vcl.Graphics.hpp does define the TColor clBlack.

Defining Static Members

C++ static data members must be defined in only one place in your application. You should declare them in your class declaration in a header, and then define the data member in a source file (not in a header file).

For example:

foo.h:
struct X { static int static_member; };

foo.cpp:
#include "foo.h"
int X::me;

bar.cpp:
#include "foo.h"
int some_function() { return X::me; }

In this example, foo.cpp has the definition of X::me, and bar.cpp shows a use. The static member is declared in foo.h, but only defined in foo.cpp.

Our 32-bit tools allowed multiple definitions of X::me, in violation of the C++ standard, so you could do the following:

foo.h:
struct X { static int static_member; };
int X::me;

In the case above (erroneous for Clang-enhanced C++ compilers, but allowed by BCC32), you are defining the static data member in the header, which will result in multiple definitions of X::me, one for each location the header is included. Our Clang-enhanced tools do not permit this, and you might see linker errors such as this:

[ilink64 Error] Error: Public symbol 'triplet::sum' defined in both module C:\USERS\WIN764\NODES.O and C:\USERS\WIN764\UNIT1.O

If you get these duplicate symbol definition errors from the linker for static data members, you should look for the erroneous pattern above, and fix your code to meet the 'one definition' requirement for static data members.

Two-Phase Name Lookup in Templates

Name resolution in templates is attempted at two separate times: definition and instantiation. Less-compliant compilers tend to defer resolution until instantiation, sometimes allowing code that does not strictly conform to the standard to compile successfully. BCC32 does not compile a template unless it is instantiated, so template definitions containing even obvious errors will compile if they are never used. Clang-enhanced C++ compilers will report these errors.

Two-phase name lookup has some subtle effects, such as not checking base classes, as detailed in this LLVM blog entry. For this particular problem, the solution is to qualify identifiers to be found in base classes with this->.

Qualified dependent types require typename keyword

What does this template do:

template <class T>
void pointerOrMultiply() {
  T::member *var;
}

The intent might be to create a pointer to a member of the template type, but with some template parameters, this could also be a multiplication, discarding the result. To avoid ambiguity that cannot be resolved until the template is instantiated, the standard requires that qualified types that are dependent on the template parameter must be preceded by the typename keyword, even if that interpretation is the only one that would make sense during instantiation:

template <class T>
void definitelyAPointer() {
  typename T::member *var;
}

Token Pasting

Clang-enhanced C++ compilers enforce the rule that the result of the preprocessor's token pasting operation (with the ## operator) must be a single valid token.

Here is a contrived example. Suppose the codebase defines a function-like macro that conditionally uses a name as-is, or qualifies it in a namespace:

#ifdef KEEP_IT_SIMPLE
  #define qual(C,M) M
#else
  #define qual(C,M) C##::##M
#endif

// ...

void notRecommendedJustAnExample(int counter) {
  qual(std, cout) << counter;
}

BCC32 will compile the latter case, and it will "work as expected". But in fact, it is not officially supported; Clang-enhanced C++ compilers will complain:

pasting formed 'std::', an invalid preprocessing token
pasting formed '::cout', an invalid preprocessing token

Two instances of ## yield two errors. (Note: the order of multiple token pasting operations in a single macro invocation is undefined.) Neither one results in a single token; they both attempt to combine an identifier and a punctuator (the scope resolution operator).

The fix for cases like this is simple: token pasting is not actually necessary. The identifier and operator end up adjacent anyway. Pasting is only required to create a new token (that is then subject to further macro expansion). So the macro could be defined like this:

  #define qual(C,M) C::M

See Also