Sử dụng std::apply trên std::expected trong C++23

C++

Hợp lý hóa việc xử lý lỗi trong C++ 23

Xử lý hiệu quả các lỗi và quản lý giá trị trả về là điều cần thiết trong quá trình phát triển C++ ngày nay. Phương pháp điển hình khi làm việc với các hàm trả về loại {std::expected} bao gồm nhiều bước kiểm tra và mã xử lý lỗi, điều này có thể làm phức tạp logic và khiến mã khó bảo trì hơn.

Bài viết này nghiên cứu việc sử dụng một phương pháp tổng quát và phức tạp hơn để đơn giản hóa việc quản lý lỗi. Để giảm mã soạn sẵn và cải thiện khả năng đọc, chúng tôi sẽ nghiên cứu việc xây dựng phương thức `magic_apply` tổng hợp kết quả của nhiều giá trị {std::expected} và chuyển chúng sang một hàm khác.

Yêu cầu Sự miêu tả
std::expected Một loại mẫu được sử dụng trong C++ để xử lý lỗi có khả năng lưu trữ cả giá trị và lỗi.
std::unexpected Khi được sử dụng với std::expected, biểu thị một giá trị lỗi không mong muốn.
template<typename...> Phác thảo một mẫu đa dạng với số lượng đối số mẫu vô hạn mà nó có thể chấp nhận.
decltype Được sử dụng trong lập trình mẫu, đặc biệt là để tìm ra loại biểu thức.
args.value() Nếu một đối tượng std::expected có một giá trị, hãy truy cập giá trị chứa trong đó.
args.has_value() Xác minh xem một giá trị có xuất hiện trong đối tượng std::expected hay không.
(... && args.has_value()) Để xác định xem mọi đối tượng std::expected có giá trị hay không, hãy gấp biểu thức.
func(args.value()...) Sử dụng các giá trị của đối tượng std::expected để gọi phương thức func.
return unexpected<Err>(args.error()...) Trả về lỗi không mong muốn chứa lỗi từ các đối tượng std::expected.

Quản lý lỗi hiệu quả bằng cách sử dụng các mẫu biến

các type được sử dụng trong các tập lệnh để giảm bớt việc xử lý lỗi trong C++23. Mục tiêu chính là phát triển một chức năng chung gọi là có thể truyền đầu ra của một số giá trị sang hàm khác. Bằng cách này, việc kiểm tra lỗi tẻ nhạt thường cần thiết khi làm việc với nhiều std::expected các giá trị bị giảm đi. khá linh hoạt vì có thể mất bất kỳ số lượng các tham số bằng cách sử dụng các mẫu variadic. Trước khi gọi hàm với nội dung của bất kỳ đối tượng, logic cơ bản của magic_apply sử dụng một biểu thức gấp, , để đảm bảo tất cả đối tượng có giá trị hợp lệ.

Ý tưởng này được minh họa trong ví dụ tập lệnh đầu tiên sử dụng các kiểu đơn giản như Và . Nó định nghĩa một chức năng thực hiện một tính toán cơ bản, và getA Và các hàm trả về các loại. Nếu cả hai giá trị từ Và getB là hợp pháp, chúng ta có thể gọi sử dụng ; nếu không, lỗi sẽ được lan truyền. Bằng cách giảm mã soạn sẵn, phương pháp này nâng cao khả năng đọc và bảo trì. Một ý tưởng tương tự được trình bày trong kịch bản thứ hai, nhưng để làm nổi bật tính linh hoạt của phương pháp này, các loại và lambda functions được sử dụng.

