Relying on Implementation Documentation
The essential question here is whether we can rely on specifications provided by a C or C++ implementation. (Since we are dealing with a situation with mixed C and C++ code, I will refer to this combined implementation as a single implementation.)
In fact, we must rely on implementation documentation. The C and C++ standards do not apply unless and until an implementation asserts that it conforms (at least in part) to the standards. The standards have no power of law; they do not apply to any person or undertaking until somebody decides to adopt them. (The C 2018 Foreword refers to an ISO statement explaining the standards are voluntary.)
If an implementation tells you it conforms to the C and C++ standards, and it also tells you it supports throwing C++ exceptions through C code, there is no reason to believe one and not the other. If you accept the implementation’s documentation, then it both conforms to the language standard and supports throwing exceptions through C code. If you do not accept the implementation’s documentation, then there is no reason to expect conformance to the language standards. (This is a general view, neglecting instances where apparent bugs give us reason to doubt specific behaviors, for example.)
If you ask whether passing an exception through C code is “undefined” in the sense used in the C or C++ standards, the answer is yes. But those standards are only discussing what they define. Their use of “undefined” does not prohibit anybody else from defining behavior. In fact, if you are using an implementation’s documentation, you have a definition for the behavior. The C and C++ standards do not undo, negate, or nullify definitions made by other documents:
- Where the C or C++ standard says any behavior is undefined that only means the behavior is undefined within the context of the C or C++ standard.
- Any other specification a programmer chooses to use may define additional behavior that is not defined by the C or C++ standard. The C and C++ standards do not prohibit this.
Example
As an example, some of the documents one might rely on to specify the behavior of a commercial software product include:
- The C standard.
- The C++ standard.
- The assembler manual.
- The compiler documentation.
- Apple’s Developer Tools documentation, include behaviors of Xcode, the linker, and other tools used during a software build.
- Processor manuals.
- Instruction set architecture specifications.
- IEEE-754 Standard for Floating Point Arithmetic.
- Unix documentation for command-line tools.
- Unix documentation for system interfaces.
For much software, it would be impossible to produce the software if the overall behavior were not defined by all these specifications combined. The notion that the C or C++ standard overrides or trumps other documentation is ludicrous.
Writing Portable Code
Any software project, or any engineering project, works from premises: It takes as given various tool specifications, material properties, device properties, and so on, and it derives desired products from those premises. Rarely does any complete end-user commercial product rely solely on the C or C++ standard. When you buy an iPhone, it obeys the laws of physics, and you are entitled to rely on it to conform to safety specifications for electrical devices and to radio frequency behaviors regulated by governmental agencies. It conforms to many specifications, and the notion that the C standard should be regarding as trumping those other specifications is absurd. If your device burst into flame because of a programming error that the C standard says has undefined behavior, that is not acceptable—the fact the C standard says it is not defined does not trump the safety specification.
Even in purely software projects, very few strictly conform to the C or C++ standards. Largely, only software that does some pure computations and limited input/output can be written in strictly conforming C or C++. That can include very useful libraries that are included in other software, but it includes very few complete commercial end-user programs—such as a few things used by mathematicians and scientists to answer questions about logic, math, and modeling, for example. Most software in this world interacts with devices and operating systems in ways not defined by the C or C++ standards. Most software uses extensions not defined by the standards—extensions that manipulate files and memory in ways not defined by the standards, that interact with devices and users in ways not defined by the standards. They display GUI windows and accept mouse and keyboard input from the user. They transmit and receive data over a network. They send radio waves to other devices.
These things are impossible without using behaviors not defined by the language standards. And, if the language standards trumped the definitions of these behaviors, writing such software would be impossible. If you wanted to send a Wi-Fi radio signal, and you had adopted the C standard, and the C standard trumped other definitions, that would mean it would be impossible for you to write software that reliable sends a radio signal. Obviously, that is not true. The C standard does not trump other specifications.
Writing “portable code” is not a feasible requirement for most software projects. It is, of course, desirable to contain non-portable code to clear interfaces. It is desirable to write what code one can using portable code so that it can be reused. But this is only part of most projects. For most projects, the project as a whole must use behaviors defined by documents other than the language standards.