5

I want to have a global names variable, which looks like that

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

And I want every one who links to this library to be able to add an item to this list.

It's easy to do that from main.

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

but what if I want to stack up two libraries? (ie, my library, libnames holds the global variable. And if someone links to libnameuser who uses libnames, it will automatically add all names defined in libnameuser to the names array in libnames.

Is there any way to do that?

In C++, I can insert the names[names_len++] = "..." to the constructor of a global object, and it must be called. But can I do that with plain C?

Chi-Lan
  • 3,575
  • 3
  • 22
  • 24

5 Answers5

4

If you are using gcc you can use the constructor attribute __attribute__((constructor)) to get the same effect. This is however non-standard C.

I would however recommend against this pattern since there is no control of ordering of any function run before main. I would rather find a nice method of hooking in all the "constructor" functions after main has started running.

doron
  • 27,972
  • 12
  • 65
  • 103
  • I don't mind the order. And I'll be glad to hear better patterns that achieve similar goal. I want to ease the user as much as possible. – Chi-Lan Oct 21 '11 at 13:20
2

If you are not 100% concerned with portability and you can guarantee GCC, you can do what you want with the "constructor" attribute in GCC. See: http://gcc.gnu.org/onlinedocs/gcc-4.1.1/gcc/Function-Attributes.html.

However, I agree with @Anders K when he says you should encapsulate that behaviour in functions.

Dean Povey
  • 9,256
  • 1
  • 41
  • 52
2

Update: Refer to https://stackoverflow.com/a/2390626/270788 for updated version of this answer.

Below is a pre-processor abstration to support C static initializer functions with GCC and MSVC. The GCC version will also work with LLVM CC, maybe some other compilers as well.

The MSVC version works by placing a ptr to the static initializer function in a special section that is processed by the application or DLL startup code.

#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"; }
Joe
  • 881
  • 7
  • 7
  • Refer to https://stackoverflow.com/a/2390626/270788 for updated version of this answer. – Joe Dec 19 '18 at 17:51
1

I think in order to achieve what you want you would be better off to create a couple of wrapper functions to your names[] array. Then modules that access your 'global' names array would have to go through that interface.

This would allow you some better control and decoupling

You could then have the array in one module and then expose a those functions in a header but keep the array hidden.

AndersK
  • 35,813
  • 6
  • 60
  • 86
  • 1
    But the question remains: How do you go about executing these functions automatically from library code? – Magnus Hoff Oct 21 '11 at 11:39
  • What @MagnusHoff said. My problem is not preventing access to `names`, but forcing users to update `names` before accessing my library. – Chi-Lan Oct 23 '11 at 13:34
0

I think initialization of a global variable works in C too.

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

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

I hope I'm not mistaking about this working in C (been using only C++ for a very long time now).

selalerer
  • 3,766
  • 2
  • 23
  • 33