AUTOSAR C++14 Rule M15-3-1

Исключения должны быть высказаны только после запуска и до окончания

Описание

Определение правила

Исключения должны быть сделаны только после пуска и до окончания работ.

Объяснение

В C++ процесс обработки исключений запускается во время выполнения main(), где исключения, возникающие в различных возможностях, обрабатываются обработчиками исключений в тех же или смежных возможностях. Перед началом выполнения main()компилятор находится в фазе запуска и после завершения выполнения main()компилятор находится в фазе завершения. В течение этих двух фаз компилятор выполняет набор предопределенных операций, но не выполняет никакого кода.

Если исключение возникает во время фазы запуска или фазы завершения, вы не можете записать обработчик исключения, который может выполняться компилятором в этих фазах. Например, можно реализовать main() как function-try-catch блок для обработки исключений. The catch блоки в main() может обрабатывать только исключения, вызванные в main(). Ни одно из catch блоки могут обрабатывать исключения, возникшие во время запуска или фазы завершения. Когда такие исключения возникают, компилятор может ненормально прекратить выполнение кода, не разматывая стек. Рассмотрим этот код, где конструкция и уничтожение статического объекта obj может вызвать исключение.

class A{
	A(){throw(0);}
	~A(){throw(0)}	
};

static A obj;

main(){
	//...
}
Статический объект obj создается путем вызова A() перед main() запускается, и уничтожается вызовом ~A() после main() заканчивается. Когда A() или ~A() вызывает исключение, обработчик исключения не может быть сопоставлен с ними. Исходя из реализации, такое исключение может привести к прекращению работы программы без размотки стека, что приведет к утечке памяти и уязвимостям безопасности.

Избегайте операций, которые могут вызвать исключение в частях вашего кода, которое может быть выполнено до запуска или после завершения программы. Например, избегайте операций, которые могут вызвать исключения в конструкторе и деструкторе статических или глобальных объектов.

Реализация Polyspace

Polyspace® помечает глобальное или статическое объявление переменной, в котором используется вызываемая сущность, которая может вызвать исключение. Для образца:

  • Функция: Когда вы вызываете функцию инициализатора или конструктор непосредственно, чтобы инициализировать глобальную или статическую переменную, Polyspace проверяет, вызывает ли функция исключение, и помечает объявление переменной, если функция может вызвать исключение. Polyspace определяет, может ли функция вызвать исключение независимо от спецификации исключения. Для образца, если a noexcept конструктор поднимает исключение, Polyspace помечает его. Если инициализатор или конструктор вызывает другую функцию, Polyspace предполагает, что вызываемая функция может вызвать исключение, только если это задано как noexcept(<false>). Некоторые стандартные функции библиотеки, такие как конструктор std::string, используйте указатели на функции для выполнения выделения памяти, что может вызвать исключения. Polyspace не помечает объявление переменной, когда используются эти функции.

  • Внешняя функция: Когда вы вызываете внешние функции, чтобы инициализировать глобальную или статическую переменную, Polyspace помечает объявление, если внешняя функция задана как noexcept(<false>).

  • Виртуальная функция: Когда вы вызываете виртуальную функцию, чтобы инициализировать глобальную или статическую переменную, Polyspace помечает ее, если виртуальная функция задана как noexcept(<false>) в любом производном классе. Например, если вы используете функцию виртуального инициализатора, которая объявлена как noexcept(<true>) в базовом классе и noexcept(<false>) в последующем производном классе Polyspace помечает его.

  • Указатели на функцию: Когда вы используете указатель на функцию для инициализации глобальной или статической переменной, Polyspace принимает, что указатель на функцию не вызывает исключений.

Polyspace игнорирует:

  • Исключения, вызванные деструкторами

  • Исключения, высказанные в atexit() операции

Polyspace также игнорирует динамический контекст при проверке на исключения. Например, можно инициализировать глобальную или статическую переменную с помощью функции, которая вызывает исключения только в определенном динамическом контексте. Polyspace помечает такое объявление, даже если исключение может никогда не быть поднято. Обосновать такое нарушение можно при помощи комментариев в Polyspace.

