exponenta event banner

Правило AUTOSAR C++ 14 M15-3-1

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

Описание

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

Исключения возникают только после запуска и до прекращения эксплуатации.

Объяснение

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

Если исключение возникает во время фазы запуска или фазы завершения, нельзя записать обработчик исключений, который компилятор может выполнить на этих фазах. Например, можно внедрить main() в качестве function-try-catch блок для обработки исключений. 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 выводит, может ли функция вызвать исключение независимо от ее спецификации исключения. Например, если 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 функция, 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 не инициализирован, а его конструктор не вызван. Его объявление не вызывает никаких исключений.

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

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