Errores-- es una librería de C++ que provee abstracciones genéricas para el manejo de errores, valores opcionales y resultados de operaciones. La librería implementa tres abstracciones fundamentales: Error, Opcion<T> y Resultado<T>. Esta librería proporciona una forma coherente y flexible de gestionar el flujo de control en situaciones donde es necesario manejar errores de manera explícita -evitando rupturas en el flujo de control a través de excepciones- y gestionar la presencia o ausencia de valores de forma segura.
Errores-- está inspirada en el manejo de errores como valores de Go y en los contenedores Option<T> y Result<T> de Rust. La librería adscribe al paradigma Cero es Inicialización - ZII (Zero Is Initialization) y se enfoca en imponer prácticas seguras con una semántica clara de propiedad de memoria.
La librería se puede incluir en un proyecto descargando e #incluyendo el archivo de encabezado errores--.hpp
#include "errores--.hpp"Esta versión introduce manejo diferenciado para atender casos en que el tipo subyacente no provee constructor por defecto:
- Implementación de especializaciones de plantillas para
Opcion<T>yResultado<T>que que manejan casos en queTno provee constructor por defecto; - Tanto
Opcion<T>comoResultado<T>ahora inicializan anullptrcuandoTes un puntero desnudo; - Agrega método útil
valorO(T porDefecto)en la especialización deOpcion<T>para valores "simples"; y - Más pruebas.
La librería implementa reglas estrictas para garantizar la seguridad de la memoria. Tanto Opcion<T> como Resultado<T> asumen la propiedad de sus miembros hasta que sean consumidos. Las restricciones de memoria se aplican según el tipo de dato:
-
Valores Directos
- Tipos fundamentales (
int,double, etc.) - Tipos de la biblioteca estándar (
std::string, etc.) - Tipos definidos por el usuario que soporten construcción por defecto
- Tipos fundamentales (
-
Punteros Inteligentes
std::unique_ptr<T>std::shared_ptr<T>
-
Punteros Desnudos
- Deben ser propiedad exclusiva de la instancia
- Se liberan automáticamente al destruir la instancia
- La propiedad se transfiere al consumir el valor
- Los punteros desnudos deben ser propiedad exclusiva de la instancia
- No se permite el uso de punteros desnudos que apunten a memoria gestionada por
std::shared_ptr - La transferencia de propiedad se realiza mediante
std::movepara punteros inteligentes - Los punteros desnudos se transfieren mediante
std::exchange
La clase Error encapsula información sobre errores mediante un código de estado y un mensaje descriptivo. Esta abstracción permite identificar claramente el estado de una operación (éxito, error o fatal) y es extensible para casos específicos. Referencia completa.
CodigoEstado Codigo(): Devuelve el código de estado del errorstd::string Mensaje(): Devuelve el mensaje de errorvoid agregarMensaje(std::string mensaje): Agrega texto adicional al mensajeoperator bool(): Devuelve verdadero si hay un error (estado no es EXITO)operator std::string(): Convierte el error a su representación en cadenaoperator const char*(): Convierte el mensaje a cadena estilo Coperator char*(): Convierte el mensaje a cadena estilo C modificableoperator<<: Permite imprimir el error en flujos de salida
namespace err {
inline Error Exito(std::string mensaje = "Exito");
inline Error Fatal(std::string mensaje = "Error Fatal");
inline Error Generico(std::string mensaje = "Error");
}Opcion<T> es un contenedor genérico que representa un valor opcional que puede estar presente o ausente. Implementa especializaciones para manejar diferentes tipos de datos de manera segura. Referencia completa.
bool estaVacia() const noexcept: Indica si la opción está vacíastd::tuple<T, bool> Consumir() noexcept: Devuelve una tupla con el valor (o en su defectoT{}/nullptr) y un indicador de si la Opción está vacía. Para valores directos que proveen constructor por defecto, o para punteros.std::tuple<T, bool> Consumir(T porDefecto) noexcept: Devuelve una tupla con el valor (si existe, sino valor por defect) y un indicador de éxito. Para valores directos que no proveen constructor por defectooperator bool(): Devuelve verdadero si la opción contiene un valoroperator()(): Alias para Consumir()
-
Valores Directos
- Retorna valor inicializado a cero si está vacía y si el tipo ofrece constructor por defecto, o acepta un argumento
T porDefectoen caso contrario - Constructor por defecto eliminado para tipos que no lo proveen.
- Retorna valor inicializado a cero si está vacía y si el tipo ofrece constructor por defecto, o acepta un argumento
-
Punteros Desnudos (T*)
- nullptr cuando está vacía
- Asume propiedad exclusiva de la memoria apuntada
- Libera la memoria automáticamente si no es consumida
- Constructor y operador de copia eliminados
- Semántica de movimiento mediante std::exchange
-
Punteros Inteligentes (std::unique_ptr, std::shared_ptr)
- nullptr cuando está vacía
- Constructor y operador de copia eliminados
- Semántica de movimiento para transferencia de propiedad
Resultado<T> encapsula el resultado de una operación que puede ser exitosa o fallar, combinando un valor de tipo T con un Error. Referencia completa
err::Error Error() const noexcept: Devuelve el estado del errorstd::tuple<T, err::Error> Consumir() noexcept: Devuelve una tupla con el valor (o en su defectoT{}/nullptr) y el error. Para valores directos que proveen constructor por defecto. O punteros.std::tuple<T, err::Error> Consumir(T porDefecto) noexcept: Devuelve una tupla con el valor (si existe, de lo contrarioporDefecto) y el error Para valores directos que no proveen constructor por defecto.operator bool(): Devuelve verdadero si la operación fue exitosaoperator()(): Alias para Consumir()
-
Valores Directos
- Retorna valor inicializado a cero en caso de error si el tipo ofrece constructor por defecto, o acepta un argumento
T porDefectoen caso contrario - Constructor por defecto eliminado para tipos que no lo proveen.
- Retorna valor inicializado a cero en caso de error si el tipo ofrece constructor por defecto, o acepta un argumento
-
Punteros Desnudos (T*)
- nullptr en caso de error
- Asume propiedad exclusiva de la memoria apuntada
- Libera memoria si no es consumida
- Constructor y operador de copia eliminados
- Semántica de movimiento mediante std::exchange
-
Punteros Inteligentes (std::unique_ptr, std::shared_ptr)
- nullptr en caso de error
- Constructor y operador de copia eliminados
- Semántica de movimiento para transferencia de propiedad
La referencia para las abstracciones, una explicación más acabada del manejo de memoria con errores-- y más ejemplos pueden encontrarse en /documentación
- Compilador compatible con C++20
- CMake y Catch2 (para ejecutar las pruebas)
La librería incluye una suite (aún en progreso) de pruebas implementada con Catch2, que verifica el comportamiento correcto de todas las abstracciones y sus especializaciones.
Se puede correr el archivo provisto ( /pruebas/correr_pruebas.exe ) o compilar las pruebas desde cero
Esta versión (v0.5.0-beta) representa un avance significativo en términos de seguridad y funcionalidad, pero aún se considera en estado beta. La API puede sufrir cambios (menores) en futuras versiones.
#include "errores--.hpp"
// Función que puede fallar retornando un Resultado
res::Resultado<float> dividir(float a, float b) {
if (b == 0.0f) {
return res::Resultado<float>(0.0f, err::ERROR, "División por cero");
}
return res::Resultado<float>(a / b);
}
// Función que puede retornar un valor opcional
opc::Opcion<std::string> obtenerEntorno(const std::string& clave) {
if (configuraciones.count(clave) == 0 || !ENTORNO) {
return opc::Opcion<std::string>(); // Retorna opción vacía
}
return opc::Opcion<std::string>(configuraciones[clave]);
}
// otra Función que puede retornar un valor opcional
opc::Opcion<int> obtenerConfiguracion(const std::string& clave) {
if (configuraciones.count(clave) == 0) {
return opc::Opcion<int>(); // Retorna opción vacía
}
return opc::Opcion<int>(configuraciones[clave]);
}
// Uso combinado de las abstracciones
void ejemplo() {
// Uso de Resultado
auto [resultado, error] = dividir(10.0f, 2.0f)();
if (error) {
std::cout << "Error al dividir: " << error.Mensaje();
return; // Manejo temprano del error
}
// Uso de Opcion
auto opcion = obtenerConfiguracion("max_conexiones");
auto [valor, ok] = opcion();
if (!ok) {
// Manejar ausencia de valor
valor = 10; // Valor por defecto
}
auto opcionEntorno = obtenerEntorno("PRODUCCION");
std::string entorno = opcionEntorno.valorO("DESARROLLO");
// Continuar con la lógica normal
std::cout << "Resultado: " << resultado << ", Configuración: " << valor << std::endl;
}
// Ejemplo con valores directos
opc::Opcion<int> optInt(42);
auto [valor, ok] = optInt();
if (ok) {
std::cout << "Valor: " << valor << "\n"; // Valor: 42
}
opc::Opcion<int> optInt();
int x = optInt.valorO(5)
std::cout << "X: " << x << "\n"; // X: 5
// Ejemplo con unique_ptr
auto ptr = std::make_unique<MiClase>(42);
res::Resultado<std::unique_ptr<MiClase>> resultado(std::move(ptr));
auto [valor, error] = resultado();
if (!error) {
valor->procesar();
}
// Ejemplo de función que retorna un Resultado
res::Resultado<float> dividir(float a, float b) {
if (b == 0) {
return res::Resultado<float>(0, err::Error(err::ERROR, "División por cero"));
}
return res::Resultado<float>(a / b);
}
auto [resultado, error] = dividir(10.0f, 2.0f)();
if (error) {
//manejar error, salvar el estado o retornar temprano
}
//continuar
#include "errores--.hpp"
// Función que puede fallar retornando un Resultado
res::Resultado<float> dividir(float a, float b) {
if (b == 0.0f) {
return res::Resultado<float>(0.0f, err::ERROR, "División por cero");
}
return res::Resultado<float>(a / b);
}
// Función que puede retornar un valor opcional
opc::Opcion<std::string> obtenerEntorno(const std::string& clave) {
if (configuraciones.count(clave) == 0 || !ENTORNO) {
return opc::Opcion<std::string>(); // Retorna opción vacía
}
return opc::Opcion<std::string>(configuraciones[clave]);
}
// otra Función que puede retornar un valor opcional
opc::Opcion<int> obtenerConfiguracion(const std::string& clave) {
if (configuraciones.count(clave) == 0) {
return opc::Opcion<int>(); // Retorna opción vacía
}
return opc::Opcion<int>(configuraciones[clave]);
}
// Uso combinado de las abstracciones
void ejemplo() {
// Uso de Resultado
auto [resultado, error] = dividir(10.0f, 2.0f)();
if (error) {
std::cout << "Error al dividir: " << error.Mensaje();
return; // Manejo temprano del error
}
// Uso de Opcion
auto opcion = obtenerConfiguracion("max_conexiones");
auto [valor, ok] = opcion();
if (!ok) {
// Manejar ausencia de valor
valor = 10; // Valor por defecto
}
auto opcionEntorno = obtenerEntorno("PRODUCCION");
std::string entorno = opcionEntorno.valorO("DESARROLLO");
// Continuar con la lógica normal
std::cout << "Resultado: " << resultado << ", Configuración: " << valor << std::endl;
}// Ejemplo con valores directos
opc::Opcion<int> optInt(42);
auto [valor, ok] = optInt();
if (ok) {
std::cout << "Valor: " << valor << "\n"; // Valor: 42
}
opc::Opcion<int> optInt();
int x = optInt.valorO(5)
std::cout << "X: " << x << "\n"; // X: 5// Ejemplo con unique_ptr
auto ptr = std::make_unique<MiClase>(42);
res::Resultado<std::unique_ptr<MiClase>> resultado(std::move(ptr));
auto [valor, error] = resultado();
if (!error) {
valor->procesar();
}// Ejemplo de función que retorna un Resultado
res::Resultado<float> dividir(float a, float b) {
if (b == 0) {
return res::Resultado<float>(0, err::Error(err::ERROR, "División por cero"));
}
return res::Resultado<float>(a / b);
}
auto [resultado, error] = dividir(10.0f, 2.0f)();
if (error) {
//manejar error, salvar el estado o retornar temprano
}
//continuarEste proyecto está licenciado bajo CC BY-SA 4.0. Ver el archivo LICENSE para más detalles.