3

C has __STDC__ but there seems to be no standard way of recognizing some extended C++ dialect. Hence for portable code I use

#define __is_extended                                   \
    ((__GNUG__   &&!__STRICT_ANSI__)  ||                \
     (_MSC_VER   && _MSC_EXTENSIONS && __cplusplus)  || \
     (__IBMCPP__ && __EXTENDED__))

This works for gcc, XLC and Visual C++ so far.

We have to test ISO/ANSI conformity idiosyncratically per compiler, right? If so, can you make suggestions for other compilers that have proven to work?

EDIT: Since there was so much discussion about the for and against of such tests, here's a real world example. Say there is some header stuff.h used widely with multiple compilers in multiple projects. stuff.h uses some compiler-specific vsnprintf (not standardized before C++11), some copy_if<> (they somehow missed it in C++98), own mutex guards and what not else. While implementing a clean C++11 variant you wrap the old (but trusted) implementation in some #if __is_extended (better: __is_idosyncratic or !__is_ANSI_C11). The new C++11 goes behind an #else. When a translation unit that still compiles as C++0x or C++98 includes stuff.h nothing changed. No compilation errors, no different behaviors at runtime. The C++11 remains experimental. The code can be safely committed to the main branch, co-workers can study it, learn from it and apply techniques with their components.

Thomas Dickey
  • 51,086
  • 7
  • 70
  • 105
Andreas Spindler
  • 7,568
  • 4
  • 43
  • 34
  • 5
    what do you gain from such a macro? the non-standard-extension do vary from compiler to compiler, so you only know that **some** extensions are activated. – m.s. Jul 19 '15 at 09:54
  • 1
    I don't understand the question. Can you give a concrete example of what you would do with the **result** of `__is_extended`? – Christian Hackl Jul 19 '15 at 10:00
  • Make sure that certain translation units can make no use of **some** language extensions. Enable compile-time assertions like `#if __is_extended #error this is portable code #endif` or maybe macros like `#define __is_ANSI_CPP11 (__cplusplus == 201103L && !__is_extended)`. – Andreas Spindler Jul 19 '15 at 10:01
  • @AndreasSpindler: But how is that better than simply invoking the compiler with strict-conformance flags? For example, `_MSC_EXTENSIONS` will be defined if you use the `/Ze` flag. The solution to this problem is not to use `/Ze` but `/Za`. Why would you first enable extensions in the compiler and then treat your own compiler configuration as an error? – Christian Hackl Jul 19 '15 at 10:07
  • @Christian: Yes, generally you're right. Then there are situations when you want to test translation units, header files, functions or even lines of code if they really conform. For example, in large projects, when you have to deal with lots of (historically grown) code written by many people it helps to have such constraints. Like when (1) switching to C++11 and ensure the things that were "extended C++" before now conform or (2) port code from Windows to UNIX and make it "true C++" first. I don't claim a macro like `__is_extended` is of general use - but during development it can be useful. – Andreas Spindler Jul 19 '15 at 10:21
  • @AndreasSpindler: I understand the problem, but I fail to see how compiling the historically grown code with `/Za` and having your compiler *point precisely* to the non-standard pieces in the code through the resulting error messages does not solve it more elegantly. – Christian Hackl Jul 19 '15 at 10:27
  • Lets put it that way: sometimes you can't change project settings, Makefiles etc. I also understand that `__is_extended` is somehow dirty. `#pragma` is dirty too. But then again, what is dirty in a dirty environment? I am a freelancer for nearly 20 years now. Many companies do not pay money for clean code - they just want functionality. And some people you meet in projects that do C++ do not really understand it. It is still not uncommon to just wrap a bunch of C functions with `struct` and call it a class. – Andreas Spindler Jul 19 '15 at 10:36
  • 2
    Defining the above token makes your program ill-formed under the standard. I also fail to see how you would use it usefully. – Yakk - Adam Nevraumont Jul 19 '15 at 11:12
  • @AndreasSpindler: You will not be able to make `#if __is_extended #error this is portable code #endif` compile without changing project settings. Even if the code inside actually is portable. And it will not help you detect "just wrap a bunch of C functions with struct and call it a class". So, what is the purpose? – Ben Voigt Jul 19 '15 at 14:48
  • It's just a macro and as such distinct from project settings. Unknown macros are expand 0 by default. Regarding the purpose: I have added an example to my original post. However, I agree that using a macro like `__is_extended` in fresh project would be weird, which is probably why the C++ standard doesn't have `__STDCPLUSPLUS__`. – Andreas Spindler Jul 19 '15 at 15:19
  • @Andreas, but it does have `__cplusplus`! – rubenvb Jun 03 '16 at 17:30

3 Answers3

3

Your question is actually backward, because the non-standard extensions supported by a compiler are specific to that compiler - often to the extent of being specific to a particular compiler version - as are the non-standard macros each compiler defines so they can be detected.

