Một đề xuất mới nhằm đưa các tính năng lập trình contract vào ngôn ngữ C đã khơi dậy những cuộc thảo luận sôi nổi trong cộng đồng lập trình. Đề xuất này, được lấy cảm hứng từ công việc contract đang diễn ra của C++, nhằm thêm các điều kiện tiên quyết và điều kiện hậu quyết vào các hàm C, nhưng các nhà phát triển lại có quan điểm trái chiều về việc liệu những bổ sung như vậy sẽ giúp ích hay gây hại cho ngôn ngữ này.
Các nguyên thủy hợp đồng được đề xuất cho C:
contract_assert(COND, "message")
- Tương tự như assert hiện có nhưng luôn hoạt động (không có NDEBUG)contract_assume(COND, "message")
- Sử dụng hành vi không xác định thông quaunreachable()
để tối ưu hóa- Điều kiện tiên quyết:
require: (condition)
- Được kiểm tra khi vào hàm - Điều kiện hậu: Được kiểm tra khi hàm trả về
Cộng đồng Chia rẽ về Triết lý Phát triển Ngôn ngữ
Đề xuất này đã phơi bày sự chia rẽ cơ bản trong cộng đồng lập trình C về hướng phát triển tương lai của ngôn ngữ. Một số nhà phát triển cho rằng C phải phát triển để duy trì tính liên quan trong thời đại mà các ngôn ngữ an toàn bộ nhớ như Rust đang trở nên phổ biến. Họ chỉ ra rằng các ngôn ngữ khác đã phát triển thành công trong khi vẫn duy trì bản sắc cốt lõi của mình.
Tuy nhiên, một phần đáng kể của cộng đồng phản đối mạnh mẽ những thay đổi lớn đối với C. Những nhà phát triển này tin rằng sức mạnh của C nằm ở tính đơn giản và độ tin cậy, cho rằng ngôn ngữ này nên tập trung vào việc làm rõ hành vi không xác định hiện có và cải thiện tài liệu thay vì thêm các tính năng mới. Họ lo ngại rằng sự phức tạp gia tăng có thể biến C thành một ngôn ngữ khó sử dụng khác như C++ hiện đại.
Mối quan ngại của cộng đồng:
- An toàn: Hành vi không xác định khi hợp đồng thất bại so với xử lý lỗi có thể dự đoán được
- Độ phức tạp: Rủi ro ngôn ngữ C trở nên phức tạp như C++ hiện đại
- Triển khai: Chỉ có 3 trình biên dịch C++ chất lượng so với hàng trăm trình biên dịch C
- Triết lý: Sự phát triển của ngôn ngữ so với việc duy trì tính đơn giản truyền thống của C
Mối quan ngại Kỹ thuật về Tính An toàn Triển khai
Hệ thống contract được đề xuất phụ thuộc nhiều vào hành vi không xác định thông qua macro unreachable()
mới của C23. Khi một giả định contract thất bại, nó kích hoạt hành vi không xác định, cho phép trình biên dịch tối ưu hóa mã nhưng tạo ra rủi ro bảo mật tiềm ẩn. Những người chỉ trích cho rằng cách tiếp cận này thay thế việc xử lý lỗi có thể dự đoán bằng sự hỏng hóc chương trình không thể dự đoán.
Việc triển khai kỹ thuật cũng đặt ra câu hỏi về tính hữu dụng thực tế. Không giống như các ngôn ngữ như Ada hoặc Eiffel có thể chứng minh contract tại thời điểm biên dịch, đề xuất C dường như tập trung nhiều hơn vào việc kiểm tra thời gian chạy và gợi ý tối ưu hóa. Một số nhà phát triển đặt câu hỏi liệu điều này có mang lại đủ lợi ích để biện minh cho sự phức tạp gia tăng hay không.
Các tính năng bắt buộc của C23/Tương lai:
- Câu lệnh
defer
vớidefer_return_value
cho các điều kiện hậu - Toán tử
typeof
để sao chép nguyên mẫu hàm - Macro
unreachable()
để tối ưu hóa hành vi không xác định - Hỗ trợ inline hàm để đặt hợp đồng
Cuộc tranh luận về Panic so với Xử lý Lỗi
Một điểm tranh cãi chính tập trung vào triết lý xử lý lỗi. Hệ thống contract giới thiệu hành vi giống panic vào C, trong đó các vi phạm contract gây ra việc chấm dứt chương trình ngay lập tức thay vì trả về mã lỗi. Những người chỉ trích cho rằng điều này loại bỏ quyền kiểm soát khỏi các nhà phát triển có thể muốn xử lý lỗi một cách nhẹ nhàng.
Bây giờ bạn đã giới thiệu panic vào C. Và panic là xấu. Panic là những quả mìn chỉ chờ đợi một số hoàn cảnh không may để làm crash ứng dụng của bạn một cách bất ngờ, điều mà bạn không thể kiểm soát vì quyền kiểm soát xử lý lỗi giờ đã bị tước khỏi bạn.
Những người ủng hộ phản bác rằng việc crash gần nguồn gốc của lỗi với thông báo rõ ràng tốt hơn so với các crash hành vi không xác định bí ẩn xảy ra muộn hơn nhiều trong quá trình thực thi.
Các Giải pháp Thay thế và Công cụ Hiện có
Một số thành viên cộng đồng đã chỉ ra các giải pháp hiện có giải quyết nhu cầu tương tự mà không cần thay đổi ngôn ngữ. Một số gợi ý rằng các macro hygenic được thêm vào header assert.h hiện có có thể cung cấp hầu hết chức năng mong muốn. Những người khác tham chiếu đến các công cụ như tính năng bounds-safety của Clang hoặc các ngôn ngữ như Ada/SPARK đã có hệ thống contract trưởng thành.
Cuộc thảo luận cũng đã làm nổi bật rằng Digital Mars C++ đã bao gồm contract từ đầu những năm 1990, đặt ra câu hỏi về lý do tại sao các tính năng như vậy không được áp dụng rộng rãi hơn trong hệ sinh thái C/C++ mặc dù đã có sẵn trong nhiều thập kỷ.
Cuộc tranh luận phản ánh những căng thẳng rộng lớn hơn trong lập trình hệ thống giữa việc duy trì tính đơn giản truyền thống của C và thích ứng với các yêu cầu an toàn hiện đại. Khi tính an toàn bộ nhớ trở nên ngày càng quan trọng đối với bảo mật, cộng đồng C phải đối mặt với những quyết định khó khăn về mức độ thay đổi mà ngôn ngữ có thể hấp thụ trong khi vẫn trung thành với triết lý thiết kế ban đầu.
Tham khảo: Contracts for C