Lợi ích của thư viện chỉ tiêu đề là gì và tại sao bạn lại viết nó theo cách đó phản đối việc đưa triển khai vào tệp riêng biệt?
Lợi ích của thư viện chỉ tiêu đề là gì và tại sao bạn lại viết nó theo cách đó phản đối việc đưa triển khai vào tệp riêng biệt?
Câu trả lời:
Có những tình huống khi thư viện chỉ có tiêu đề là lựa chọn duy nhất, chẳng hạn như khi xử lý các mẫu.
Có thư viện chỉ dành cho tiêu đề cũng có nghĩa là bạn không phải lo lắng về các nền tảng khác nhau nơi thư viện có thể được sử dụng. Khi bạn tách việc triển khai, bạn thường làm như vậy để ẩn chi tiết triển khai và phân phối thư viện dưới dạng kết hợp của tiêu đề và thư viện ( lib
, dll
hoặc .so
tệp). Tất nhiên, chúng phải được biên dịch cho tất cả các hệ điều hành / phiên bản khác nhau mà bạn cung cấp hỗ trợ.
Bạn cũng có thể phân phối các tệp triển khai, nhưng điều đó có nghĩa là một bước bổ sung cho người dùng - biên dịch thư viện của bạn trước khi sử dụng nó.
Tất nhiên, điều này áp dụng cho từng trường hợp cụ thể. Ví dụ: các thư viện chỉ dành cho tiêu đề đôi khi tăngkích thước mã & thời gian biên dịch.
Lợi ích của thư viện chỉ dành cho tiêu đề:
Nhược điểm của thư viện chỉ có tiêu đề:
Tệp đối tượng lớn hơn. Mọi phương thức nội tuyến từ thư viện được sử dụng trong một số tệp nguồn cũng sẽ nhận được một ký hiệu yếu, định nghĩa ngoài dòng trong tệp đối tượng đã biên dịch cho tệp nguồn đó. Điều này làm chậm trình biên dịch và cũng làm chậm trình liên kết. Trình biên dịch phải tạo ra tất cả những thứ đó, và sau đó trình liên kết phải lọc nó ra.
Biên dịch dài hơn. Ngoài vấn đề cồng kềnh được đề cập ở trên, quá trình biên dịch sẽ mất nhiều thời gian hơn vì các tiêu đề vốn có dung lượng lớn hơn với thư viện chỉ dành cho tiêu đề so với thư viện đã biên dịch. Các tiêu đề lớn đó sẽ cần được phân tích cú pháp cho từng tệp nguồn sử dụng thư viện. Một yếu tố khác là các tệp tiêu đề đó trong thư viện chỉ dành cho #include
tiêu đề phải có các tiêu đề cần thiết bởi các định nghĩa nội tuyến cũng như các tiêu đề cần thiết nếu thư viện được xây dựng như một thư viện đã biên dịch.
Biên dịch rối hơn. Bạn nhận được nhiều phụ thuộc hơn với thư viện chỉ có tiêu đề vì những thứ bổ sung #include
đó cần thiết với thư viện chỉ có tiêu đề. Thay đổi việc triển khai một số chức năng chính trong thư viện và bạn có thể cần phải biên dịch lại toàn bộ dự án. Thực hiện thay đổi đó trong tệp nguồn cho một thư viện đã biên dịch và tất cả những gì bạn phải làm là biên dịch lại một tệp nguồn thư viện đó, cập nhật thư viện đã biên dịch với tệp .o mới đó và liên kết lại ứng dụng.
Khó hơn cho con người để đọc. Ngay cả khi có tài liệu tốt nhất, người sử dụng thư viện đôi khi phải dùng đến việc đọc các tiêu đề cho thư viện. Các tiêu đề trong thư viện chỉ dành cho tiêu đề chứa đầy các chi tiết triển khai cản trở việc hiểu giao diện. Với một thư viện đã biên dịch, tất cả những gì bạn thấy là giao diện và bình luận ngắn gọn về những gì việc triển khai thực hiện và đó thường là tất cả những gì bạn muốn. Đó thực sự là tất cả những gì bạn nên muốn. Bạn không cần phải biết chi tiết triển khai để biết cách sử dụng thư viện.
detail
.
Tôi biết đây là một chủ đề cũ, nhưng không ai đề cập đến giao diện ABI hoặc các vấn đề trình biên dịch cụ thể. Vì vậy, tôi nghĩ tôi sẽ làm.
Điều này về cơ bản dựa trên khái niệm về việc bạn viết một thư viện với tiêu đề để phân phối cho mọi người hoặc tự sử dụng lại so với có mọi thứ trong tiêu đề. Nếu bạn đang nghĩ đến việc sử dụng lại tiêu đề và tệp nguồn và biên dịch lại chúng trong mọi dự án thì điều này không thực sự áp dụng.
Về cơ bản, nếu bạn biên dịch mã C ++ của mình và xây dựng một thư viện với một trình biên dịch thì người dùng cố gắng sử dụng thư viện đó với một trình biên dịch khác hoặc một phiên bản khác của cùng một trình biên dịch thì bạn có thể gặp lỗi trình liên kết hoặc hành vi thời gian chạy lạ do không tương thích nhị phân.
Ví dụ, các nhà cung cấp trình biên dịch thường thay đổi việc triển khai STL giữa các phiên bản. Nếu bạn có một hàm trong thư viện chấp nhận một vectơ std :: thì nó mong đợi các byte trong lớp đó được sắp xếp theo cách chúng được sắp xếp khi thư viện được biên dịch. Nếu, trong phiên bản trình biên dịch mới, nhà cung cấp đã thực hiện các cải tiến về hiệu quả đối với std :: vector thì mã của người dùng sẽ thấy lớp mới có thể có cấu trúc khác và chuyển cấu trúc mới đó vào thư viện của bạn. Mọi thứ đi xuống dốc từ đó ... Đây là lý do tại sao không nên chuyển các đối tượng STL qua ranh giới thư viện. Điều tương tự cũng áp dụng cho các loại C Run-Time (CRT).
Trong khi nói về CRT, thư viện của bạn và mã nguồn của người dùng thường cần được liên kết với cùng một CRT. Với Visual Studio nếu bạn xây dựng thư viện của mình bằng CRT đa luồng, nhưng người dùng liên kết với CRT gỡ lỗi đa luồng thì bạn sẽ gặp sự cố liên kết vì thư viện của bạn có thể không tìm thấy các ký hiệu mà nó cần. Tôi không thể nhớ đó là chức năng nào, nhưng đối với Visual Studio 2015, Microsoft đã tạo một hàm CRT nội tuyến. Đột nhiên, nó nằm trong tiêu đề không phải là thư viện CRT nên các thư viện mong đợi tìm thấy nó tại thời điểm liên kết không thể thực hiện được nữa và điều này đã tạo ra lỗi liên kết. Kết quả là các thư viện này cần biên dịch lại với Visual Studio 2015.
Bạn cũng có thể gặp lỗi liên kết hoặc hành vi lạ nếu bạn sử dụng Windows API nhưng bạn xây dựng với các cài đặt Unicode khác nhau cho người dùng thư viện. Điều này là do Windows API có các hàm sử dụng chuỗi Unicode hoặc ASCII và macro / xác định tự động sử dụng đúng loại dựa trên cài đặt Unicode của dự án. Nếu bạn truyền một chuỗi qua ranh giới thư viện không đúng loại thì mọi thứ sẽ bị hỏng trong thời gian chạy. Hoặc bạn có thể thấy rằng chương trình không liên kết ngay từ đầu.
Những điều này cũng đúng khi chuyển các đối tượng / kiểu qua ranh giới thư viện từ các thư viện bên thứ ba khác (ví dụ: vectơ Eigen hoặc ma trận GSL). Nếu thư viện bên thứ 3 thay đổi tiêu đề của họ giữa bạn biên dịch thư viện và người dùng của bạn biên dịch mã của họ thì mọi thứ sẽ hỏng.
Về cơ bản để an toàn, những thứ duy nhất bạn có thể vượt qua ranh giới thư viện được xây dựng theo kiểu và Dữ liệu cũ thuần túy (POD). Lý tưởng nhất là bất kỳ POD nào cũng phải có cấu trúc được xác định trong tiêu đề của riêng bạn và không dựa vào bất kỳ tiêu đề nào của bên thứ ba.
Nếu bạn cung cấp thư viện chỉ tiêu đề thì tất cả mã sẽ được biên dịch với cùng cài đặt trình biên dịch và chống lại cùng tiêu đề, do đó rất nhiều vấn đề này sẽ biến mất (cung cấp phiên bản thư viện phần thứ ba mà bạn và người dùng của bạn sử dụng tương thích với API).
Tuy nhiên có những tiêu cực đã được đề cập ở trên, chẳng hạn như thời gian biên soạn tăng lên. Ngoài ra, bạn có thể đang điều hành một doanh nghiệp vì vậy bạn có thể không muốn giao tất cả các chi tiết triển khai mã nguồn của mình cho tất cả người dùng của bạn trong trường hợp một trong số họ lấy cắp nó.
"Lợi ích" chính là nó yêu cầu bạn cung cấp mã nguồn, vì vậy bạn sẽ kết thúc với các báo cáo lỗi trên máy và với các trình biên dịch mà bạn chưa từng nghe đến. Khi thư viện hoàn toàn là các mẫu, bạn không có nhiều lựa chọn, nhưng khi bạn có lựa chọn, chỉ tiêu đề thường là một lựa chọn kỹ thuật kém. (Mặt khác, tất nhiên, tiêu đề chỉ có nghĩa là bạn không phải ghi lại bất kỳ thủ tục tích hợp nào.)
Nội tuyến có thể được thực hiện bằng Tối ưu hóa thời gian liên kết (LTO)
Tôi muốn nhấn mạnh điều này vì nó làm giảm giá trị của một trong hai ưu điểm chính của thư viện chỉ tiêu đề: "bạn cần định nghĩa trên tiêu đề để nội dòng".
Một ví dụ cụ thể tối thiểu về điều này được hiển thị tại: Tối ưu hóa thời gian liên kết và nội tuyến
Vì vậy, bạn chỉ cần vượt qua một cờ và nội tuyến có thể được thực hiện trên các tệp đối tượng mà không cần bất kỳ công việc tái cấu trúc nào, không cần phải giữ các định nghĩa trong tiêu đề cho điều đó nữa.
Tuy nhiên, LTO cũng có thể có những nhược điểm riêng: Có lý do gì không sử dụng tối ưu hóa thời gian liên kết (LTO)?