diff --git a/README.md b/README.md index 5dfa1ad..0d15379 100644 --- a/README.md +++ b/README.md @@ -4,15 +4,15 @@ `Errores--` está inspirada en el manejo de errores como valores de `Go` y en los contenedores `Option` y `Result` 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. -[![Hecho por Chaska](https://img.shields.io/badge/hecho_por-Ch'aska-303030.svg)](https://cajadeideas.ar) -[![Versión: Alpha v0.2.0](https://img.shields.io/badge/version-Alpha_v0.2.0-orange.svg)](https://github.com/hernanatn/github.com/hernanatn/aplicacion.go/releases/latest) -[![Verisón de C++: 20](https://img.shields.io/badge/C++-20-blue?logo=cplusplus)](https://es.cppreference.com/w/cpp/20) +hecho_por_Chaska C++ version licencia + + ![Empaquetado](https://img.shields.io/github/actions/workflow/status/Hernanatn/errores--/empaquetar.yml?label=bundle) -[![Licencia: CC BY-NC 4.0](https://img.shields.io/badge/Licencia-CC_BY--SA_4.0-lightgrey.svg)](LICENSE) +![Pruebas](https://img.shields.io/github/actions/workflow/status/Hernanatn/errores--/pruebas.yml?label=tests) 1. [Introducción](#errores--) - [Uso](#uso) -2. [Novedades en v0.2.0-alpha](#novedades-en-v020-alpha) +2. [Novedades en v0.5.0-beta](#novedades-en-v020-beta) 3. [Manejo de Memoria](#manejo-de-memoria) - [Tipos de Datos Soportados](#tipos-de-datos-soportados) - [Restricciones de Memoria](#restricciones-de-memoria) @@ -38,15 +38,14 @@ La librería se puede incluir en un proyecto descargando e `#incluyendo` el arch #include "errores--.hpp" ``` -## Novedades en v0.2.0-alpha +## Novedades en v0.5.0-beta -Esta versión introduce mejoras significativas en el manejo de memoria y la seguridad de tipos: +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` y `Resultado` que manejan de forma segura valores directos, punteros desnudos y punteros inteligentes. -- Utilización de conceptos de C++20 para restricciones de tipos más expresivas y seguras. -- Suite de pruebas completa implementada con Catch2. -- Semántica de movimiento mejorada para tipos que no admiten copia. -- Documentación expandida con ejemplos de uso. +- Implementación de especializaciones de plantillas para `Opcion` y `Resultado` que que manejan casos en que `T` no provee constructor por defecto; +- Tanto ``Opcion`` como ``Resultado`` ahora inicializan a `nullptr` cuando `T` es un puntero desnudo; +- Agrega método útil `valorO(T porDefecto)` en la especialización de `Opcion` para valores "simples"; y +- Más pruebas. ## Manejo de Memoria @@ -91,7 +90,7 @@ La clase `Error` encapsula información sobre errores mediante un código de est - `operator char*()`: Convierte el mensaje a cadena estilo C modificable - `operator<<`: Permite imprimir el error en flujos de salida -#### Funciones Utilitarias +#### Funciones Útiles ```cpp namespace err { inline Error Exito(std::string mensaje = "Exito"); @@ -106,25 +105,27 @@ namespace err { #### Métodos - `bool estaVacia() const noexcept`: Indica si la opción está vacía -- `std::tuple Consumir() noexcept`: Devuelve una tupla con el valor (si existe) y un indicador de éxito +- `std::tuple Consumir() noexcept`: Devuelve una tupla con el valor (o en su defecto `T{}` / `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 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 defecto* - `operator bool()`: Devuelve verdadero si la opción contiene un valor - `operator()()`: Alias para Consumir() #### Especializaciones 1. **Valores Directos** - - Maneja tipos por valor (int, std::string, etc.) - - Inicialización a cero cuando está vacío + - 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. 2. **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 3. **Punteros Inteligentes** (std::unique_ptr, std::shared_ptr) - - Semántica de movimiento para transferencia de propiedad - - nullptr cuando está vacío + - nullptr cuando está vacía - Constructor y operador de copia eliminados + - Semántica de movimiento para transferencia de propiedad ### Resultado @@ -133,24 +134,27 @@ namespace err { #### Métodos - `err::Error Error() const noexcept`: Devuelve el estado del error -- `std::tuple Consumir() noexcept`: Devuelve una tupla con el valor y el error +- `std::tuple Consumir() noexcept`: Devuelve una tupla con el valor (o en su defecto `T{}` / `nullptr`) y el error. *Para valores directos que proveen constructor por defecto. O punteros*. +- `std::tuple Consumir(T porDefecto) noexcept`: Devuelve una tupla con el valor (si existe, de lo contrario `porDefecto`) y el error *Para valores directos que no proveen constructor por defecto*. - `operator bool()`: Devuelve verdadero si la operación fue exitosa - `operator()()`: Alias para Consumir() #### Especializaciones 1. **Valores Directos** - - Retorna valor inicializado a cero en caso de error - - Copia el valor en caso de éxito + - 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. 2. **Punteros Desnudos** (T*) + - nullptr en caso de error - Asume propiedad exclusiva de la memoria apuntada - - Retorna nullptr en caso de error - Libera memoria si no es consumida + - Constructor y operador de copia eliminados - Semántica de movimiento mediante std::exchange 3. **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 - - Retorna nullptr en caso de error ## Documentación 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](/documentación) @@ -168,7 +172,7 @@ Se puede correr el archivo provisto ( [/pruebas/correr_pruebas.exe](/pruebas/pru ## Estado del Proyecto -Esta versión (v0.2.0-alpha) representa un avance significativo en términos de seguridad y funcionalidad, pero aún se considera en estado alpha. La API puede sufrir cambios en futuras versiones. +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. ## Ejemplos @@ -186,6 +190,14 @@ res::Resultado dividir(float a, float b) { } // Función que puede retornar un valor opcional +opc::Opcion obtenerEntorno(const std::string& clave) { + if (configuraciones.count(clave) == 0 || !ENTORNO) { + return opc::Opcion(); // Retorna opción vacía + } + return opc::Opcion(configuraciones[clave]); +} + +// otra Función que puede retornar un valor opcional opc::Opcion obtenerConfiguracion(const std::string& clave) { if (configuraciones.count(clave) == 0) { return opc::Opcion(); // Retorna opción vacía @@ -209,6 +221,9 @@ void ejemplo() { // 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; @@ -222,8 +237,12 @@ void ejemplo() { opc::Opcion optInt(42); auto [valor, ok] = optInt(); if (ok) { - std::cout << "Valor: " << valor << "\n"; + std::cout << "Valor: " << valor << "\n"; // Valor: 42 } + +opc::Opcion optInt(); +int x = optInt.valorO(5) +std::cout << "X: " << x << "\n"; // X: 5 ``` ### Manejo de Punteros Inteligentes @@ -257,4 +276,4 @@ if (error) { ``` ## Licencia -Este proyecto está licenciado bajo CC BY-SA 4.0. Ver el archivo LICENSE para más detalles. +Este proyecto está licenciado bajo CC BY-SA 4.0. Ver el archivo [LICENSE](LICENSE) para más detalles. diff --git "a/documentaci\303\263n/Opcion.md" "b/documentaci\303\263n/Opcion.md" index c124691..f3ddfff 100644 --- "a/documentaci\303\263n/Opcion.md" +++ "b/documentaci\303\263n/Opcion.md" @@ -28,19 +28,29 @@ namespace opc { ### Implementación Principal ```cpp namespace opc { - template - struct Opcion : public OpcionBase { - private: + template + struct Opcion : public OpcionBase{ + private: T data; - using OpcionBase::vacia; - - public: - Opcion() noexcept; + using OpcionBase::vacia; + public: + explicit Opcion() noexcept + requires utiles::genericos::sin_constructor_por_defecto = delete; + explicit Opcion() noexcept(utiles::genericos::con_constructor_por_defecto) + requires utiles::genericos::con_constructor_por_defecto; explicit Opcion(T data) noexcept; + + T valorO(T porDefecto) const noexcept; - std::tuple Consumir() noexcept; - operator bool() noexcept; - std::tuple operator()() noexcept; + std::tuple Consumir() noexcept(utiles::genericos::con_constructor_por_defecto) + requires utiles::genericos::con_constructor_por_defecto; + std::tuple Consumir(T porDefecto) noexcept + requires utiles::genericos::sin_constructor_por_defecto; + + std::tuple operator()() noexcept(utiles::genericos::con_constructor_por_defecto) + requires utiles::genericos::con_constructor_por_defecto; + std::tuple operator()(T porDefecto) noexcept + requires utiles::genericos::sin_constructor_por_defecto; }; } ``` @@ -99,7 +109,8 @@ namespace opc { ### Métodos - `bool estaVacia() const noexcept`: Indica si la opción está vacía -- `std::tuple Consumir() noexcept`: Devuelve una tupla con el valor (si existe) y un indicador de éxito +- `std::tuple Consumir() noexcept`: Devuelve una tupla con el valor (o en su defecto `T{}` / `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 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 defecto*. - `operator bool()`: Devuelve verdadero si la opción contiene un valor - `operator()()`: Alias para Consumir() diff --git "a/documentaci\303\263n/Resultado.md" "b/documentaci\303\263n/Resultado.md" index 1f15321..f03635e 100644 --- "a/documentaci\303\263n/Resultado.md" +++ "b/documentaci\303\263n/Resultado.md" @@ -30,16 +30,26 @@ namespace res { using ResultadoBase::error; public: - Resultado() noexcept; + explicit Resultado() noexcept + requires utiles::genericos::sin_constructor_por_defecto = delete; + explicit Resultado() noexcept(utiles::genericos::con_constructor_por_defecto) + requires utiles::genericos::con_constructor_por_defecto; explicit Resultado(T data) noexcept; explicit Resultado(T data, err::Error error) noexcept; explicit Resultado(T data, err::CodigoEstado codigo, std::string mensaje) noexcept; - - std::tuple Consumir() noexcept; ~Resultado() noexcept; operator bool() noexcept; - std::tuple operator()() noexcept; + + std::tupleConsumir(T porDefecto) noexcept + requires utiles::genericos::sin_constructor_por_defecto; + std::tupleConsumir() noexcept(utiles::genericos::con_constructor_por_defecto) + requires utiles::genericos::con_constructor_por_defecto; + + std::tuple operator()(T porDefecto) noexcept + requires utiles::genericos::sin_constructor_por_defecto; + std::tuple operator()() noexcept(utiles::genericos::con_constructor_por_defecto) + requires utiles::genericos::con_constructor_por_defecto; }; } ``` @@ -108,7 +118,8 @@ namespace res { ### Métodos - `err::Error Error() const noexcept`: Devuelve el estado del error -- `std::tuple Consumir() noexcept`: Devuelve una tupla con el valor y el error +- `std::tuple Consumir() noexcept`: Devuelve una tupla con el valor (o en su defecto `T{}` / `nullptr`) y el error. *Para valores directos que proveen constructor por defecto. O punteros*. +- `std::tuple Consumir(T porDefecto) noexcept`: Devuelve una tupla con el valor (si existe, de lo contrario `porDefecto`) y el error *Para valores directos que no proveen constructor por defecto*. - `operator bool()`: Devuelve verdadero si la operación fue exitosa - `operator()()`: Alias para Consumir() diff --git a/fuente/Opcion.hpp b/fuente/Opcion.hpp index a7fa07f..ee7ca1e 100644 --- a/fuente/Opcion.hpp +++ b/fuente/Opcion.hpp @@ -14,8 +14,8 @@ namespace opc { // Declaración public: OpcionBase() noexcept : vacia(true) {} virtual ~OpcionBase() noexcept = default; - bool estaVacia() const noexcept { return vacia; } - operator bool() noexcept { return !vacia; } + bool estaVacia() const noexcept { return vacia; }; + operator bool() noexcept { return !vacia; }; }; /** @@ -60,9 +60,15 @@ namespace opc { // Declaración using OpcionBase::vacia; public: - Opcion() noexcept; + + explicit Opcion() noexcept + requires utiles::genericos::sin_constructor_por_defecto = delete; + + explicit Opcion() noexcept(utiles::genericos::con_constructor_por_defecto) + requires utiles::genericos::con_constructor_por_defecto; explicit Opcion(T data) noexcept; + T valorO(T porDefecto) const noexcept; /** * @brief Consumir "eleva" el valor de la opción y la "consume" - transfiere la propiedad de la data subyacente si es un puntero. * @@ -82,7 +88,11 @@ namespace opc { // Declaración * defecto) y un indicador de si la opción contenía un valor válido (`true` o * `false`). */ - std::tuple Consumir() noexcept; + std::tuple Consumir() noexcept(utiles::genericos::con_constructor_por_defecto) + requires utiles::genericos::con_constructor_por_defecto; + + std::tuple Consumir(T porDefecto) noexcept + requires utiles::genericos::sin_constructor_por_defecto; operator bool() noexcept; @@ -92,9 +102,12 @@ namespace opc { // Declaración * defecto) y un indicador de si la opción contenía un valor válido (`true` o * `false`). */ - std::tuple operator()() noexcept; - }; + std::tuple operator()() noexcept(utiles::genericos::con_constructor_por_defecto) + requires utiles::genericos::con_constructor_por_defecto; + std::tuple operator()(T porDefecto) noexcept + requires utiles::genericos::sin_constructor_por_defecto; + }; template requires utiles::genericos::puntero_desnudo struct Opcion : public OpcionBase { @@ -114,6 +127,7 @@ namespace opc { // Declaración ~Opcion() noexcept; operator bool() noexcept; + T valorO(T porDefecto) const noexcept; std::tuple Consumir() noexcept; std::tuple operator()() noexcept; }; @@ -144,8 +158,9 @@ namespace opc { // Declaración namespace opc{ // Implementación template - Opcion::Opcion() noexcept{ - this->data = T{}; + Opcion::Opcion() noexcept(utiles::genericos::con_constructor_por_defecto) + requires utiles::genericos::con_constructor_por_defecto { + this->data = T{}; this->vacia = true; } @@ -156,16 +171,36 @@ namespace opc{ // Implementación } template - std::tuple Opcion::Consumir() noexcept{ + std::tuple Opcion::Consumir(T porDefecto) noexcept + requires utiles::genericos::sin_constructor_por_defecto { + bool ok = !this->estaVacia(); + return std::make_tuple(ok ? this->data : porDefecto, ok); + }; + + template + std::tuple Opcion::Consumir() noexcept(utiles::genericos::con_constructor_por_defecto) + requires utiles::genericos::con_constructor_por_defecto { bool ok = !this->estaVacia(); return std::make_tuple(ok ? this->data : T{}, ok); } template - std::tuple Opcion::operator()() noexcept{ + std::tuple Opcion::operator()() noexcept(utiles::genericos::con_constructor_por_defecto) + requires utiles::genericos::con_constructor_por_defecto { return Consumir(); } + template + std::tuple Opcion::operator()(T porDefecto) noexcept + requires utiles::genericos::sin_constructor_por_defecto { + return Consumir(porDefecto); + } + + + template + T Opcion::valorO(T porDefecto) const noexcept{ + return this->estaVacia() ? porDefecto : data; + }; /* @@ -174,7 +209,7 @@ namespace opc{ // Implementación template requires utiles::genericos::puntero_desnudo Opcion::Opcion() noexcept{ - this->data = T{}; + this->data = nullptr; this->vacia = true; } @@ -219,6 +254,12 @@ namespace opc{ // Implementación return Consumir(); } + template requires utiles::genericos::puntero_desnudo + T Opcion::valorO(T porDefecto) const noexcept{ + bool ok = !this->estaVacia(); + this->vacia = true; + return ok ? std::exchange(this->data,nullptr) : porDefecto; + }; /* * Especialización para Punteros Inteligentes */ diff --git a/fuente/Resultado.hpp b/fuente/Resultado.hpp index c18d527..ee5b771 100644 --- a/fuente/Resultado.hpp +++ b/fuente/Resultado.hpp @@ -54,17 +54,27 @@ namespace res { //Declaración using ResultadoBase::error; public: - Resultado() noexcept : resultado(T{}), ResultadoBase::error(err::Exito()) {}; + explicit Resultado() noexcept + requires utiles::genericos::sin_constructor_por_defecto = delete; + explicit Resultado() noexcept(utiles::genericos::con_constructor_por_defecto) + requires utiles::genericos::con_constructor_por_defecto; explicit Resultado(T data) noexcept; explicit Resultado(T data, err::CodigoEstado codigo, std::string mensaje) noexcept; explicit Resultado(T data, err::Error error) noexcept; - - std::tupleConsumir() noexcept; + ~Resultado() noexcept; + std::tupleConsumir(T porDefecto) noexcept + requires utiles::genericos::sin_constructor_por_defecto; + std::tupleConsumir() noexcept(utiles::genericos::con_constructor_por_defecto) + requires utiles::genericos::con_constructor_por_defecto; + operator bool() noexcept; - std::tuple operator()() noexcept; + std::tuple operator()(T porDefecto) noexcept + requires utiles::genericos::sin_constructor_por_defecto; + std::tuple operator()() noexcept(utiles::genericos::con_constructor_por_defecto) + requires utiles::genericos::con_constructor_por_defecto; }; template requires utiles::genericos::puntero_desnudo @@ -74,7 +84,7 @@ namespace res { //Declaración using ResultadoBase::error; public: - Resultado() noexcept : resultado(nullptr), ResultadoBase::error(err::Exito()) {}; + Resultado() noexcept; explicit Resultado(T data) noexcept; @@ -123,6 +133,13 @@ namespace res { //Declaración } namespace res { // Implementación + template + Resultado::Resultado() noexcept(utiles::genericos::con_constructor_por_defecto) + requires utiles::genericos::con_constructor_por_defecto { + this->resultado = T{}; + this->error = err::Exito(); + } + template Resultado::Resultado(T data) noexcept{ this->resultado = data; @@ -150,19 +167,41 @@ namespace res { // Implementación } template - std::tuple Resultado::Consumir() noexcept{ + std::tuple Resultado::Consumir(T porDefecto) noexcept + requires utiles::genericos::sin_constructor_por_defecto { + bool ok = !this->Error(); + return std::make_tuple(ok ? this->resultado : porDefecto, error); + }; + + template + std::tuple Resultado::Consumir() noexcept(utiles::genericos::con_constructor_por_defecto) + requires utiles::genericos::con_constructor_por_defecto { bool ok = !this->Error(); return std::make_tuple(ok ? this->resultado : T{}, error); } + template - std::tuple Resultado::operator()() noexcept{ - return Consumir(); - } + std::tuple Resultado::operator()() noexcept(utiles::genericos::con_constructor_por_defecto) + requires utiles::genericos::con_constructor_por_defecto { + return Consumir(); + } + + template + std::tuple Resultado::operator()(T porDefecto) noexcept + requires utiles::genericos::sin_constructor_por_defecto { + return Consumir(porDefecto); + } /* * Especialización para Punteros Desnudos */ + template requires utiles::genericos::puntero_desnudo + Resultado::Resultado() noexcept{ + this->resultado = nullptr; + this->error = err::Exito(); + } + template requires utiles::genericos::puntero_desnudo Resultado::Resultado(const Resultado&& otro) noexcept{ this->resultado = std::exchange(otro->resultado, nullptr); diff --git a/fuente/utiles/Genericos.hpp b/fuente/utiles/Genericos.hpp index f37fe41..2d9fa17 100644 --- a/fuente/utiles/Genericos.hpp +++ b/fuente/utiles/Genericos.hpp @@ -1,53 +1,45 @@ #ifndef GENERICOS_H #define GENERICOS_H -#include #include - +#include namespace utiles { - namespace genericos { - template - struct tiene_et : std::false_type {}; - - template - struct tiene_et> : std::true_type {}; - - template - struct tiene_vt : std::false_type {}; - - template - struct tiene_vt> : std::true_type {}; - - template - struct es_solo_movible { - private: - static constexpr bool es_movible = - std::is_move_constructible_v && - std::is_move_assignable_v; - - static constexpr bool es_copiable = - std::is_copy_constructible_v && - std::is_copy_assignable_v; - - public: - static constexpr bool value = es_movible && !es_copiable; - }; - - template - concept puntero_inteligente = - std::is_same_v> || - std::is_same_v>; - - template - concept puntero_desnudo = std::is_pointer::value; - - template - concept puntero = - puntero_desnudo || - puntero_inteligente; - - } -} +namespace genericos { +template struct tiene_et : std::false_type {}; +template struct tiene_et> : std::true_type {}; +template struct tiene_vt : std::false_type {}; +template struct tiene_vt> : std::true_type {}; + + + +template +concept es_copiable = std::is_copy_constructible_v && std::is_copy_assignable_v; + +template +concept es_movible = std::is_move_constructible_v && std::is_move_assignable_v; + +template +concept es_solo_movible = es_movible && !es_copiable; + +template +concept puntero_inteligente = + std::is_same_v> || + std::is_same_v>; + +template +concept puntero_desnudo = std::is_pointer::value; + +template +concept puntero = puntero_desnudo || puntero_inteligente; + +template +concept sin_constructor_por_defecto = !std::is_default_constructible_v; + +template +concept con_constructor_por_defecto = std::is_default_constructible_v; + +} // namespace genericos +} // namespace utiles #endif \ No newline at end of file diff --git a/pruebas/pruebas.exe b/pruebas/pruebas.exe index 85eafb4..5c0778c 100644 Binary files a/pruebas/pruebas.exe and b/pruebas/pruebas.exe differ