Общий шаблон для инициализации и завершения работы библиотеки? - PullRequest
8 голосов
/ 07 февраля 2010

Можно ли использовать шаблон для вызова необходимых процедур инициализации и очистки базовой (C) библиотеки? В моем случае я хотел бы создать класс-обертку, чтобы он мог быть составлен в другие объекты. Проблема в том, что, когда я уничтожаю класс-оболочку, вызываются процедуры очистки базовой библиотеки. Это нормально, пока я не создаю несколько объектов моего класса-оболочки. У меня вопрос, каков лучший способ действительно справиться с этой ситуацией? Вспоминается статический счетчик ссылок, но я хотел знать, были ли другие потенциально лучшие варианты и связанные сделки.

Ответы [ 5 ]

4 голосов
/ 07 февраля 2010

Если инициализация может быть вызвана до основного запуска, а очистка - после основного конца, этот маленький трюк (хак?) Может сработать для вас:

#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";
}

Выход:

init
using library
fini

Он использует (злоупотребляет?) Тот факт, что конструкторы для статических объектов вызываются до main, а деструкторы - после main. Это как RAII для всей программы.

4 голосов
/ 07 февраля 2010

Не все должно быть классом. Шаблон Singleton позволит вам превратить это в класс, но на самом деле он ничего не покупает по сравнению с глобальными функциями:

bool my_library_init();
void my_library_shutdown();

Первый вызов возвращает true, если библиотека была успешно инициализирована, второй просто тихо делает все, что нужно сделать, и завершается. За этими интерфейсами вы можете добавить любой тип подсчета ссылок или типа отслеживания потоков.

Кроме того, не забывайте о том, что ваша библиотека может делать все это прозрачным образом. Когда вызывается первая библиотечная функция, может ли она обнаружить, что она еще не инициализирована, и настроить все перед выполнением работы? Для отключения просто зарегистрируйте ресурсы, которые должны быть уничтожены, глобальным объектом, чтобы они уничтожались при выходе из программы. Делать это таким образом, безусловно, сложнее, но, возможно, стоит воспользоваться преимуществами удобства использования для тех, кто вызывает вашу библиотеку.

1 голос
/ 08 февраля 2010

Я видел много Singleton разговоров, поэтому я могу только порекомендовать посмотреть на Работы Александреску .

Однако я не уверен, что вам действительно нужна Singleton там. Потому что, если вы это сделаете, вы предполагаете, что все ваши звонки будут разделять состояние ... так ли это? Вы действительно хотите, когда вы вызываете библиотеку через другой экземпляр Wrapper, чтобы получить состояние, в котором ее установил последний вызов?

Если нет, вам нужно сериализовать доступ и повторно инициализировать данные каждый раз.

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

Довольно просто ... хотя вам нужно убедиться, что Mutex правильно инициализирован (один раз) и работает до тех пор, пока он больше не понадобится.

Boost предлагает возможности для раз выпуска , и, поскольку мы используем подход на основе стека с MMutex, он не должен ошибаться ... Я думаю (гул).

0 голосов
/ 07 февраля 2010

Если ваш набор подпрограмм библиотеки C не слишком велик, вы можете попробовать объединить шаблоны Singleton и Facade так, чтобы подпрограммы библиотеки C вызывались только через фасад. Фасад обеспечивает инициализацию и очистку библиотеки C. Синглтон гарантирует, что есть только один экземпляр Фасада.

#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();
}

Выход:

init
foo
bar
fini
0 голосов
/ 07 февраля 2010

Если вы можете изменить реализацию библиотеки, вы можете иметь каждый вызов одной из функций библиотеки для доступа к единственному файлу, который создается при первом использовании.

Или вы помещаете глобальную / статическую переменную в библиотеку, которая инициализирует ее во время построения и выключает ее во время уничтожения. (Это может раздражать, если библиотека сама использует глобальные переменные, и порядок инициализации / завершения работы конфликтует с ними. Кроме того, компоновщики могут решить устранить глобальные ссылки без ссылок ...)

В противном случае, я не понимаю, как вы хотите избежать подсчета ссылок. (Обратите внимание, однако, что он имеет недостаток, заключающийся в возможности создания нескольких циклов инициализации / выключения в течение срока действия программы.)

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...