3

This code snippet below does compiles,

#include<sys/types.h>
#include<sys/wait.h>
#include<iostream>

int main()
{
    int ret = 0xFFFF;
    std::cout << WEXITSTATUS(ret);
}

whereas this code snippet does not compile indeed with G++ 4.9.4:

#include<sys/types.h>
#include<sys/wait.h>
#include<iostream>

int main()
{
     std::cout << WEXITSTATUS(0xFFFF);
}

Here is what the compiler complains:

In file included from /usr/include/x86_64-linux-gnu/sys/wait.h:77:0,
                 from t.cpp:2:
t.cpp: In function ‘int main()’:
t.cpp:7:22: error: lvalue required as unary ‘&’ operand
         std::cout << WEXITSTATUS(0xFFFF);
                      ^

Here is the detail info about the compiler:

g++ --version
g++ (Ubuntu 4.9.4-2ubuntu1~16.04) 4.9.4
Copyright (C) 2015 Free Software Foundation, Inc.
This is free software; see the source for copying conditions.  There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

And the compiler is installed on Ubuntu16.04 by the commands below

sudo apt-get install gcc-4.9
sudo apt-get install g++-4.9
sudo update-alterntives --install /usr/bin/gcc gcc /usr/bin/gcc-4.9 20
sudo update-alterntives --install /usr/bin/g++ g++ /usr/bin/g++-4.9 20

Note: I have to use g++-4.9, I have no other choice.

And It's strange that I could not reproduce the said phenomenon on godbolt.org. It compiles on godbolt.org with gcc 4.9.3(gcc 4.9.4 is not available).

Here is the output of g++ -E the_said_code_snippet_does_not_compile.cpp

//omit
# 4 "t.cpp" 2

int main()
{
        std::cout << ((((*(const int *) &(0xFFFF))) & 0xff00) >> 8);
}

Could anybody shed some light on this matter?

UPDATED:

I can reproduce the error now!See this link.

UPDATED:

It's just a simplified example. What am I actually face is WEXITSTATUS(pclose(fp)) does not compile.

