Lanzar excepciones con mensaje en C++

Jay Shaw 12 octubre 2023
  1. Lanzar excepciones con mensaje usando la excepción estándar de C++ - Argumento no válido
  2. Lanzar excepciones con mensaje usando la excepción estándar de C++ - Excepción anidada
  3. Use el error de tiempo de ejecución para lanzar excepciones con el mensaje en C++
  4. Conclusión
Lanzar excepciones con mensaje en C++

Este artículo explicará cómo lanzar excepciones con un mensaje variable en C++. El lanzamiento de una excepción es el proceso de cambiar el control del programa para evitar bloqueos o desbordamientos.

Se ejecuta colocando un lanzamiento de excepción dentro del programa donde podría ocurrir un problema. Hay varias palabras clave de manejo de excepciones en C++, pero este artículo verá cómo generar excepciones con un mensaje variable.

Lanzar excepciones con mensaje usando la excepción estándar de C++ - Argumento no válido

Este programa utiliza una excepción de argumento no válido de las excepciones estándar de C++ para lanzar excepciones con un mensaje variable.

Importar paquetes

El programa utiliza dos paquetes de importación, stdexcept para incluir excepciones estándar de C++ y iostream para operaciones de entrada y salida.

Método para lanzar una excepción

Este programa definió una función para comparar dos números proporcionados como entrada y arroja un caso de excepción cuando se encuentra una entrada negativa. Aquí se utiliza la excepción estándar std::invalid_argument.

En este programa se define un método comparar con dos valores enteros, var_a y var_b, como parámetros. Esto usa si para verificar un número negativo (a, b o ambos) y arroja una excepción si es así.

