Main Content

AUTOSAR C++14 Rule A15-1-1

Only instances of types derived from std::exception should be thrown

Since R2020b

Description

Rule Definition

Only instances of types derived from std::exception should be thrown.

Rationale

Raising generic objects as exceptions can make your code difficult to read and reuse. Consider this code where exceptions are raised in two different try-catch blocks.

try{
	//..
	throw 1; // 1 means logic error;
}
catch(...){
	//...
}
//...
try{
	//...
	throw  std::logic_error{"Logic Error"};
}
catch(std::exception& e){
	//..
}
In the first code block, the cause or the meaning of this exception is not clear. An ambiguous exception such as this one can make the code difficult to read and reuse. These type of generic exceptions might also clash with exceptions raised elsewhere in the code, making the exceptions more difficult to handle. This rule states that such generic objects are not acceptable as exception objects.

In the second code block, the meaning and cause of the exception is clearly communicated by raising a specific and unique type of object as an exception. Such throw statements also match standard conventions. Clearly communicating the developer intent, adhering to the standard conventions, and raising unique type of exceptions make the code easy to read, understand, and reuse.

The class std::exception provides a consistent interface to raise unique exceptions corresponding to specific errors. It is standard convention to use this interface for raising exceptions. To make your code readable and reusable, raise objects of specific types that are derived from std::exception as the exception. Generic objects of type std::exception cannot be unique. Such exceptions violate this rule.

Polyspace Implementation

  • Polyspace® flags a throw statement if the type of the raised object is not a class that is publicly derived from std::exception.

  • If the raised object is part of a multiple inheritance hierarchy, then Polyspace flags the object if none of the base classes derive publicly from std::exception or if the base classes do not include std::exception.

  • If you use a throw; statement without an argument in a catch block, Polyspace does not flag the throw; statement.

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

This example shows how Polyspace flags throw statements that raise objects of a noncompliant class hierarchy.

#include <stdexcept>
#include <memory>

class ConventionalException : public std::logic_error{
public:
	using std::logic_error::logic_error;
};
class CustomException {};
class CustomException_derived : public CustomException {};
class PrivateDerived : private std::exception {};
class ProtectedDerived : protected std::exception {};
class MultipleInheritenceCompliant : 
	public ConventionalException, public CustomException {
public:
	MultipleInheritenceCompliant()
	: ConventionalException("Logic Error and Data Error") {
	}
};
class MultipleInheritenceNoncompliant :
	public ProtectedDerived, public CustomException {};

void Foo() {
	throw std::exception();   // Noncompliant 
	throw CustomException(); // Noncompliant
	throw CustomException_derived(); // Noncompliant 
	throw PrivateDerived();       // Noncompliant 
	throw ProtectedDerived();     // Noncompliant 
	throw MultipleInheritenceCompliant(); // Compliant
	throw MultipleInheritenceNoncompliant(); // Noncompliant
	throw ConventionalException{"Logic Error"};//Compliant
	throw std::make_shared<std::exception> // Noncompliant
	(std::logic_error("Logic Error")); 
}

  • Polyspace flags the statement throw std::exception() because it raises a generic std::exception object, which might not be unique in your code.

  • Polyspace flags the statement throw CustomException() because the class CustomException does not derive from std::exception. Polyspace flags the statement throw CustomException_derived() for the same reason.

  • Polyspace flags the statement throw PrivateDerived() because the class PrivateDerived derives from std::exception privately. You cannot catch this exception by using a catch(std::exception& e) block because of the private inheritance between std::exception and PrivateDerived. Polyspace flags the statement throw ProtectedDerived() for the same reason.

  • Polyspace flags the statement throw MultipleInheritenceNoncompliant() because the class MultipleInheritenceNoncompliant does not publicly derive from std::exception.

  • Polyspace flags the statement throw std::make_shared<std::exception>(std::logic_error("Logic Error")) because this statement raises a std::shared_ptr object as an exception which does not derive from std::exception.

  • Polyspace does not flag the statements throw MultipleInheritenceCompliant() and throw ConventionalException{"Logic Error"} because these statements raise objects of classes that are publicly derived from std::exception.

This example shows how Polyspace flags throw statements in a catch block.

#include <memory>
#include <stdexcept>
class MyException : public std::logic_error
{
public:
	using std::logic_error::logic_error;
	// Implementation
};
void catch_and_rethrow() {
	try {
		/* ... */
	}catch (std::exception e) {
		throw; // Compliant
		throw e; // Noncompliant		
	}catch (std::exception& e) {
		throw e; // Noncompliant
		throw;   // Compliant
	}catch (MyException& Error) {
		throw Error; // Compliant
	}
}
  • Empty throw; statements in catch blocks might raise different types of exceptions depending on the run-time context. Because Polyspace analyzes the throw; statements without any run-time context-specific information, it cannot determine what kind of object an empty throw; statement might raise. Polyspace does not flag throw; statements in catch blocks.

  • Polyspace flags the statements throw e because they raise the std::exception object e as an exception. Avoid such throw statements in catch blocks that accept references to or instances of the std::exception class.

  • Polyspace does not flag the statement throw Error because it raises an object of class MyException that derives from std::exception.

Check Information

Group: Exception handling
Category: Advisory, Automated

Version History

Introduced in R2020b