2010-02-07 72 views
7

Có một mẫu mà tôi có thể sử dụng để gọi các yêu cầu khởi tạo và thói quen dọn dẹp của thư viện cơ bản (C) không? Trong trường hợp của tôi, tôi muốn tạo lớp bao bọc để nó có thể được sáng tác thành các đối tượng khác. Vấn đề là, khi tôi phá hủy lớp bao bọc, các thói quen dọn dẹp của thư viện cơ bản được gọi. Đó là tốt cho đến khi tôi nhanh chóng nhiều đối tượng của lớp wrapper của tôi. Câu hỏi của tôi là cách cách tốt nhất để thực sự xử lý tình huống này là gì? Một truy cập tham chiếu tĩnh đến với tâm trí, nhưng tôi muốn biết nếu có những lựa chọn khác có khả năng tốt hơn và các ngành nghề có liên quan.Mẫu chung cho khởi tạo và tắt thư viện?

+1

Đó không phải là ý tưởng tuyệt vời, nhưng mẫu Singleton có giúp ích không? – batbrat

+1

Bạn chỉ cần tìm mẫu được sử dụng bởi tiêu chuẩn :: ios_base :: Init? Nó khởi tạo một thư viện C++ (iostreams), nhưng cùng có thể làm việc cho bạn. –

+0

Số lần tính toán tĩnh riêng tư đối với lớp trình bao bọc của bạn giống như một giải pháp tốt cho tôi. – munificent

Trả lời

4

Không phải mọi thứ đều phải là một lớp học. Các mô hình Singleton sẽ cho phép bạn tắt chức năng này vào một lớp học, nhưng nó thực sự không mua bất cứ thứ gì trên chức năng toàn cầu:

bool my_library_init(); 
void my_library_shutdown(); 

Cuộc gọi đầu tiên trả về true nếu thư viện được khởi tạo thành công, thứ hai chỉ lặng lẽ không bất cứ điều gì cần được thực hiện và thoát. Bạn có thể thêm bất kỳ công cụ đếm tham chiếu hoặc loại theo dõi chủ đề nào sau các giao diện mà bạn muốn.

Ngoài ra, đừng bỏ qua khả năng thư viện của bạn có thể thực hiện tất cả điều này một cách minh bạch. Khi chức năng thư viện đầu tiên được gọi, nó có thể phát hiện ra rằng nó chưa được khởi tạo và thiết lập mọi thứ trước khi thực hiện công việc? Để tắt máy, chỉ cần đăng ký các tài nguyên bị phá hủy với một đối tượng chung, vì vậy chúng bị phá hủy khi chương trình thoát. Làm theo cách này chắc chắn là phức tạp hơn, nhưng có thể đáng được hưởng lợi ích cho người gọi thư viện của bạn.

+0

Nếu tôi hiểu chính xác, điều này sẽ hiệu quả nếu tôi có thể thay đổi thư viện cơ bản. Trong trường hợp này, tôi không thể. – jdt141

+0

Không hề. Viết hai hàm toàn cục này, sau đó gọi chúng. Có thể thư viện C bên dưới của bạn cung cấp các hàm như vậy. Chủ yếu, tôi đang tranh luận chống lại cái đầu gối-giật "mọi thứ phải là một lớp học" suy nghĩ. Tôi là tất cả cho OOP, nơi nó có ý nghĩa, nhưng tôi không thấy ý nghĩa ở đây. –

+0

Công việc tôi đang làm là thư viện. Tôi là khách hàng đến tài nguyên được chia sẻ được đề cập. Tôi muốn tóm tắt các cuộc gọi cụ thể của thư viện cơ bản từ khách hàng của tôi. Nó sẽ là thực tế xấu để không hoàn toàn khởi tạo đối tượng tôi đang tạo trong ctor (khởi tạo một phần). Điều gì sẽ xảy ra nếu máy khách quên gọi các cuộc gọi khởi tạo hoặc tắt máy? Sau đó, bạn sẽ (có thể) có được hành vi không xác định - không phải là một điều tốt. – jdt141

1

Nếu khởi tạo có thể được gọi trước khi bắt đầu chính, và dọn dẹp gọi là sau khi kết thúc chính, thủ thuật này rất ít (? Hack) có thể làm việc cho bạn:

#include <iostream> 

// C library initialization routine 
void init() {std::cout << "init\n";} 

// C library cleanup routine 
void fini() {std::cout << "fini\n";} 

// Put this in only one of your *.cpp files 
namespace // anonymous 
{ 
    struct Cleaner 
    { 
     Cleaner() {init();} 
     ~Cleaner() {fini();} 
    }; 
    Cleaner cleaner; 
}; 

int main() 
{ 
    std::cout << "using library\n"; 
} 

Output:

init 
using library 
fini 

