2013-03-22 47 views
11

Tôi đang thử nghiệm khả năng hiển thị biểu tượng C++ trên Linux và gcc. Dường như cách ưa thích là sử dụng -fvisibility = hidden và xuất các biểu tượng được sử dụng từng cái một theo trang wiki có khả năng hiển thị gcc (http://gcc.gnu.org/wiki/Visibility). Vấn đề của tôi là nhiều thư viện không xử lý tốt điều này, họ quên xuất biểu tượng rõ ràng, đây là một vấn đề nghiêm trọng. Sau một số lỗi cố định, ngay cả một số phần của tăng có thể vẫn bị ảnh hưởng. Tất nhiên những lỗi đó nên được sửa, nhưng cho đến khi tôi muốn sử dụng một cách "an toàn" để ẩn càng nhiều càng tốt các biểu tượng càng tốt.Khả năng hiển thị biểu tượng và không gian tên

Tôi đã đưa ra một giải pháp: Tôi đặt tất cả các ký hiệu trong không gian tên và tôi sử dụng thuộc tính ẩn biểu tượng trên đó và xuất giao diện công khai, theo cách này chỉ có biểu tượng của tôi mới bị ảnh hưởng.

Vấn đề là tôi nhận được thông báo cảnh báo khi tôi biên dịch nội dung nào đó đối với thư viện đó cho mọi lớp mà tôi chưa xuất và tôi sử dụng trong ứng dụng làm trường lớp.

namespace MyDSO __attribute__ ((visibility ("hidden"))) { 
    struct Foo { 
    void bar() __attribute__ ((visibility ("default"))) {} 
    }; 
} 

struct Bar { 
    MyDSO::Foo foo; 
}; 

int main() {} 

Thông báo cảnh báo có thể được sao chép trong ví dụ nhỏ này, nhưng tất nhiên không gian tên phải nằm trong thư viện lớp khác trong ứng dụng.

$ gcc-4.7.1 namespace.cpp -o namespace 
namespace.cpp:7:8: warning: ‘Bar’ declared with greater visibility than the type of its field ‘Bar::foo’ [-Wattributes] 

Như tôi hiểu tầm nhìn biểu tượng, ẩn namespace nên có hiệu lực thi hành khá giống với sử dụng -fvisibility = ẩn, nhưng tôi không bao giờ nhận được những cảnh báo tương tự sử dụng sau này. Tôi thấy rằng khi tôi vượt qua -fvisibility = ẩn cho ứng dụng, lớp trong ứng dụng cũng sẽ bị ẩn, vì vậy tôi sẽ không nhận được cảnh báo. Nhưng khi tôi không vượt qua tùy chọn, không có biểu tượng nào trong các tiêu đề dường như bị ẩn với trình biên dịch, vì vậy tôi sẽ không nhận được cảnh báo nữa.

Đề xuất của thông báo cảnh báo này là gì? Đây có phải là vấn đề nghiêm trọng không? Trong trường hợp nào điều này có thể gây ra bất kỳ vấn đề nào? Làm thế nào ẩn không gian tên là khác nhau để fvisibility = ẩn?

Trả lời

16

Trước khi tôi trả lời câu hỏi cụ thể của bạn, tôi nên đề cập đến những người khác đang đọc áp dụng thuộc tính hiển thị biểu tượng cho mỗi không gian tên là một tính năng cụ thể của GCC. MSVC chỉ hỗ trợ dllexport trên các lớp, các hàm và các biến, và nếu bạn muốn mã của bạn được di chuyển, bạn phải phù hợp với MSVC ở đó. Như hướng dẫn hiển thị biểu tượng GCC ban đầu của tôi, bạn có thể dễ dàng sử dụng lại máy móc dllexport dựa trên macro của MSVC để đạt được điều gì đó tương tự trên GCC, vì vậy việc chuyển sang MSVC sẽ giúp bạn xử lý khả năng hiển thị biểu tượng " ".

Về vấn đề cụ thể của bạn, GCC là chính xác để cảnh báo bạn. Nếu một người dùng bên ngoài đã cố gắng sử dụng loại Bar công khai, họ gần như chắc chắn cần phải sử dụng mọi thứ bên trong Bar, bao gồm Bar :: foo. Đối với chính xác cùng một lý do tất cả các chức năng thành viên tư nhân, mặc dù được tư nhân, cần phải được nhìn thấy. Rất nhiều người ngạc nhiên về điều này, lý do là biểu tượng chức năng thành viên riêng tư không thể truy cập được với bất kỳ ai, nhưng họ quên rằng chỉ vì người lập trình không có quyền truy cập không có nghĩa là trình biên dịch không cần truy cập. Nói cách khác, các hàm thành viên riêng tư là riêng tư cho bạn, nhưng không phải là trình biên dịch. Nếu chúng xuất hiện trong một tệp tiêu đề, điều đó thường có nghĩa là trình biên dịch cần quyền truy cập, ngay cả trong một không gian tên ẩn danh (mà chỉ ẩn danh với các lập trình viên, chứ không phải các trình biên dịch có xu hướng sử dụng hàm băm của nội dung là tên không gian tên "thực").

Ẩn một không gian tên có hiệu ứng rất khác với -fvisibility = ẩn. Điều này là do GCC phun ra nhiều biểu tượng ở trên và vượt ra ngoài các biểu tượng đó cho một loại cụ thể, ví dụ: cho vtables, cho type_info vv -fvisibility = ẩn giấu nội dung bạn không thể ẩn bởi bất kỳ trình biên dịch nào được hướng dẫn, và nó hoàn toàn cần thiết để tải hai tệp nhị phân vào cùng một quá trình với các ký hiệu va chạm, ví dụ:hai đối tượng dùng chung được tạo bằng các phiên bản Boost khác nhau.

Tôi đánh giá cao nỗ lực của bạn để khắc phục các sự cố gây ra bởi khả năng hiển thị biểu tượng bị hỏng trong ELF và hậu quả đối với các tệp nhị phân C++ bị hỏng và năng suất lập trình bị mất nhiều. Tuy nhiên bạn không thể sửa chúng - chúng là lỗi trong bản thân ELF, được thiết kế cho C và không phải C++. Nếu có bất kỳ sự an ủi nào, tôi đã viết một tờ giấy trắng BlackBerry cách đây vài tháng về vấn đề hiển thị biểu tượng ELF cũng giống như chúng tôi trong BB10 vì chúng dành cho bất kỳ tập đoàn lớn nào với một codebase C++ đáng kể. Vì vậy, có lẽ bạn có thể thấy một số giải pháp được đề xuất cho C++ 17, đặc biệt là nếu việc triển khai Mô-đun C++ của Doug Gregor giúp tiến bộ tốt.

+0

Cảm ơn câu trả lời chi tiết của bạn. Chỉ vì tò mò, tôi quan tâm đến những gì các trình biên dịch cần trình biên dịch mà không thể được tạo ra? Theo như tôi biết trong các lớp tầm thường, ngay cả typeinfo có thể được tạo ra. Khi sử dụng vis = hidden, bạn sẽ không nhận được cảnh báo ngay cả khi bạn ẩn các biểu tượng không được ẩn, bạn chỉ nhận được lỗi biểu tượng không xác định từ trình liên kết. Sử dụng gcc không gian tên ẩn có thể phát hiện sự cố. Có thể có sử dụng hợp pháp để chỉ xuất một số biểu tượng trong một lớp, nhưng gcc phát ra cảnh báo. Mô-đun C++ của Doug Gregor rất thú vị, tôi thích bài thuyết trình của anh ấy, cảm ơn bạn đã chia sẻ nó. – VargaD

+1

Trong ngắn hạn, typeinfo cho bất kỳ lớp nào với một virtual luôn luôn được phát ra, trong khi typeinfo cho các lớp không có chỉ được phát ra khi typeid() hoặc một cái gì đó sử dụng nó (exception catches, dynamic_cast <> etc) được sử dụng. Ngoài ra, hầu hết các trình biên dịch phát ra hai hoặc nhiều hơn các hàm tạo của hàm xây dựng cho mỗi hàm dựng sẵn của chương trình, và có một số phép thuật trong thế hệ phá hủy nữa. Trong ngắn hạn, -fvisibility = ẩn giấu rất nhiều, và phương pháp của bạn sẽ chỉ ẩn các công cụ lập trình cụ thể và không phải là ma thuật internals. Phần lớn điều này bắt đầu trở nên dễ dàng hơn với các Mô-đun C++, mặc dù không được giải quyết. Niall –

0

Việc bạn sử dụng thuộc tính hiển thị có vẻ ngược lại với tôi; Tôi nghĩ rằng bạn sẽ có kết quả tốt hơn bằng cách sử dụng -fvisibility = hidden và thêm khả năng hiển thị "mặc định" vào không gian tên khai báo thư viện, vì giao diện của thư viện có thể hiển thị mặc định hoặc bạn không thể sử dụng nó từ ứng dụng của mình. Nếu bạn không muốn sửa đổi tiêu đề thư viện, bạn có thể sử dụng khả năng hiển thị #pragma GCC đẩy/bật xung quanh #includes của bạn. Ngoài ra, như Niall nói, đánh dấu các chức năng thành viên cá nhân như mặc định không hoạt động, toàn bộ loại Foo cần phải có khả năng hiển thị mặc định nếu nó là một phần của giao diện của thư viện.

+0

Cảm ơn bạn đã trả lời. Tôi không thấy _why_ cả lớp cần được xuất. Hầu hết các lớp học thậm chí không có vtable. Tôi rất nhiều ứng dụng thử nghiệm, thậm chí đã cố gắng để ném không xuất ngoại lệ, nhưng tất cả chúng hoạt động khá tốt. Tôi cố gắng hiểu tại sao nó cần được xuất khẩu. Bạn có biết trong trường hợp nào nó không thành công? Bạn có thể cho thấy một ví dụ? – VargaD

+0

Vấn đề cơ bản là trong C++, các lớp có liên kết; nếu một lớp có khả năng hiển thị ẩn, điều đó có nghĩa là bạn không định sử dụng nó bên ngoài DSO của riêng nó. Và do đó, việc đưa ra một chức năng thành viên khả năng hiển thị lớn hơn lớp của nó không có ý nghĩa, vì bạn cần sử dụng lớp đó để sử dụng hàm thành viên. Tại sao bạn không muốn xuất bản thân lớp đó? –

+0

Vấn đề của tôi là việc xuất toàn bộ lớp không thể duy trì được, các biểu tượng dễ dàng được xuất. Trong C sử dụng -fvisibility-hidden bạn chỉ cần xuất khẩu biểu tượng bạn muốn xuất, nhưng trong C++ bạn phải xuất toàn bộ lớp và khó ẩn tất cả các biểu tượng bạn không muốn xuất. Tôi thấy rằng các lớp học có vtable phải được xuất khẩu. Tôi đã đọc nhiều lần rằng trong khả năng hiển thị biểu tượng C++ có thể gây ra rất nhiều vấn đề, và tôi không nên ẩn các lớp đã xuất các phương thức, nhưng tôi không thể thấy tại sao. Tôi đã cố gắng để tạo ra lỗi thời gian chạy vì khả năng hiển thị, nhưng tôi không thể. – VargaD