Passing multiple -std switches to g++
Asked Answered
K

1

10

Is it safe to assume that running g++ with

g++ -std=c++98 -std=c++11 ...

will compile using C++11? I haven't found an explicit confirmation in the documentation, but I see the -O flags behave this way.

Kalfas answered 12/11, 2016 at 13:20 Comment(0)
H
19

The GCC manual doesn't state that the last of any mutually exclusive -std=... options specified takes effect. The first occurrence or the last occurrence are the only alternatives. There are numerous GCC flags that take mutually exclusive alternative values from a finite set - mutually exclusive, at least modulo the language of a translation unit. Let's call them mutex options for short. It is a seemingly random rarity for it to be documented that the last setting takes effect. It is documented for the -O options as you've noted, and in general terms for mutually exclusive warning options, perhaps others. It's never documented that the first of multiple setting takes effect, because it's never true.

The documentation leans - with imperfect consistency - on the historical conventions of command usage in unix-likes OSes. If a command accepts a mutex option then the last occurrence of the option takes effect. If the command were - unusually - to act only on the first occurrence of the option then it would be a bug for the command to accept subsequent occurrences at all: it should give a usage error.

This is custom and practice. The custom facilitates scripting with tools that respect it, e.g. a script can invoke a tool passing a default setting of some mutex option but enable the user to override that setting via a parameter of the script, whose value can simply be appended to the default invocation.

In the absence of official GCC documentation to the effect you want, you might get reassurance by attempting to find any GCC mutex option for which it is not the case that the last occurrence takes effect. Here's one stab:

I'll compile and link this program:

main.cpp

#include <cstdio>

#if __cplusplus >= 201103L 
static const char * str = "C++11";
#else
static const char * str = "Not C++11";
#endif

int main() 
{
    printf("%s\n%d\n",str,str); // Format `%d` for `str` mismatch
    return 0;
} 

with the commandline:

g++ -std=c++98 -std=c++11 -m32 -m64 -O0 -O1 -g3 -g0 \
-Wformat -Wno-format -o wrong -o right main.cpp

which requests contradictory option pairs:

  • -std=c++98 -std=c++11: Conform to C++98. Conform to C++11.
  • -m32 -m64: Produce 32-bit code. Produce 64-bit code.
  • -O0 -O1: Do not optimise at all. Optimize to level 1.
  • -g3 -g0: Emit maximum debugging info. Emit no debugging info.
  • -Wformat -Wno-format. Sanity-check printf arguments. Don't sanity check them.
  • -o wrong -o right. Output program wrong. Output program right

It builds successfully with no diagnostics:

$ echo "[$(g++ -std=c++98 -std=c++11 -m32 -m64 -O0 -O1 -g3 -g0 \
-Wformat -Wno-format -o wrong -o right main.cpp 2>&1)]"
[]

It outputs no program wrong:

$ ./wrong
bash: ./wrong: No such file or directory

It does output a program right:

$ ./right
C++11
-1713064076

which tells us it was compiled to C++11, not C++98.

The bug exposed by the garbage -1713064076 was not diagnosed because -Wno-format, not -Wformat, took effect.

It is a 64-bit, not 32-bit executable:

$ file right
right: ELF 64-bit LSB shared object, x86-64 ...

It was optimized -O1, not -O0, because:

$ "[$(nm -C right | grep str)]"
[]

shows that the local symbol str is not in the symbol table.

And it contains no debugging information:

echo "[$(readelf --debug-dump right)]"
[]

as per -g0, not -g3.

Since GCC is open-source software, another way of resolving doubts about its behaviour that is available to C programmers, at least, is to inspect the relevant source code, available via git source-control at https://github.com/gcc-mirror/gcc.

The relevant source code for your question is in file gcc/gcc/c-family/c-opts.c, function,

/* Handle switch SCODE with argument ARG.  VALUE is true, unless no-
   form of an -f or -W option was given.  Returns false if the switch was
   invalid, true if valid.  Use HANDLERS in recursive handle_option calls.  */
bool
c_common_handle_option (size_t scode, const char *arg, int value,
            int kind, location_t loc,
            const struct cl_option_handlers *handlers);

It is essentially a simple switch ladder over option settings enumerated by scode - which is OPT_std_c__11 for option -std=c++11 - and leaves no doubt that it puts an -std option setting into effect regardless of what setting was in effect previously. You can look at branches other than master (gcc-{5|6|7}-branch) with the same conclusion.

It's not uncommon to find GCC build system scripts that rely on the validity of overriding an option setting by appending a new setting. Legalistically, this is usually counting on undocumented behaviour, but there's a better chance of Russia joining NATO than of GCC ceasing to take the last setting that it parses for a mutex option.

Heterolysis answered 13/5, 2017 at 19:5 Comment(1)
Thanks for the very detailed explanation and examples.Kalfas

© 2022 - 2024 — McMap. All rights reserved.