﻿# V1090\. The 'std::uncaught\_exception' function is deprecated since C\+\+17 and is removed in C\+\+20\. Consider replacing this function with 'std::uncaught\_exceptions'\.

Анализатор обнаружил вызов функции 'std::uncaught\_exception'\. Применение этой функции может привести к неверной логике программы\. Начиная с C\+\+17, она признана устаревшей и должна быть заменена на функцию 'std::uncaught\_exceptions'\.

Функция '[std::uncaught\_exception](https://en.cppreference.com/w/cpp/error/uncaught_exception)' обычно применяется для того, чтобы понять, вызывается ли код при раскрутке стека\. Рассмотрим пример:

```cpp
constexpr std::string_view defaultSymlinkPath = "system/logs/log.txt";

class Logger
{
  std::string   m_fileName;
  std::ofstream m_fileStream;

  Logger(const char *filename)
    : m_fileName { filename }
    , m_fileStream { m_fileName }
  {
  }

  void Log(std::string_view);

  ~Logger()
  {
    fileStream.close();
    if (!std::uncaught_exception())
    {
      std::filesystem::create_symlink(m_fileName, defaultSymlinkPath);
    }
  }
};

class Calculator
{
public:
  int64_t Calc(const std::vector<std::string> &params);
  // ....
  ~Calculator()
  {
    try
    {
      Logger logger("log.txt");
      Logger.Log("Calculator destroyed");
    }
    catch (...)
    {
      // ....
    }
  }
}

int64_t Process(const std::vector<std::string> &params)
{
  try
  {
    Calculator calculator;
    return Calculator.Calc(params);
  }
  catch (...)
  {
    // ....
  }
}
```

В деструкторе класса 'Logger' вызывается функция 'std::filesystem::create\_symlink', которая может бросить [исключение](https://en.cppreference.com/w/cpp/filesystem/create_symlink#Exceptions)\. Например, если для использования пути 'system/logs/log\.txt' у программы недостаточно прав\. Если деструктор 'Logger' будет вызван напрямую в результате раскрутки стека, то бросать исключения из этого деструктора [нельзя](https://en.cppreference.com/w/cpp/language/throw#Stack_unwinding) – программа будет аварийно прервана через 'std::terminate'\. Поэтому перед вызовом функции программист сделал дополнительную проверку 'if \(\!std::uncaught\_exception\(\)\)'\.

Однако такой код содержит ошибку\. Предположим, что функция 'Calc' бросила исключение\. Тогда перед выполнением catch\-clause произойдёт вызов деструктора 'Calculator'\. В нём будет создан экземпляр класса 'Logger', в лог запишется сообщение\. Затем будет вызван деструктор 'Logger'\. Внутри него произойдёт вызов функции 'std::uncaught\_exception'\. Эта функция вернёт 'true', потому что исключение, брошенное функцией 'Calc', ещё не перехвачено\. Поэтому символическая ссылка для файла с логом не будет создана\.

Однако в данном случае можно попробовать создать символическую ссылку\. Дело в том, что деструктор 'Logger' будет вызван не напрямую в результате раскрутки стека, а из деструктора 'Calculator'\. Поэтому из деструктора 'Logger' можно бросить исключение — нужно только перехватить его до выхода из деструктора 'Calculator'\.

Для исправления необходимо воспользоваться функцией 'std::uncaught\_exceptions' из C\+\+17:

```cpp
class Logger
{
  std::string   m_fileName;
  std::ofstream m_fileStream;
  int           m_exceptions = std::uncaught_exceptions(); // <=

  Logger(const char *filename)
    : m_fileName { filename }
    , m_fileStream { m_fileName }
  {
  }

  ~Logger()
  {
    fileStream.close();
    if (m_exceptions == std::uncaught_exceptions())
    {
      std::filesystem::create_symlink(m_fileName, defaultSymlinkPath);
    }
  }
};
```

Теперь при создании объекта класса 'Logger' в поле 'm\_exceptions' сохранится текущее количество неперехваченных исключений\. Если между созданием объекта и вызовом его деструктора не было брошено новых исключений, то условие будет истинным\. Поэтому программа попробует создать символическую ссылку для файла с логом\. Если при этом будет брошено исключение, то оно будет перехвачено и обработано в деструкторе 'Calculator', и программа продолжит выполнение\.