Pour obtenir une clé
d'essai remplissez le formulaire ci-dessous
Demandez des tariffs
Nouvelle licence
Renouvellement de licence
--Sélectionnez la devise--
USD
EUR
RUB
* En cliquant sur ce bouton, vous acceptez notre politique de confidentialité

Free PVS-Studio license for Microsoft MVP specialists
To get the licence for your open-source project, please fill out this form
** En cliquant sur ce bouton, vous acceptez notre politique de confidentialité.

I am interested to try it on the platforms:
** En cliquant sur ce bouton, vous acceptez notre politique de confidentialité.

Votre message a été envoyé.

Nous vous répondrons à


Si vous n'avez toujours pas reçu de réponse, vérifiez votre dossier
Spam/Junk et cliquez sur le bouton "Not Spam".
De cette façon, vous ne manquerez la réponse de notre équipe.

>
>
>
V1090. The 'std::uncaught_exception' fu…
Analyzer diagnostics
General Analysis (C++)
General Analysis (C#)
General Analysis (Java)
Diagnosis of micro-optimizations (C++)
Diagnosis of 64-bit errors (Viva64, C++)
Customer specific requests (C++)
MISRA errors
AUTOSAR errors
OWASP errors (C#)
Problems related to code analyzer
Additional information
Contents

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'.

07 Sep 2022

The analyzer detected the 'std::uncaught_exception' function call. The use of this function may lead to incorrect program logic. Since C++17, this function has been deprecated and should be replaced with the 'std::uncaught_exceptions' function.

The 'std::uncaught_exception' function is usually used to understand whether the code is called when the stack unwinding takes place. Let's look at the following example:

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 (...)
  {
    // ....
  }
}

Inside the 'Logger' class destructor, the 'std::filesystem::create_symlink' function is called. This function may throw an exception, for example, if a program doesn't have permissions to use the 'system/logs/log.txt' path. If the 'Logger' destructor is called directly as a result of the stack unwinding, then, it is impossible to throw exceptions from this destructor — the program will be aborted via 'std::terminate'. Therefore, before the function is called, a developer makes an extra check - 'if (!std::uncaught_exception())'.

However, such code contains an error. Suppose the 'Calc' function throws an exception. Then, before the catch-clause is executed, the 'Calculator' destructor will be called. An instance of the 'Logger' class will be created inside this call, and the message will be written to the log. After that, the 'Logger' destructor will be called. Then, the 'std::uncaught_exception' function will be called inside the destructor. This function will return 'true' because the exception thrown by the 'Calc' function has not been caught yet. Therefore, a symbolic link to the log file will not be created.

However, in this case, you can try to create the symbolic link. The fact is that the 'Logger' destructor will not be called directly as a result of the stack unwinding — it will be called from the 'Calculator' destructor. Therefore, you can throw an exception from the 'Logger' destructor — you only need to catch this exception before it exits from the 'Calculator' destructor.

To fix this, you need to use the 'std::uncaught_exceptions' function from C++17:

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);
    }
  }
};

Now, when you create the 'Logger' instance, the current number of uncaught exceptions will be saved in the 'm_exceptions' field. If no new exceptions were thrown between creating the object and calling its destructor, the condition will be true. Therefore, the program will try to create the symbolic link to the log file. If an exception is thrown, it will be caught and processed in the 'Calculator' destructor, and the program will continue execution.

This diagnostic is classified as:

Unicorn with delicious cookie
Nous utilisons des cookies pour améliorer votre expérience de navigation. En savoir plus
Accepter