John
  • 2,963
  • 11
  • 33
  • Please try `g++ -E t.cpp` and post the expanded body of `int main`. – 273K May 26 '22 at 06:58
  • Very strange taking address, casting, dereferencing. It looks like some kind of volatile access. – 273K May 26 '22 at 07:06
  • @273K It seems there is no need to acquire the address indeed. Just define WEXITSTATUS(status) as (((status &0xFFFF) & 0xff00) >> 8) is ok. How do you think about it? – John May 26 '22 at 07:11
  • I would look at the update history of glibc sys/wait.h. – 273K May 26 '22 at 07:13
  • @273K OK. And I can reproduce the error now!See this [link](https://coliru.stacked-crooked.com/a/174ac9c00d9117a0). – John May 26 '22 at 07:17
  • Why do you want to pass a literal in the first place? It doesn't seem very useful. – molbdnilo May 26 '22 at 07:30
  • @molbdnilo It's just a simplified example. What am I actually face is `WEXITSTATUS(pclose(fp))` does not compile. – John May 26 '22 at 07:33
  • this solution works fine. https://stackoverflow.com/a/13674801/7007942. just assign pclose to a int. – Kevin Chan Jul 29 '22 at 02:52

2 Answers2

2

The WEXITSTATUS macro is a matter of the C standard library implementation, not the compiler per se. Typically (and in the case of GCC) the compiler doesn't supply the C standard library implementation. It is an independent package.

Most Linux distributions, including Ubuntu, use glibc as C standard library implementation.

In glibc until version 2.23, inclusive, the macro was defined in the following way when using C++ and __USE_MISC is set (see commit link below):

#   define __WAIT_INT(status)   (*(const int *) &(status))

// ...

# define WEXITSTATUS(status)    __WEXITSTATUS (__WAIT_INT (status))

The actual implementation of the macro is inside __WEXITSTATUS, but the use of __WAIT_INT seems to be for the purpose of supporting the non-POSIX "union wait" variant of the wait interface. With this definition, a prvalue cannot be used with the macro, because it tries to take the address of status.

In 2016, with commit b49ab5f4503f36dcbf43f821f817da66b2931fe6 support for union wait - according to the NEWS entry deprecated in the early 1990s - has been removed and now the definition is simply

# define WEXITSTATUS(status)    __WEXITSTATUS (status)

Now it would work with a prvalue as well.

It seems that Ubuntu 16.04 still uses a glibc version from before that change, which isn't surprising since it was released at the time of the commit.

I don't know what POSIX has to say about whether or not it should be possible to use the macro with a int rvalue rather than the name of a variable.

That WEXITSTATUS can't always be used directly on a call to pclose seems to be known issue. Apparently the above-mentioned extension, which is now not present in glibc anymore, was (is?) also present in (some?) BSDs (and may originate from them?). See e.g. this question, in which the answer also expresses doubts about POSIX-compliance. However, OpenBSD, mentioned in the linked question, also removed union wait in 2014. According to the changelog it had been deprecated since 4.3BSD (released 1986).

user17732522
  • 53,019
  • 2
  • 56
  • 105
  • user17732522 *Typically (and in the case of GCC) the compiler doesn't supply the C standard library implementation. It is an **independent package.*** It seems that I don't have to manually installed the said package when installing gcc4.9.4 with `apt-get install...`. Do you think I need to manually install it? – John May 26 '22 at 07:24
  • @John It is always installed in every installation because it is so fundamental to the system. Essentially every program will use it. – user17732522 May 26 '22 at 07:25
  • Do you mean the aforementioned package is installed even if there no `Gcc`\`G++` is installed on `Ubuntu`? Am I right? – John May 26 '22 at 07:27
  • @John Yes. There might be a difference between a `glibc` package and a `glibc-dev` package. But I am not sure at the moment how it is in Ubuntu. Every linux distribution handles this slightly differently. – user17732522 May 26 '22 at 07:28
  • And the said package would not be updated when you install or update the gcc\g++.Am I right? – John May 26 '22 at 07:31
  • 1
    @John Again because the package is so fundamental to the system, usually no major upgrades are made to it in a given release of the linux distribution, to avoid introducing incompatibilities. Typically only bug fixes are applied. Again, that is a matter of distribution policy and I don't know how exactly Ubuntu handles it, but you will probably need to move to a newer Ubuntu release to get a newer glibc version. – user17732522 May 26 '22 at 07:35
0

That looks like an oddball implementation of macro, causing problems with operator precedence. Usually it should be something like this:

#define     WEXITSTATUS(status)        (((status) & 0xff00) >> 8)

now if it is (and completely wrong as result)

#define     WEXITSTATUS(status)        status & 0xff00 >> 8

You wold get following order:

( std::cout << status ) & (0xff00 >> 8)

Though I can't imagine that such broken implementation could exist?

The processed code snippet shows that macro assumes status to be an expression returning at least a prvalue, something one can get address of and "magic" of casting it to const int.

#define     WEXITSTATUS(status)        ((((*(const int *) &(status))) & 0xff00) >> 8)

The error happens from address operator getting a wrong operand. Essentially this macro cannot work with a constant only with expression or variable, assuming that those are values returned from particular source described in documentation.

Swift - Friday Pie
  • 12,777
  • 2
  • 19
  • 42
  • If it was defined so, then the first code snipped wouldn't compile. – 273K May 26 '22 at 07:04
  • It seems there is no need to acquire the address indeed. Just define `WEXITSTATUS(status) ` as `(((status &0xFFFF) & 0xff00) >> 8)` is ok. How do you think about it? – John May 26 '22 at 07:11
  • @John I took first definition from existing sources (unsure how contemporary). THe shown definition is some kind of pre-C11 weirdness in accessing value which could be changed during execution, e.g. a volatile. – Swift - Friday Pie May 26 '22 at 07:14
  • @john and yes, I know it doesn't actually get high-order bits that way. There was this: https://stackoverflow.com/questions/63803462/why-is-the-macro-like-wexitstatusstatus-implemented-in-this-way – Swift - Friday Pie May 26 '22 at 07:17
  • @Swift-FridayPie Thank you for the clarification. But I really can't see any relation between what you said and the link, and I can't see the link helps my question. – John May 26 '22 at 07:39
  • 1
    @john the gist of it is that you were not supposed to use the macro how you did in second example. The fact it's not reproducible in other\later distros is that the `wait.h` had changed since then. `wait.h` is part of platform and godbolt is running on a modern one. – Swift - Friday Pie May 26 '22 at 08:55