Khi làm việc với STM32, có một từ khóa __weak mà khi chúng ta định nghĩa lại hàm được đánh dấu bởi __weak, chương trình sẽ gọi hàm mới này thay vì hàm gốc.
Tính năng này tương tự như việc định nghĩa lại trong C++ và cũng giống như khái niệm về weak symbol được mô tả trong cuốn sách "The Self-Cultivation of a Programmer".
Weak Symbol và Strong Symbol
-
- Trong C/C++, các hàm và biến toàn cục đã khởi tạo mặc định là strong symbol, trong khi các biến toàn cục chưa khởi tạo là weak symbol.
-
- Có thể chuyển đổi strong symbol thành weak symbol bằng cách sử dụng
__attribute__((weak)).
- Có thể chuyển đổi strong symbol thành weak symbol bằng cách sử dụng
Quy tắc 1: Không cho phép nhiều định nghĩa của strong symbol Quy tắc 2: Nếu một symbol là strong symbol trong một file mục tiêu và weak symbol trong các file khác, thì chọn strong symbol Quy tắc 3: Nếu một symbol là weak symbol trong tất cả các file, thì chọn symbol chiếm nhiều không gian nhất
// Không phải là strong symbol hay weak symbol, đây là tham chiếu đến biến bên ngoài
extern int ext;
// Weak symbol
int weak;
// Strong symbol
int strong = 1;
// Weak symbol (biến này không thể tồn tại cùng với biến toàn cục `int weak2 = 1;`)
int __attribute__((weak)) weak2 = 2;
// Strong symbol
int main()
{
return 0;
}
Weak Reference và Strong Reference
- Nếu không tìm thấy định nghĩa của strong reference, liên kết viên sẽ báo lỗi: "symbol not defined".
- Nếu weak reference không được định nghĩa, liên kết viên sẽ không báo lỗi, mặc định giá trị là 0 hoặc một giá trị đặc biệt, dễ dẫn đến lỗi khi chạy.
// Khi hàm main cố gắng gọi hàm foo, địa chỉ của foo là 0, gây ra lỗi truy cập địa chỉ bất hợp pháp
__attribute__((weakref)) void foo();
int main()
{
foo();
}
// Cách cải tiến
__attribute__((weakref)) void foo();
int main()
{
if (foo) foo();
}
- Các weak symbol được định nghĩa trong thư viện có thể bị ghi đè bởi strong symbol do người dùng định nghĩa, cho phép chương trình sử dụng phiên bản tùy chỉnh của hàm thư viện.
- Chương trình có thể định nghĩa các weak reference cho các module chức năng mở rộng. Khi module này được liên kết, nó sẽ hoạt động bình thường. Nếu bỏ bớt module, chương trình vẫn có thể liên kết, nhưng thiếu tính năng tương ứng, giúp dễ dàng cắt giảm và kết hợp tính năng.
Ví dụ từ sách
Trong thiết kế chương trình Linux, nếu một chương trình được thiết kế để hỗ trợ chế độ đơn luồng hoặc đa luồng, có thể sử dụng weak reference để xác định liệu chương trình đang được liên kết với thư viện Glibc đơn luồng hay đa luồng (có tùy chọn -lpthread khi biên dịch). Điều này giúp chương trình quyết định thực thi phiên bản đơn luồng hay đa luồng. Chúng ta có thể định nghĩa một weak reference cho hàm pthread_create và sau đó chương trình sẽ xác định động xem có liên kết với thư viện pthread hay không, từ đó quyết định thực thi phiên bản nào.