Giảm độ phức tạp trong xử lý lỗi C++ với `std::expected}

Tập lệnh C++23 sử dụng các mẫu biến đổi

#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;
}

Kết hợp các giá trị {std::expected} khác nhau cho kết quả C++23

Tập lệnh C++23 sử dụng hàm 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;
}

Cải thiện khả năng xử lý lỗi C++ bằng các mẫu Variadic

Năng lực của tăng cường đáng kể việc xử lý lỗi trong các hệ thống phức tạp là một lợi ích quan trọng khác của việc sử dụng nó trong C++. Việc kết hợp các kết quả của nhiều hành động không đồng bộ một cách liền mạch là điều cần thiết trong các tình huống khi chúng mang lại hiệu quả. các loại. Ngoài việc làm cho mã đơn giản hơn, phương pháp này còn đảm bảo khả năng xử lý lỗi mạnh mẽ. Các hàm tổng quát và linh hoạt hơn có thể được tạo ra bằng cách kết hợp một số lượng tùy ý các hàm giá trị với variadic templates.

Tính linh hoạt của cho phép nó được sử dụng với các hàm có nhiều loại đối số khác nhau. Việc triển khai còn được thực hiện đơn giản hơn bằng cách sử dụng , tự động suy ra kiểu trả về của lệnh gọi hàm kết hợp. Hơn nữa, kỹ thuật này có thể được mở rộng để quản lý các nhiệm vụ phức tạp hơn, bao gồm cả việc hợp nhất giá trị với các loại lỗi khác hoặc thay đổi giá trị trước khi gửi chúng đến hàm. Do khả năng thích ứng của nó, mẫu này có thể được sử dụng cho nhiều nhiệm vụ khác nhau, từ các phép tính đơn giản đến các thao tác phức tạp.

Câu hỏi thường gặp về Mẫu đa dạng và std::expected

  1. Là gì ?
  2. Đây là loại mẫu C++ có thể chứa lỗi hoặc giá trị hợp lệ và được sử dụng để quản lý lỗi.
  3. Làm thế nào công việc?
  4. Nó loại bỏ sự cần thiết phải kiểm tra lỗi lặp đi lặp lại bằng cách kết hợp các kết quả của nhiều các giá trị và chuyển chúng tới một hàm.
  5. Mẫu variadic là gì?
  6. Các mẫu biến mang lại nhiều sự tự do trong thiết kế hàm bằng cách cho phép các hàm chấp nhận số lượng tham số tùy ý.
  7. Tại sao sử dụng TRONG ?
  8. Việc sử dụng các giá trị của đối tượng để tự động xác định kiểu trả về của hàm được gọi.
  9. Là có khả năng xử lý các loại lỗi khác nhau?
  10. Có, nó có thể được thực hiện để hoạt động với các giá trị có nhiều loại lỗi khác nhau với một vài điều chỉnh.
  11. Việc sử dụng có lợi ích gì lời đề nghị?
  12. Khi xử lý lỗi, nó đưa ra cách tiếp cận rõ ràng và rõ ràng hơn so với các kỹ thuật thông thường hơn như ngoại lệ hoặc mã trả về.
  13. Là một phần của ?
  14. Ngoài ra , trên thực tế, nó đại diện cho một giá trị không chính xác.
  15. Các hành động không đồng bộ có thể được sử dụng với ?
  16. Nó thực sự có khả năng thích ứng để xử lý các giá trị được trả về bởi các hoạt động không đồng bộ.
  17. Biểu thức gấp là gì?
  18. Ở đây, tính năng trong C++ được gọi là biểu thức gấp được sử dụng để kiểm tra xem tất cả các đối tượng chứa các giá trị hợp lệ một cách đơn giản.

Trong C++23, việc triển khai một hàm chung để xử lý nhiều giá trị std::expected sẽ cải thiện đáng kể khả năng đọc mã và đơn giản hóa đáng kể việc xử lý lỗi. Hàm magic_apply làm giảm mã soạn sẵn và tăng cường khả năng bảo trì bằng cách sử dụng các mẫu biến đổi để đảm bảo tất cả các giá trị dự kiến ​​đều chính xác trước khi xử lý. Phương pháp này cung cấp một giải pháp linh hoạt có thể áp dụng cho các tình huống khác nhau và mang lại cho chương trình C++ hiện đại một cách rõ ràng hơn, hiệu quả hơn để xử lý lỗi.