Имитация конструктора статических объектов в C - PullRequest
5 голосов
/ 21 октября 2011

Я хочу иметь глобальную переменную names, которая выглядит так

char* names[NAMES_CAP];
int names_len = 0;

И я хочу, чтобы каждый, кто ссылается на эту библиотеку, мог добавить элемент в этот список.

Это легко сделать с main.

int main(int argc,char**argv) {
    names[names_len++] = "new name";
    names[names_len++] = "new name 2";
}

а что если я захочу собрать две библиотеки? (т.е. моя библиотека, libnames содержит глобальную переменную. И если кто-то ссылается на libnameuser, который использует libnames, он автоматически добавит все имена, определенные в libnameuser, в массив names в libnames.

Есть ли способ сделать это?

В C ++ я могу вставить names[names_len++] = "..." в конструктор глобального объекта, и он должен быть вызван. Но могу ли я сделать это с простым C?

Ответы [ 5 ]

4 голосов
/ 21 октября 2011

Если вы используете gcc, вы можете использовать атрибут конструктора __attribute__((constructor)), чтобы получить тот же эффект. Это, однако, нестандартный C.

Тем не менее, я бы рекомендовал не использовать этот шаблон, поскольку отсутствует контроль порядка выполнения какой-либо функции перед main. Я предпочел бы найти хороший метод перехвата всех функций конструктора после запуска main.

2 голосов
/ 22 октября 2011

Обновление: обновленную версию этого ответа см. https://stackoverflow.com/a/2390626/270788.

Ниже приведена абстракция препроцессора для поддержки функций статического инициализатора C с GCC и MSVC.Версия GCC также будет работать с LLVM CC, возможно, с некоторыми другими компиляторами.

Версия MSVC работает, помещая ptr в функцию статического инициализатора в специальном разделе, который обрабатывается кодом запуска приложения или DLL..

#if defined(__GNUC__)
  #define INITIALIZER(f) \
    static void f(void) __attribute__((constructor)); \
    static void f(void)
#elif defined(_MSC_VER)
  #define INITIALIZER(f) \
    static void __cdecl f(void); \
    __declspec(allocate(".CRT$XCU")) void (__cdecl*f##_)(void) = f; \
    static void __cdecl f(void)
#endif

INITIALIZER(initializer_1) { names[names_len++] = "new name"; }
INITIALIZER(initializer_2) { names[names_len++] = "new name 2"; }
2 голосов
/ 21 октября 2011

Если вы не на 100% обеспокоены переносимостью и можете гарантировать GCC, вы можете делать то, что хотите, с помощью атрибута «конструктор» в GCC.См .: http://gcc.gnu.org/onlinedocs/gcc-4.1.1/gcc/Function-Attributes.html.

Однако я согласен с @Anders K, когда он говорит, что вы должны инкапсулировать это поведение в функции.

1 голос
/ 21 октября 2011

Я думаю, что для достижения того, что вы хотите, было бы лучше создать пару функций-оболочек для массива names []. Затем модули, которые обращаются к вашему массиву «глобальных» имен, должны пройти через этот интерфейс.

Это позволит вам лучше контролировать и отделять

Затем вы можете иметь массив в одном модуле, а затем выставить эти функции в заголовке, но сохранить массив скрытым.

0 голосов
/ 21 октября 2011

Я думаю, инициализация глобальной переменной работает и в Си.

int func1();
int someGlobalVariable = func1();

int func1()
{
    /* Your initialization code can go here */
} 

Надеюсь, я не ошибаюсь по поводу этой работы в C (использую только C ++ уже очень давно).

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