Nó sử dụng (lạm dụng?) thực tế là các hàm tạo cho các đối tượng tĩnh được gọi trước chính và các trình phá hủy được gọi sau chính. Nó giống như RAII cho toàn bộ chương trình.

+4

cũng google cho Thứ tự khởi tạo tĩnh Fiasco. ví dụ, việc sử dụng 'std :: cout' trong' init' và 'fini' đã làm cho C++ rookie thần kinh này trở nên hiện hữu? –

+0

@somebody: Đó không phải là ý định của tôi để sử dụng một đối tượng toàn cầu trong một đối tượng tĩnh khác, nhưng bạn vẫn nêu ra một điểm tuyệt vời. –

+0

Cảm ơn, ai đó. Bạn đã làm cho tôi disown câu trả lời này hackish. ;-P –

0

Nếu bạn có thể thay đổi cài đặt thư viện, bạn có thể có từng cuộc gọi đến một trong các chức năng của thư viện truy cập một singleton được tạo khi sử dụng lần đầu.

Hoặc bạn đặt một biến toàn cầu/tĩnh vào thư viện khởi tạo nó trong khi xây dựng và tắt nó trong quá trình hủy. (Điều đó có thể trở nên khó chịu nếu thư viện sử dụng các biến toàn cục và thứ tự khởi tạo/tắt xung đột với chúng. Ngoài ra, các trình liên kết có thể quyết định loại trừ các hình ảnh không được quan tâm ...)

Nếu không, tôi không thấy bạn muốn để tránh đếm tham chiếu. (Lưu ý, tuy nhiên, nó có nhược điểm là có thể tạo nhiều chu kỳ init/shutdown trong suốt thời gian của chương trình.)

0

Nếu tập hợp thư viện C của bạn không quá lớn, bạn có thể thử kết hợp các mẫu SingletonFacade rằng các thường trình thư viện C chỉ được gọi thông qua Mặt tiền. Mặt tiền đảm bảo việc khởi tạo và dọn dẹp thư viện C. Singleton đảm bảo rằng chỉ có một ví dụ của Mặt tiền.

#include <iostream> 

// C library initialization and cleanup routines 
void init() {std::cout << "init\n";} 
void fini() {std::cout << "fini\n";} 

// C library routines 
void foo() {std::cout << "foo\n";} 
void bar() {std::cout << "bar\n";} 


class Facade // Singleton 
{ 
public: 
    void foo() {::foo();} 
    void bar() {::bar();} 
    static Facade& instance() {static Facade instance; return instance;} 

private: 
    Facade() {init();} 
    ~Facade() {fini();} 
}; 

// Shorthand for Facade::instance() 
inline Facade& facade() {return Facade::instance();} 


int main() 
{ 
    facade().foo(); 
    facade().bar(); 
} 

Output:

init 
foo 
bar 
fini 
+2

có thể là OT cho OP, nhưng có an toàn không? –

+0

@somebody: Không, 'Facade :: instance()' không phải là chủ đề an toàn. Cách giải quyết dễ dàng (nhưng xấu xí) là gọi 'Facade :: instance()' trong 'main()' trước khi bất kỳ luồng nào được sinh ra. Tại thời điểm đó, có vẻ hơi ngớ ngẩn khi sử dụng mặt tiền, và bạn cũng có thể sử dụng 'init' /' fini' trực tiếp trong chính. –

+0

Tôi thích cách SO làm cho tôi tìm hiểu về sự tinh tế của lập trình. –

1

Tôi đã thấy rất nhiều Singleton nói chuyện, vì vậy tôi chỉ có thể khuyên bạn nên nhìn vào Alexandrescu's work.

Tuy nhiên tôi không chắc chắn rằng bạn thực sự cần một số Singleton tại đó. Bởi vì nếu bạn làm thế, bạn cho rằng tất cả các cuộc gọi của bạn sẽ chia sẻ trạng thái ... đúng không? Bạn có thực sự muốn khi bạn gọi cho thư viện thông qua một trường hợp khác nhau của Wrapper để có được trạng thái trong đó cuộc gọi cuối cùng thiết lập nó?

Nếu không, bạn cần phải tuần tự hóa quyền truy cập và khởi động lại dữ liệu mỗi lần.

class Wrapper 
{ 
public: 
    Wrapper() { lock(Mutex()); do_init_c_library(); } 
    ~Wrapper() { do_clean_up_c_library(); unlock(Mutex()); } 

private: 
    static Mutex& Mutex() { static Mutex MMutex; return MMutex; } 
}; // class Wrapper 

Khá đơn giản ... mặc dù bạn cần đảm bảo rằng Mutex được khởi tạo chính xác (một lần) và tồn tại cho đến khi không còn cần thiết nữa.

Boost cung cấp cơ sở vật chất cho once issue và vì chúng tôi sử dụng phương pháp tiếp cận dựa trên ngăn xếp với MMutex, nó không nên bị hỏng ... Tôi nghĩ (hum).

Các vấn đề liên quan