Main Content

AUTOSAR C++14 Rule A15-0-2

At least the basic guarantee for exception safety shall be provided for all operations. In addition, each function may offer either the strong guarantee or the nothrow guarantee

Since R2022a

Description

Rule Definition

At least the basic guarantee for exception safety shall be provided for all operations. In addition, each function may offer either the strong guarantee or the nothrow guarantee.

Rationale

To facilitate recovery from an exception while preserving the invariant of the relevant objects, programs must be designed to have exception safety. The C++ standard allows these levels of exception safety:

  • Basic Exception Safety: This level of exception safety requires that after raising an exception, the basic invariants of all objects are maintained in a valid state and no memory is leaked. If an operation has basic exception safety, then you can destroy or assign to an object after the operations, even if an exception has been raised. The operations in standard library offers at least basic exception safety.

  • Strong Exception Safety: This level of exception safety requires that after raising an exception, the state of the program remains as it was before the exception. Operations with strong exception safety either succeed or exit with an exception and have no effects on the program.

  • nothrow: This level of safety requires that an exception cannot occur in an operation.

Without at least the basic exception safety, exceptions might corrupt the state of a program or create memory leaks. The AUTOSAR C++14 standard requires that a program must offer at least the basic exception safety for all operations, strong exception safety for key operations, and nothrow guarantee for some operations.

Polyspace Implementation

Polyspace® raises a violation of this rule if any of these conditions are true:

  • An exception violates a class invariant: This condition is met if a non-noexcept member function attempts to raise an exception after modifying any of the fields in the class. For instance, Polyspace flags a throw() statement or a new statement if they are used after modifying the internal state of a class.

  • An exception leaves dynamic memory in an invalid state: This condition is met if an exception is raised without deallocating the dynamic memory that is already allocated. For instance, Polyspace flags an exception raising statement if it is outside a try block or inside a catch block and the allocated resources are not deallocated before the statement. Exception raising statements might include:

    • A throw statement

    • Calls to functions containing throw statements

    • Calls to constructors containing throw statements.

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 <cstdint>
#include <cstring>
template<typename T=int>
class myArray
{
public:
	myArray(){/*...*/}
	myArray(const myArray& rhs)
	{
		DeepCopyNC(rhs);   
		DeepCopy(rhs); 
	}
	~myArray()
	{
		delete[] array;
	}
	void DeepCopyNC(const myArray& rhs) // Noncompliant
	{
		if (this != &rhs) {
			delete[] array;
			array = nullptr; 
			len = rhs.len; 

			if (len > 0) {

				array = new T[len]; 
				std::memcpy(array, rhs.array, len * sizeof(T));
			}
		}
	}
	void DeepCopy(const myArray& rhs) // Compliant
	{
		T* eTmp = nullptr;

		if (rhs.len > 0) {
			eTmp = new T[rhs.len]; 
			std::memcpy(eTmp, rhs.array, rhs.len * sizeof(T));
		}

		delete[] array;
		array = eTmp;
		len = rhs.len;
	}

private:
	T* array;
	std::size_t len;
};
extern 	myArray<> c1{};
void foo(){

	myArray<> c2{c1};
	
}

This example shows a generic class template myArray that manages a raw array. The copy constructor of this class shows two implementations of deep copy. In the function DeepCopyNC(), the memory operations are performed after modifying the this->array and this->len fields. If the memory operation exits with an exception, the invariant of the original class is violated. Because the class invariant is violated by an exception, the function DeepCopyNC() does not have basic exception safety. Polyspace flags it. To provide basic exception safety, modify the class invariant only when the memory operation succeeds, as shown in DeepCopy.

#include<cstdlib>
#include<exception>
 class complex_ptr{
	
	complex_ptr(){
		real = (double*)malloc(sizeof(double));
		imag = (double*)malloc(sizeof(double));
		if(real==nullptr || imag==nullptr){
			throw std::exception(); //Noncompliant
		}
	}
	~complex_ptr(){
		free(real);
		free(imag);
	}
	private:
	double* real;
	double* imag;
	
};
class complex_ptr2{
	
	complex_ptr2() {
		real = (double*)malloc(sizeof(double));
		imag = (double*)malloc(sizeof(double));
		if(real==nullptr || imag==nullptr){
			deallocate();
			throw std::exception(); //Compliant
		}
	}
	void deallocate(){
		free(real);
		free(imag);
	}
	~complex_ptr2(){
		deallocate();
	}
	private:
	double* real;
	double* imag;
	
};
void foo(void){
	complex_ptr Z;
	complex_ptr2 X;
	//...
}

In this example, the class complex_ptr is responsible for allocating and deallocating two raw pointers to double. The constructor complex_ptr::complex_ptr() terminates with an exception when a memory allocation operation fails. The class goes out of scope before deallocating the already allocated resources, resulting in a memory leak. Because an exception causes a memory leak, this code does not have basic exception safety. Polyspace flags the throw statement in the constructor.

Similar to complex_ptr, the constructor of class complex_ptr2 raises an exception when a memory allocation operation fails. Before raising the exception, the constructor deallocates the allocated memory by calling deallocate(). This constructor provides basic exception safety and is compliant with this rule.

#include<exception>
extern int rd;

struct magnitude {
	magnitude() {
		//...
		throw std::exception();
	}
};


class complex_num {
public:
	complex_num() {
		theta = new int;    
		r = new magnitude();//Noncompliant
	}
	magnitude* r;
	int* theta;
};

void foo() {
	complex_num t;
}

In this example, the constructor of complex_numconstructs an int and a magnitude object. The constructor of a magnitude object might raise an exception, causing the already allocated theta to leak. Because an exception might lead to memory leak, this code does not have basic exception safety. Polyspace flags the call to magnitude(). To provide basic exception safety, construct the magnitude object within a try-catch block.

Check Information

Group: Exception handling
Category: Required, Partially automated

Version History

Introduced in R2022a