Chà, tôi khá ngạc nhiên khi các lựa chọn thay thế cho cú pháp này chưa được đề cập. Một cơ chế phổ biến khác (nhưng cũ hơn) là gọi một hàm không được xác định và dựa vào trình tối ưu hóa để biên dịch lệnh gọi hàm nếu khẳng định của bạn là đúng.
#define MY_COMPILETIME_ASSERT(test) \
do { \
extern void you_did_something_bad(void); \
if (!(test)) \
you_did_something_bad(void); \
} while (0)
Mặc dù cơ chế này hoạt động (miễn là bật tối ưu hóa), nó có nhược điểm là không báo cáo lỗi cho đến khi bạn liên kết, tại thời điểm đó, nó không tìm thấy định nghĩa cho hàm you_did_s Something_bad (). Đó là lý do tại sao các nhà phát triển nhân bắt đầu sử dụng các thủ thuật như độ rộng trường bit có kích thước âm và các mảng có kích thước âm (sau này đã ngừng phá vỡ các bản dựng trong GCC 4.4).
Để thông cảm cho nhu cầu xác nhận thời gian biên dịch, GCC 4.3 đã giới thiệu error
thuộc tính hàm cho phép bạn mở rộng khái niệm cũ hơn này, nhưng tạo ra lỗi thời gian biên dịch với thông báo bạn chọn - không còn là mảng có kích thước âm "khó hiểu" " thông báo lỗi!
#define MAKE_SURE_THIS_IS_FIVE(number) \
do { \
extern void this_isnt_five(void) __attribute__((error( \
"I asked for five and you gave me " #number))); \
if ((number) != 5) \
this_isnt_five(); \
} while (0)
Trên thực tế, kể từ Linux 3.9, giờ đây chúng ta có một macro được gọi là compiletime_assert
sử dụng tính năng này và hầu hết các macro trong bug.h
đã được cập nhật tương ứng. Tuy nhiên, macro này không thể được sử dụng như một trình khởi tạo. Tuy nhiên, bằng cách sử dụng bằng biểu thức câu lệnh (một phần mở rộng C GCC khác), bạn có thể!
#define ANY_NUMBER_BUT_FIVE(number) \
({ \
typeof(number) n = (number); \
extern void this_number_is_five(void) __attribute__(( \
error("I told you not to give me a five!"))); \
if (n == 5) \
this_number_is_five(); \
n; \
})
Macro này sẽ đánh giá tham số của nó chính xác một lần (trong trường hợp nó có tác dụng phụ) và tạo ra lỗi thời gian biên dịch có nội dung "Tôi đã nói với bạn rằng đừng cho tôi năm!" nếu biểu thức ước lượng đến năm hoặc không phải là hằng số thời gian biên dịch.
Vậy tại sao chúng ta không sử dụng cái này thay vì các trường bit có kích thước âm? Than ôi, hiện tại có nhiều hạn chế trong việc sử dụng biểu thức câu lệnh, bao gồm cả việc sử dụng chúng làm bộ khởi tạo không đổi (đối với hằng số enum, độ rộng trường bit, v.v.) ngay cả khi biểu thức câu lệnh hoàn toàn không đổi (nghĩa là có thể được đánh giá đầy đủ tại thời gian biên dịch và mặt khác vượt qua__builtin_constant_p()
bài kiểm tra). Hơn nữa, chúng không thể được sử dụng bên ngoài cơ thể chức năng.
Hy vọng, GCC sẽ sớm sửa đổi những thiếu sót này và cho phép các biểu thức câu lệnh không đổi được sử dụng làm công cụ khởi tạo liên tục. Thách thức ở đây là đặc tả ngôn ngữ xác định biểu thức hằng số pháp lý là gì. C ++ 11 đã thêm từ khóa constexpr cho loại hoặc điều này, nhưng không có đối tác tồn tại trong C11. Mặc dù C11 đã nhận được các xác nhận tĩnh, sẽ giải quyết một phần của vấn đề này, nhưng nó sẽ không giải quyết được tất cả những thiếu sót này. Vì vậy, tôi hy vọng rằng gcc có thể cung cấp chức năng constexpr dưới dạng tiện ích mở rộng thông qua -std = gnuc99 & -std = gnuc11 hoặc một số như vậy và cho phép sử dụng nó trên biểu thức câu lệnh et. al.