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 queT
no provee constructor por defecto; - Tanto
Opcion<T>
comoResultado<T>
ahora inicializan anullptr
cuandoT
es 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::move
para 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 porDefecto
en 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 porDefecto
en 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
}
//continuar
Este proyecto está licenciado bajo CC BY-SA 4.0. Ver el archivo LICENSE para más detalles.