Main Content

AUTOSAR C++14 Rule A15-1-5

Exceptions shall not be thrown across execution boundaries

Since R2022b

Description

Rule Definition

Exceptions shall not be thrown across execution boundaries.

Rationale

An execution boundary separates code in your projects that are compiled by different compilers or different versions of a compiler. For example, you might use different compilers to compile your code and a library function. In this case, the execution boundary exists between the call site in your executable and the function implementation in the library.

Exception handling requires interoperability between the functions that raise the exception and the functions that handle the raised exceptions. Code on two sides of an execution boundary might implement exception handling by using incompatible interfaces. For instance:

// lib.h
void foo() noexcept(false);
//lib.cpp
void foo() noexcept(false){
  //...
  throw -1;
}
//App.cpp
#include"lib.h"
int main(){
  try{
    foo();
  }catch(int& e){
    //handle exception
  }
}
If you compile the library file lib.cpp by using GCC and compile the application App.cpp by using Microsoft Visual Studio, then these two portions of your code use incompatible exception-handling interfaces. The exception arising from the library is not caught, which terminates the application unexpectedly.

Avoid raising exceptions in code that you intend to reuse as a library in different projects. Instead of raising exceptions, return different error codes to handle unexpected situations.

As an exception, raising exceptions across an execution boundary is compliant with this rule if the codes on the two sides of the boundary are guaranteed to use the same exception handling interface

Polyspace Implementation

Polyspace® raises a violation of this rule if a library interface function raises an exception. Violations are not raised if a new operator in a such a function raises an std::bad_alloc exception.

To use this checker correctly, you must specify those functions in your code that are part of a library interface. Declare a function foo() as a library interface function by setting their visibility attribute. For instance:

  • GNU and Clang compiler: void __attribute__((visibility("default"))) foo(){/*...*/}

  • Visual Studio: void __declspec(dllexport) foo(){/**.../}

If you do not explicitly specify a function as visible, Polyspace assumes that it is not part of a library interface.

Troubleshooting

If you expect a rule violation but Polyspace does not report it, see Diagnose Why Coding Standard Violations Do Not Appear as Expected.

Examples

expand all

#include<exception>
int FLAG;
void __attribute__((visibility("default"))) foo(int rd) noexcept(false) {//Noncompliant 
    if (rd==-1)
        throw std::exception();
}

int __attribute__((visibility("default"))) bar(bool rd) noexcept(true) { 
    if (rd==-1){
		FLAG = 52;
		return -1;
	}
}

In this example, the visibility attribute of the function foo() is set to default, which indicates that this function belongs in a library interface. Because foo() might be used in projects that have different exception handling mechanisms, exceptions raised by foo() might remain unhandled. Avoid raising exceptions from library interface functions. Instead, use other methods for handling errors, as shown in bar. The library function bar sets an error flag and returns an error code when a logic error occurs in the code.

Version History

Introduced in R2022b