csingletonheader-only

Global singleton in header-only C library


I am trying to implement a global singleton variable in the header-only library in C (not C++). So after searching on this forum and elsewhere, I came across a variation of Meyer's singleton that I am adapting to C here:

    /* File: sing.h */
    #ifndef SING_H
    #define SING_H
    inline int * singleton()
    {
        static int foo = 0;
        return &foo;
    }
    #endif

Notice that I am returning a pointer because C lacks & referencing available in C++, so I must work around it.

OK, now I want to test it, so here is a simple test code:

    /* File: side.h */
    #ifndef SIDE_H
    #define SIDE_H
    void side();
    #endif
    /*File: side.c*/
    #include "sing.h"
    #include <stdio.h>
    void side()
    {
        printf("%d\n",*(singleton()));
    }
    /*File: main.c*/
    #include "sing.h"
    #include "side.h"
    #include <stdio.h>
    int main(int argc, char * argv[])
    {
        /* Output default value - expected output: 0 */
        printf("%d\n",*(singleton()));
        *(singleton()) = 5;
        /* Output modified value - expected output: 5 */
        printf("%d\n",*(singleton()));
        /* Output the same value from another module - expected output: 5*/
        side();
        return 0;
    }

Compiles and runs fine in MSVC in C mode (also in C++ mode too, but that's not the topic). However, in gcc it outputs two warnings (warning: ‘foo’ is static but declared in inline function ‘singleton’ which is not static), and produces an executable which then segfaults when I attempt to run it. The warning itself kind of makes sense to me (in fact, I am surprised I don't get it in MSVC), but segfault kind of hints at the possibility that gcc never compiles foo as a static variable, making it a local variable in stack and then returns expired stack address of that variable.

I tried declaring the singleton as extern inline, it compiles and runs fine in MSVC, results in linker error in gcc (again, I don't complain about linker error, it is logical). I also tried static inline (compiles fine in both MSVC and gcc, but predictably runs with wrong output in the third line because the side.c translation unit now has its own copy of singleton.

So, what am I doing wrong in gcc? I have neither of these problems in C++, but I can't use C++ in this case, it must be straight C solution.

I could also accept any other form of singleton implementation that works from header-only library in straight C in both gcc and MSVC.


Solution

  • I am trying to implement a global singleton variable in the header-only library in C (not C++).

    By "global", I take you to mean "having static storage duration and external linkage". At least, that's as close as C can come. That is also as close as C can come to a "singleton" of a built-in type, so in that sense, the term "global singleton" is redundant.

    Notice that I am returning a pointer because C lacks & referencing available in C++, so I must work around it.

    It is correct that C does not have references, but you would not need either pointer or reference if you were not using a function to wrap access to the object. I'm not really seeing what you are trying to gain by that. You would likely find it easier to get what you are looking for without. For example, when faced with duplicate external defintions of the same variable identifier, the default behavior of all but the most recent versions of GCC was to merge them into a single variable. Although current GCC reports this situation as an error, the old behavior is still available by turning on a command-line switch.

    On the other hand, your inline function approach is unlikely to work in many C implementations. Note especially that inline semantics are rather different in C than in C++, and external inline functions in particular are rarely useful in C. Consider these provisions of the C standard:

    (Emphasis added.)

    So again, the approach presented in your example cannot be relied upon to work in C, though you might find that in practice, it does work with certain compilers.


    If you need this to be a header-only library, then you can achieve it in a portable manner by placing an extra requirement on your users: exactly one translation unit in any program using your header library must define a special macro before including the header. For example:

    /* File: sing.h */
    #ifndef SING_H
    #define SING_H
    
    #ifdef SING_MASTER
    
    int singleton = 0;
    
    #else
    
    extern int singleton;
    
    #endif
    #endif
    

    With that, the one translation unit that defines SING_MASTER before including sing.h (for the first time) will provide the needed definition of singleton, whereas all other translation units will have only a declaration. Moreover, the variable will be accessible directly, without either calling a function or dereferencing a pointer.