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 std::expected 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à magic_apply có thể truyền đầu ra của một số std::expected 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. magic_apply khá linh hoạt vì có thể mất bất kỳ số lượng std::expected 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ỳ std::expected đối tượng, logic cơ bản của magic_apply sử dụng một biểu thức gấp, (... && args.has_value()), để đảm bảo tất cả std::expected đố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ư int Và double. Nó định nghĩa một compute_all chức năng thực hiện một tính toán cơ bản, và getA Và getB các hàm trả về std::expected các loại. Nếu cả hai giá trị từ getA Và getB là hợp pháp, chúng ta có thể gọi compute_all sử dụng magic_apply; 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, string 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 std::expected 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ả. std::expected 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 std::expected giá trị với variadic templates.
Tính linh hoạt của magic_apply 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 decltype, 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 std::expected 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
- Là gì std::expected?
- Đâ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.
- Làm thế nào magic_apply công việc?
- 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 std::expected các giá trị và chuyển chúng tới một hàm.
- Mẫu variadic là gì?
- 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 ý.
- Tại sao sử dụng decltype TRONG magic_apply?
- Việc sử dụng các giá trị của std::expected đối tượng để tự động xác định kiểu trả về của hàm được gọi.
- Là magic_apply có khả năng xử lý các loại lỗi khác nhau?
- Có, nó có thể được thực hiện để hoạt động với std::expected các giá trị có nhiều loại lỗi khác nhau với một vài điều chỉnh.
- Việc sử dụng có lợi ích gì std::expected lời đề nghị?
- 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ề.
- Là std::unexpected một phần của std::expected?
- Ngoài ra std::expected, std::unexpected trên thực tế, nó đại diện cho một giá trị không chính xác.
- Các hành động không đồng bộ có thể được sử dụng với magic_apply?
- Nó thực sự có khả năng thích ứng để xử lý std::expected các giá trị được trả về bởi các hoạt động không đồng bộ.
- Biểu thức gấp là gì?
- Ở đâ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ả std::expected các đối tượng chứa các giá trị hợp lệ một cách đơn giản.
Kết thúc:
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.