Dưới đây là những gì tôi đang cố gắng để làm:Làm thế nào để thực hiện một khuôn khổ bộ chuyển đổi trong C++ mà làm việc trong cả Linux và Windows
Tôi đang phát triển một IDE cross-platform (Linux và Windows) có hỗ trợ plug-ins. Tôi cần hỗ trợ khả năng mở rộng bằng cách sử dụng một khuôn khổ bộ điều hợp tương tự như khung công tác mà Eclipse cung cấp. Xem here để biết thêm chi tiết, nhưng về cơ bản tôi cần những điều sau đây:
Hãy để Adaptee
và Adapted
là các lớp hoàn toàn không có liên quan và chúng tôi không được phép thay đổi theo bất kỳ cách nào. Tôi muốn tạo ra một lớp AdapterManager
trong đó có một phương pháp
template <class Adaptee, class Adapted> Adapted* adapt(Adaptee* object);
mà sẽ tạo ra một thể hiện của Adapted
cho một thể hiện của Adaptee
. Làm thế nào chính xác dụ được tạo ra phụ thuộc vào một chức năng bộ chuyển đổi mà sẽ phải được đăng ký với AdapterManager
. Mỗi trình cắm thêm mới có thể đóng góp các chức năng của bộ điều hợp cho các loại tùy ý.
Dưới đây là suy nghĩ của tôi về một giải pháp khả thi và tại sao nó không hoạt động:
chức năng RTTIC++ 11 và lớp
type_info
cung cấp một phương pháphash_code()
mà trả về một số nguyên duy nhất đối với từng loại trong chương trình. Xem here. Do đóAdapterManager
có thể chỉ đơn giản chứa một bản đồ cho mã băm cho các lớp Adaptee và Adapter trả về một con trỏ hàm tới hàm bộ điều hợp. Điều này làm cho việc thực hiện các chức năngadapt()
trên tầm thường:template <class Adaptee, class Adapted> Adapted* AdapterManager::adapt(Adaptee* object) { AdapterMapKey mk(typeid(Adapted).hash_code(), typeid(Adaptee).hash_code()); AdapterFunction af = adapterMap.get(mk); if (!af) return nullptr; return (Adapted*) af(object); }
Bất kỳ plug-in có thể dễ dàng mở rộng khuôn khổ bằng cách đơn giản chèn một chức năng bổ sung vào bản đồ. Cũng lưu ý rằng bất kỳ trình cắm thêm nào cũng có thể cố gắng điều chỉnh bất kỳ lớp nào cho bất kỳ lớp nào khác và thành công nếu có tồn tại một hàm bộ điều hợp tương ứng được đăng ký với
AdapterManager
bất kể ai đã đăng ký nó.- Một vấn đề với điều này là sự kết hợp giữa các mẫu và trình cắm thêm (đối tượng được chia sẻ/DLL). Vì hai trình cắm thêm có thể khởi tạo một lớp mẫu với các tham số giống nhau, điều này có khả năng dẫn đến hai trường hợp riêng biệt của các cấu trúc
type_info
tương ứng và các kết quả có khả năng khác nhau làhash_code()
. Các chức năng bộ điều hợp được đăng ký từ một trình cắm thêm có thể không phải lúc nào cũng hoạt động trong một trình cắm thêm khác. - Trong Linux, trình liên kết động dường như có thể xử lý nhiều khai báo loại trong các thư viện được chia sẻ khác nhau theo một số điều kiện theo this (điểm 4.2). Tuy nhiên vấn đề thực sự là trong Windows, nơi mà dường như mỗi DLL sẽ nhận được phiên bản riêng của một mẫu instantiation bất kể nó cũng được định nghĩa trong các DLL được nạp khác hoặc thực thi chính. Trình liên kết động dường như không linh hoạt so với trình liên kết được sử dụng trong Linux.
- Tôi đã xem xét sử dụng các mẫu tức thì rõ ràng có vẻ như giảm sự cố, nhưng vẫn không giải quyết được vì hai trình cắm khác nhau vẫn có thể khởi tạo cùng một mẫu theo cùng một cách.
Câu hỏi:
- Có ai biết một cách để đạt được điều này trong Windows? Nếu bạn được phép sửa đổi các lớp hiện có, liệu điều này có giúp được không?
- Bạn có biết cách tiếp cận khác để đạt được chức năng này trong C++, trong khi vẫn bảo toàn tất cả các thuộc tính mong muốn: không thay đổi các lớp hiện có, làm việc với mẫu, hỗ trợ trình cắm và nền tảng không?
Cập nhật 1:
dự án này sử dụng khung Qt cho nhiều thứ bao gồm cả các plug-in cơ sở hạ tầng. Qt thực sự giúp phát triển nền tảng chéo. Nếu bạn biết về một giải pháp cụ thể cho vấn đề Qt, điều đó cũng được hoan nghênh.
Cập nhật 2:
bình luận n.m. đã khiến tôi nhận ra rằng tôi chỉ biết về các vấn đề trong lý thuyết và chưa thực sự được thử nghiệm nó. Vì vậy, tôi đã làm một số thử nghiệm trong cả Windows và Linux bằng cách sử dụng định nghĩa sau đây:
template <class T>
class TypeIdTest {
public:
virtual ~TypeIdTest() {};
static int data;
};
template <class T> int TypeIdTest<T>::data;
Lớp này được khởi tạo trong hai chia sẻ thư viện/DLL khác nhau với T = int. Cả hai thư viện được tải một cách rõ ràng vào thời gian chạy. Dưới đây là những gì tôi tìm thấy:
Trong Linux tất cả mọi thứ chỉ hoạt động:
- Hai instantiations sử dụng cùng một vtable.
- Đối tượng được trả về bởi
typeid
có cùng địa chỉ. - Ngay cả thành viên dữ liệu tĩnh cũng giống nhau.
- Vì vậy, thực tế là mẫu được khởi tạo trong nhiều thư viện được tải động được tạo hoàn toàn không có sự khác biệt. Trình liên kết dường như đơn giản sử dụng instantiation được nạp đầu tiên và bỏ qua phần còn lại.
Trong của Windows hai instantiations là 'hơi' riêng biệt:
- Các
typeid
cho các trường hợp khác nhau trảtype_info
đối tượng tại địa chỉ khác nhau. Tuy nhiên, các vật thể này bằng nhau khi được thử nghiệm với==
. Các mã băm tương ứng cũng bằng nhau. Có vẻ như trên Windows bình đẳng giữa các loại được thiết lập bằng cách sử dụng tên của loại - có ý nghĩa. Càng xa càng tốt. - Tuy nhiên, vtables cho hai trường hợp là khác nhau. Tôi không chắc có bao nhiêu vấn đề. Trong các thử nghiệm của tôi, tôi đã có thể sử dụng
dynamic_cast
để downcast một thể hiện củaTypeIdTest
thành một loại có nguồn gốc qua các ranh giới thư viện được chia sẻ. - Vấn đề là mỗi lần sử dụng bản sao của trường tĩnh là
data
. Điều đó có thể gây ra rất nhiều vấn đề và về cơ bản không cho phép các trường tĩnh trong các lớp mẫu.
Nhìn chung, dường như ngay cả trong Windows mọi thứ không tồi tệ như tôi nghĩ, nhưng tôi vẫn miễn cưỡng trong việc sử dụng phương pháp này cho rằng mẫu instantiations vẫn sử dụng riêng biệt vtables và lưu trữ tĩnh. Có ai biết cách tránh vấn đề này không? Tôi không tìm thấy giải pháp nào.
Tôi nghĩ bạn đang cố giải quyết vấn đề thời gian chạy lúc biên dịch. Tôi nghĩ rằng trong trường hợp này thừa kế và các chức năng ảo sẽ thẳng thắn hơn và ít bị lỗi hơn các mẫu (vì bạn đã lưu ý có rất nhiều phép thuật đang diễn ra trong những gì bạn đang cố gắng làm). –
Tôi nghĩ rằng bản chất của vấn đề là không có cách nào để có được bàn tay của tôi trên một định danh kiểu duy nhất (ngay cả trong thời gian chạy) mà sẽ làm việc trên các biên giới plug-in. Ít nhất đó dường như là trường hợp trong Windows. Tôi không chắc chắn rằng các thừa kế và các chức năng ảo sẽ hoạt động vì mỗi trình cắm thêm có thể đóng góp các lớp mới mà cũng nên có khả năng thích nghi tùy ý. Bạn có một ý tưởng về cách nó có thể được thực hiện? –
Bạn không thể điều chỉnh các loại arbirtrary tại thời gian biên dịch từ một plugin vì bạn cần tệp tiêu đề của plugin được cho biết. Điều đó đánh bại mục đích của các plugin, bạn chỉ có thể biên dịch mọi thứ cùng một lúc. Rõ ràng là bạn không thể điều chỉnh các kiểu arbirtrary khi chạy trong C++. Mặt khác, bạn có thể điều chỉnh các kiểu tùy ý có cùng một lớp cơ sở. Nếu điều này là không đủ, thì mỗi plugin phải cung cấp chức năng đăng ký có thể được gọi vào đầu chương trình sẽ thực hiện việc đăng ký tất cả các loại thích ứng và chức năng thích ứng tương ứng của chúng. –