To get a trial key
fill out the form below
Team license
Enterprise license
** By clicking this button you agree to our Privacy Policy statement

Request our prices
New License
License Renewal
--Select currency--
USD
EUR
RUB
* By clicking this button you agree to our Privacy Policy statement

Free PVS-Studio license for Microsoft MVP specialists
** By clicking this button you agree to our Privacy Policy statement

To get the licence for your open-source project, please fill out this form
** By clicking this button you agree to our Privacy Policy statement

I am interested to try it on the platforms:
** By clicking this button you agree to our Privacy Policy statement

Message submitted.

Your message has been sent. We will email you at


If you haven't received our response, please do the following:
check your Spam/Junk folder and click the "Not Spam" button for our message.
This way, you won't miss messages from our team in the future.

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

Sep 07 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
Our website uses cookies to enhance your browsing experience. Would you like to learn more?
Accept