Usando std::apply em std::expected em C++23

C++

Simplificando o tratamento de erros em C++23

O tratamento eficaz de erros e o gerenciamento de valores de retorno são essenciais no desenvolvimento atual de C++. O método típico de trabalhar com funções que retornam tipos {std::expected} inclui muitas verificações e código de tratamento de erros, o que pode complicar a lógica e tornar o código mais difícil de manter.

Este artigo investiga o uso de um método mais sofisticado e geral para simplificar o gerenciamento de erros. Para reduzir o código padrão e melhorar a legibilidade, investigaremos a construção de um método `magic_apply` que agrega os resultados de muitos valores {std::expected} e os passa para outra função.

Comando Descrição
std::expected Um tipo de modelo usado em C++ para tratamento de erros que tem a capacidade de armazenar valores e erros.
std::unexpected Quando usado com std::expected, representa um valor de erro inesperado.
template<typename...> Descreve um modelo variado com uma quantidade infinita de argumentos de modelo que ele pode aceitar.
decltype Usado na programação de templates, principalmente para descobrir o tipo de expressão.
args.value() Se um objeto std::expected tiver um valor, acessa o valor contido nele.
args.has_value() Verifica se um valor está presente em um objeto std::expected.
(... && args.has_value()) Para determinar se cada objeto std::expected tem valores, dobre a expressão.
func(args.value()...) Usa os valores dos objetos std::expected para chamar o método func.
return unexpected<Err>(args.error()...) Retorna um erro inesperado contendo os erros dos objetos std::expected.

Gerenciamento eficaz de erros usando modelos variáveis

O type é usado nos scripts para facilitar o tratamento de erros em C++23. O objetivo principal é desenvolver uma função genérica chamada que pode transmitir a saída de vários valores para outra função. Ao fazer isso, a tediosa verificação de erros que normalmente é necessária ao trabalhar com muitos std::expected valores são reduzidos. é bastante flexível porque pode levar qualquer número de parâmetros utilizando modelos variados. Antes de chamar a função com o conteúdo de qualquer objeto, a lógica fundamental da magic_apply emprega uma expressão de dobra, , para garantir que tudo objetos têm valores válidos.

Essa ideia é ilustrada no primeiro exemplo de script usando tipos simples como e . Ele define um função que realiza um cálculo básico, e getA e funções que retornam tipos. Se ambos os valores de e getB são legítimos, podemos chamar usando ; caso contrário, o erro é propagado. Ao reduzir o código clichê, esse método melhora a legibilidade e a facilidade de manutenção. Ideia semelhante é apresentada no segundo roteiro, mas para destacar a versatilidade da abordagem, tipos e lambda functions são usados.

Reduzindo a complexidade no tratamento de erros C++ com `std::expected}

Script C++23 usando modelos variados

#include <expected>
#include <string>
#include <iostream>
#include <tuple>

using namespace std;

template<typename Func, typename... Args, typename Err>
auto magic_apply(Func func, const expected<Args, Err>&... args) -> expected<decltype(func(args.value()...)), Err> {
    if ((... && args.has_value())) {
        return func(args.value()...);
    } else {
        return unexpected<Err>(args.error()...);
    }
}

expected<int, string> getA(int x) {
    if (x > 0) return x;
    return unexpected<string>("Error in getA");
}

expected<double, string> getB(double y) {
    if (y > 0) return y;
    return unexpected<string>("Error in getB");
}

double compute_all(int a, double b) {
    return a + b;
}

int main() {
    auto result = magic_apply(compute_all, getA(10), getB(20.5));
    if (result) {
        cout << "Result: " << result.value() << endl;
    } else {
        cout << "Error: " << result.error() << endl;
    }
    return 0;
}

Combinando valores C++23 de resultados {std::expected} diferentes

Script C++23 usando funções Lambda

#include <expected>
#include <string>
#include <iostream>

using namespace std;

template<typename Func, typename... Args, typename Err>
auto magic_apply(Func func, const expected<Args, Err>&... args) -> expected<decltype(func(args.value()...)), Err> {
    bool all_valid = (args.has_value() && ...);
    if (all_valid) {
        return func(args.value()...);
    } else {
        return unexpected<Err>(args.error()...);
    }
}

expected<string, string> getA(bool flag) {
    if (flag) return "SuccessA";
    return unexpected<string>("Failed A");
}

expected<string, string> getB(bool flag) {
    if (flag) return "SuccessB";
    return unexpected<string>("Failed B");
}

string compute_all(const string& a, const string& b) {
    return a + " and " + b;
}

int main() {
    auto result = magic_apply(compute_all, getA(true), getB(true));
    if (result) {
        cout << "Result: " << result.value() << endl;
    } else {
        cout << "Error: " << result.error() << endl;
    }
    return 0;
}

Melhorando o tratamento de erros C++ com modelos variados

A capacidade de melhorar significativamente o tratamento de erros em sistemas complexos é outro benefício crucial de empregá-lo em C++. Combinar perfeitamente os resultados de muitas ações assíncronas é essencial em situações em que elas produzem tipos. Além de tornar o código mais simples, esse método garante um forte tratamento de erros. Funções mais versáteis e genéricas podem ser criadas combinando um número arbitrário de valores com variadic templates.

A versatilidade de permite que ele seja usado com funções que aceitam vários tipos de argumentos. A implementação é ainda mais simplificada utilizando , que deduz automaticamente o tipo de retorno da chamada de função combinada. Além disso, esta técnica pode ser expandida para gerenciar tarefas mais complexas, incluindo a fusão valores com outros tipos de erro ou alterando os valores antes de enviá-los para a função. Devido à sua adaptabilidade, o padrão pode ser usado para uma ampla gama de tarefas, desde cálculos simples até operações complexas.

Perguntas frequentes sobre modelos variados e std::expected

  1. O que é ?
  2. É um tipo de modelo C++ que pode conter um erro ou um valor válido e é usado para gerenciamento de erros.
  3. Como é que trabalhar?
  4. Elimina a necessidade de repetidas verificações de erros, combinando os resultados de numerosos valores e passá-los para uma função.
  5. O que são modelos variados?
  6. Os modelos de variáveis ​​oferecem uma grande liberdade no design de funções, permitindo que as funções aceitem um número arbitrário de parâmetros.
  7. Por que usar em ?
  8. Utilizando os valores do objetos para determinar automaticamente o tipo de retorno da função que está sendo chamada.
  9. É capaz de lidar com vários tipos de erros?
  10. Sim, pode ser feito para funcionar com valores com vários tipos de erros com alguns ajustes.
  11. Quais vantagens a utilização oferecer?
  12. Ao lidar com erros, oferece uma abordagem mais expressiva e limpa do que técnicas mais convencionais, como exceções ou códigos de retorno.
  13. É parte de ?
  14. Além de , na verdade, representa um valor incorreto.
  15. As ações assíncronas podem ser utilizadas com ?
  16. Na verdade, é adaptável para lidar valores retornados por operações assíncronas.
  17. O que é uma expressão dobrada?
  18. Aqui, o recurso em C++ chamado expressão fold é usado para verificar se todos objetos contêm valores válidos de maneira simples.

Em C++23, implementar uma função genérica para lidar com vários valores std::expected melhora muito a legibilidade do código e simplifica muito o tratamento de erros. A função magic_apply reduz o código clichê e melhora a capacidade de manutenção usando modelos variados para garantir que todos os valores previstos estejam corretos antes do processamento. Este método oferece uma solução flexível que pode ser aplicada a diferentes situações e oferece à programação C++ moderna uma maneira mais limpa e eficaz de lidar com falhas.