Поиск и устранение проблем

Если вы ожидаете нарушения правил, но не видите его, обратитесь к разделу «Стандартные нарушения кодирования не отображаются».

Примеры

расширить все

В этом примере показано, как Polyspace флаги строит или инициализирует глобальную или статическую переменную, которая может вызвать исключение. Рассмотрим этот код, где статические и глобальные объекты инициализируются с помощью различных вызываемых сущностей.

#include <stdexcept>
#include <string>
class C
{
public:
	C ( ){throw ( 0 );}
	~C ( ){throw ( 0 );}
};
int LibraryFunc();                                    
int LibraryFunc_noexcept_false() noexcept(false);     
int LibraryFunc_noexcept_true() noexcept(true);       
int  g() noexcept {                                   
	throw std::runtime_error("dead code");
	return 0;
}
int f() noexcept {                                    
	return g();                                         
}
int init(int a) {
	if (a>10) {
		throw std::runtime_error("invalid case");
	}
	return a;
}
void* alloc(size_t s) noexcept {          
	return new int[s];
}
int a = LibraryFunc() + 
LibraryFunc_noexcept_true();            // Compliant
int global_int =
LibraryFunc_noexcept_false() +          // Noncompliant
LibraryFunc_noexcept_true();
static C static_c;                      //Noncompliant
static C static_d;                      //Compliant
C &get_static_c(){
	return static_c;
}
C global_c;                             //Noncompliant 
int a3 = f();                           //Compliant
int b3 = g();                           //Noncompliant
int a4 = init(5);                       //Noncompliant
int b5 = init(20);                      //Noncompliant
int* arr = (int*)alloc(5);              //Noncompliant

int main(){
	//...
}

  • Глобальный указатель arr инициализируется при помощи функции alloc(). Потому что alloc() использует new для выделения памяти это может вызвать исключение при инициализации arr во время запуска программы. Polyspace помечает объявление arr и освещает использование new в функциональном alloc().

  • Целочисленная переменная b3 инициализируется вызовом функции g(), который задается как noexcept. Polyspace выводит, что правильная спецификация исключения g() является noexcept(false) потому что он содержит throw() оператор. Инициализация глобальной переменной b3 при помощи g() может вызвать исключение при инициализации arr во время запуска программы. Polyspace помечает объявление b3 и подсвечивает throw оператор в g(). Декларация a3 по вызову f() не помечен. Потому что f() является noexcept функция, которая не выдает, и вызывает другую noexcept function, Polyspace выводит, что f() не вызывает исключение.

  • Глобальные переменные a4 и b5 инициализируются вызовом функции init(). Функция init() может вызвать исключение в определенных случаях, в зависимости от контекста. Поскольку Polyspace выводит спецификацию исключения функции статически, это принимает, что init() может вызвать исключение независимо от контекста. Следовательно, Polyspace помечает объявления обеих a4 и b5хотя init() вызывает исключение только при инициализации b5.

  • Глобальная переменная global_int инициализируется вызовом двух внешних функций. Внешняя функция LibraryFunc_noexcept_false() задается как noexcept(false) и Polyspace принимает, что эта внешняя функция может вызвать исключение. Polyspace помечает объявление global_int. Polyspace не помечает объявление a поскольку он инициализируется путем вызова внешних функций, которые не заданы как noexcept(false).

  • Статическая переменная static_c и нестатическая глобальная переменная global_cобъявляется и инициализируется при помощи конструктора класса C, что может вызвать исключение. Polyspace помечает объявления этих переменных и подсвечивает throw() оператор в конструкторе C классов. Polyspace не помечает объявление неиспользуемой статической переменной static_d, даже если его конструктор может вызвать исключение. Потому что он не используется static_d не инициализирован, и его конструктор не вызывается. Его заявление не вызывает никаких исключений.

Проверяйте информацию

Группа: Обработка исключений
Категория: Необходимый, Автоматизированный