The usual technique is the reverse: specify some feature you want, associate it with some macro, and only write code which uses that feature if the associated macro is defined.

Let's say there is some funky feature that is supported - in exactly the same way by Visual C++ 11 and g++ version 3.2.1, but not with any other compilers (not even other versions of Visual C++ or g++).

//  in some header that detects if the compiler supports all sorts of features    

#if ((defined(__GNUG__) && __GNUC__ == 3 && __GNUC_MINOR__ == 2 && __GNUC_PATCHLEVEL__ == 1) || (defined(_MSC_VER) && _MSC_VER == 1700))

#define FUNKY_FEATURE

#endif

// and, in subsequent user code ....

#ifdef FUNKY_FEATURE

  // code which uses that funky feature

 #endif

There are plenty of freely available general purpose libraries which use this sort of technique (obviously with better naming of macros). One example that comes to mind is the ACE (Adaptive Communication Environment) framework which has a set of portability macros, documented here.

Using such macros is not a job for the faint-hearted if you are concerned about a large set of non-standard features, given that it is necessary to understand what versions of what compilers (or libraries) support each feature, and to update the macros every time a new compiler, a new library, or even a patch is released.

It is also necessary to avoid using reserved identifiers in naming those macros, and to ensure the macro names are unique. Identifiers starting with a double underscore are reserved.

Peter
  • 35,646
  • 4
  • 32
  • 74
1

In general this will be hard to do because if you're relying on a non-conformant compiler then there's no standardized way to require only standard rules (the behavior of a non-standard compiler is not specified by the standard).

What you could do is adding an extra build step or commit hook and pass the code ALSO through a specific portable compiler (like g++) with specific strict conformancy options.

6502
  • 112,025
  • 15
  • 165
  • 265
  • Yes, this works for whole translation units. But you've to alter the build system. If `__is_extended` checks become permanent I would prefer that too. But sometimes it is much faster to use the preprocessor. – Andreas Spindler Jul 19 '15 at 10:49
0

First, you are not allowed to name variables in such way (#define __is_extended) because names beginning with two underscores are reserved for the implementation.

The method you have is still compiler-dependent and could fail: apart from __cplusplus, none of those macros are standard and therefore the implementation is not required to define them. Moreover, that test is basically checking what compiler is being used not whether or not some extensions are being used.

My advice is simply not to use extensions. There's very very little need for them. If you still want to make sure they don't get used anyway, you may turn your compiler's flags to restrict the usage of extensions; for GCC, there's a whole chapter about this in the section "Options Controlling C Dialect".

edmz
  • 8,220
  • 2
  • 26
  • 45
  • (1) `_MSC_EXTENSIONS` and `__STRICT_ANSI__` and `__EXTENDED__` really are only defined if some extensions are used. (2) Yes - since C++11 there are less uses for extensions. But there is many C++98 code out there that will be migrated slowly. (3) Even `gcc -Wall -Wextra -pedantic` does not complain about names with underscores so I guess we're on safe ground :-) – Andreas Spindler Jul 19 '15 at 10:26
  • @AndreasSpindler 1) They're not required to be defined. Also, for gcc/clang a sample code with extensions doesn't define them. `__STRICT_ANSI__` just exists if `-ansi` or `-std` is specified. 3) It's UB. No diagnostic is required and the compiler can do whatever it wants to treat it. See [this](https://stackoverflow.com/questions/228783/what-are-the-rules-about-using-an-underscore-in-a-c-identifier). – edmz Jul 19 '15 at 10:45
  • I don't understand what you mean with "they're not required to defined". As long as the manual says that a specific macro is defined under certain compiler options we can use it, right? For example, `__STRICT_ANSI__` is defined when `-ansi`, `-std=c++98`, `-std=c++11` are used, but not with `-std=gnu++11`. This works well. Can be test quickly with something like `touch empty.cpp; gcc -std=c++11 -dM -E empty.cpp | grep __STRICT`. – Andreas Spindler Jul 19 '15 at 11:00
  • @AndreasSpindler Yes you can. But, is it _guaranteed_ to exist tomorrow? No. Is it guaranteed to be defined on another implementation? No. Is it required to be meaningful? No. And so on. You get the idea. – edmz Jul 19 '15 at 11:07
  • ...even tomorrow is not guaranteed to exist tomorrow ;-) Anything will change sooner or later. I found that compiler macros do not change so often. It is more likely that your build system breaks or some runtime component (database, servers) change behaviors. – Andreas Spindler Jul 19 '15 at 11:17
  • @AndreasSpindler: "`_MSC_EXTENSIONS` and `__EXTENDED__` really are only defined if some extensions are used." No. They are defined if extensions are enabled. They tell you nothing about whether your code is using them. – Ben Voigt Jul 19 '15 at 14:50
  • Sorry, my English was ambiguous . "used" actually meant using compiler switches. – Andreas Spindler Jul 19 '15 at 15:31