if (var_a < 0 || var_b < 0) {
  throw std::invalid_argument("Negative value encountered");

Método principal

El método main utiliza las palabras clave de excepción try-catch para generar excepciones válidas.

El manejo de excepciones se ejecuta llamando al método comparar dentro de los bloques intentar. Como las entradas (-1,3) tienen un número negativo, el bloque try enviará el argumento al bloque catch, que lanza excepciones con un mensaje variable e imprime el mensaje.

#include <iostream>
#include <stdexcept>

using namespace std;

int check_neg(int var_a, int var_b) {
  if (var_a < 0 || var_b < 0) {
    throw std::invalid_argument("Negative value encountered");
  }
  return 0;
}

int main() {
  try {
    compare(-1, 3);
  } catch (const std::invalid_argument& e) {
    std::cout << "booh!";
  }
}

Producción :

booh!
--------------------------------
Process exited after 0.006709 seconds with return value 0
Press any key to continue . . .

Lanzar excepciones con mensaje usando la excepción estándar de C++ - Excepción anidada

Las excepciones se pueden anidar entre sí para mostrar varios mensajes de lanzamientos de excepción de forma consecutiva. El programa de esta sección envuelve los mensajes de error en excepciones anidadas, luego lanza excepciones con un mensaje variable en cada ocurrencia y las muestra juntas.

Importar paquetes

Los paquetes de importación utilizados en este programa son:

  • iostream
  • stdexcept
  • exception
  • string
  • fstream

Métodos de miembros

El programa tiene tres funciones miembro: print_exception, open_file y run.

Método print_exception:

Tiene dos parámetros públicos: std::exception& ex para detectar excepciones std y una variable entera stage que se inicializa con el valor cero. Dentro del método, la primera línea std::cerr se usa para imprimir un mensaje de error que es Excepción encontrada:, pero se coloca en niveles de anidamiento usando std::string(level,'').

El mensaje de error va seguido de ex.what, donde ex es un objeto de std::exception y ex.what devolverá una cadena explicativa sobre el error que ocurrió.

El bloque try usa std::rethrow_if_nested(e) para lanzar una excepción, pero solo en el caso de que el objeto std ex se derive de una excepción anidada, lo que ocurre en este programa.

El bloque catch utiliza un objeto std nestedException para volver a llamarse a sí mismo recursivamente mientras aumenta el valor de stage en 1. El método catch(...) captura todas las excepciones.

void print_exception(const std::exception& ex, int level = 0) {
  std::cerr << std::string(level, ' ') << "Encountered Exception: " << ex.what()
            << '\n';
  try {
    std::rethrow_if_nested(ex);
  } catch (const std::exception& nestedException) {
    print_exception(nestedException, stage + 1);
  } catch (...) {
  }
}

Lo que sucede aquí en el código anterior es que intenta imprimir la cadena explicativa de la excepción. Si la función la encuentra anidada, recurre para imprimir el explicativo de la excepción que contiene.

Método open_archivo:

La función tiene un único parámetro de cadena std::string& s, que crea un objeto std::string y le transfiere un alias s.

El bloque try-catch intenta leer un archivo, detecta una excepción cuando el archivo no se puede cargar y lo envuelve en una función anidada.

void open_file(const std::string& s) {
  try {
    std::ifstream file(s);
    file.exceptions(std::ios_base::failbit);
  } catch (...) {
    std::throw_with_nested(std::runtime_error("Couldn't open " + s));
  }
}

Método ejecutar():

El método void run() llama al método open_file dentro del bloque try. El bloque catch(...) atrapa todas las excepciones que surgen a través de std::throw_with_nested.

Genera una excepción que combina e con la excepción que se está manejando actualmente.

La excepción que se maneja en ese momento se convierte en la excepción externa y e se convierte en la excepción anidada. "run() falló" es el mensaje final impreso.

void run() {
  try {
    open_file("nonexistent.file");
  } catch (...) {
    std::throw_with_nested(std::runtime_error("run() failed"));
  }
}

Método int main():

El método main llama a la función run() dentro del bloque try, mientras que dentro del bloque catch, el programa llama al método print_exception mientras pasa e como parámetro.

Ejecuta las funciones que lanzan excepciones con un mensaje variable, imprime las excepciones capturadas en un formato anidado y muestra un mensaje personalizado con cada lanzamiento de excepción.

int main() {
  try {
    run();
  } catch (const std::exception& e) {
    print_exception(e);
  }
}

Código completo:

#include <exception>
#include <fstream>
#include <iostream>
#include <stdexcept>
#include <string>

void print_exception(const std::exception& ex, int stage = 0) {
  std::cerr << std::string(stage, ' ') << "Encountered Exception: " << ex.what()
            << '\n';
  try {
    std::rethrow_if_nested(ex);
  } catch (const std::exception& nestedException) {
    print_exception(nestedException, stage + 1);
  } catch (...) {
  }
}

void open_file(const std::string& s) {
  try {
    std::ifstream file(s);
    file.exceptions(std::ios_base::failbit);
  } catch (...) {
    std::throw_with_nested(std::runtime_error("Couldn't open " + s));
  }
}

void run() {
  try {
    open_file("nonexistent.file");
  } catch (...) {
    std::throw_with_nested(std::runtime_error("run() failed"));
  }
}

int main() {
  try {
    run();
  } catch (const std::exception& e) {
    print_exception(e);
  }
}

Producción :

exception: run() failed
 exception: Couldn't open nonexistent.file
  exception: basic_ios::clear

--------------------------------
Process exited after 0.05326 seconds with return value 0
Press any key to continue . . .

Use el error de tiempo de ejecución para lanzar excepciones con el mensaje en C++

Las clases personalizadas se pueden usar para generar excepciones con un mensaje variable, junto con la ruta del archivo de origen y la línea donde se genera este error.

Este programa usa macros y clases personalizadas para generar un mensaje variable en la cadena explicativa usando std::runtime_error.

Importar paquetes

Este programa requiere cuatro paquetes de importación, que son:

#include <iostream>
#include <sstream>
#include <stdexcept>
#include <string>

Clase pública

Este programa usa una clase constructora para lanzar excepciones con un mensaje variable y mostrar ese mensaje personalizado como una cadena explicativa cuando se llama al constructor público usando std::runtime_error.

La primera línea del fragmento de código crea una clase my_exception y un nuevo objeto runtime_error. Junto a él, se crea un objeto de cadena msg con alcance global, que se utilizará para pasar la variable explicativa cadena.

La clase constructora my_exception se inicializa con tres parámetros: un objeto de cadena std arg que pasa la cadena explicativa, un objeto puntero *file de tipo de datos char que almacena el nombre del archivo y una variable entera línea que almacena la línea donde se lanza la excepción.

Se crea un objeto de salida de cadena con std::ostringstream para mostrar una salida cuando se llama a este objeto. Por último, este objeto se almacena dentro de la variable msg.

El destructor ~my_exception() devuelve la variable msg como una cadena terminada en nulo usando return msg.c_str();.

class my_exception : public std::runtime_error {
  std::string msg;

 public:
  my_exception(const std::string &arg, const char *file, int line)
      : std::runtime_error(arg) {
    std::ostringstream o;
    o << file << ":" << line << ": " << arg;
    msg = o.str();
  }
  ~my_exception() throw() {}
  const char *what() const throw() { return msg.c_str(); }
};

Macros y lanzamiento de excepciones

Aquí, una macro se define usando #define throw_line(arg). Llamar a throw_line() lanzará my_exception(arg, __FILE__, __LINE__).

El método void f() llama a la macro throw_line y pasa el mensaje Oh no!.

#define throw_line(arg) throw my_exception(arg, __FILE__, __LINE__);

void f() { throw_line("Oh no!"); }

Método principal

El método principal lanzará excepciones con un mensaje variable llamando al método f() dentro del bloque try. Esto pasa el bloque de mensajes al método my_exception.

Dentro del bloque catch, se crea un objeto runtime_error ex, que llamará a la cadena explicativa usando ex.what().

Cuando se llama a la cadena explicativa, se llama al objeto ostream dentro del constructor my_exception y devuelve el nombre del archivo y la línea de error junto con el mensaje variable.

int main() {
  try {
    f();
  } catch (const std::runtime_error &ex) {
    std::cout << ex.what() << std::endl;
  }
}

Código completo:

#include <iostream>
#include <sstream>
#include <stdexcept>
#include <string>

class my_exception : public std::runtime_error {
  std::string msg;

 public:
  my_exception(const std::string &arg, const char *file, int line)
      : std::runtime_error(arg) {
    std::ostringstream o;
    o << file << ":" << line << ": " << arg;
    msg = o.str();
  }
  ~my_exception() throw() {}
  const char *what() const throw() { return msg.c_str(); }
};
#define throw_line(arg) throw my_exception(arg, __FILE__, __LINE__);

void f() { throw_line("Oh no!"); }

int main() {
  try {
    f();
  } catch (const std::runtime_error &ex) {
    std::cout << ex.what() << std::endl;
  }
}

Producción :

D:\c++\std-exceptions\8.cpp:23: Oh no!

--------------------------------
Process exited after 0.006875 seconds with return value 0
Press any key to continue . . .

Conclusión

Este artículo explica las diversas formas de lanzar excepciones con un mensaje variable y luego mostrar el mensaje cuando se lanzan excepciones.

Esperamos que este artículo le haya ayudado a aprender a lanzar excepciones con un mensaje variable, y podrá usarlo en escenarios de la vida real.

Artículo relacionado - C++ Exception