Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

What's the most elegant way to do a non-trivial initialization before main?

I'm not really into C design patterns, so my doubt is potentially simple (although a little specific). The real application of this question is harder to explain, so let me simplify it.

Suppose I have an array, in which I want to store prime numbers. The number of primes this array contains is defined by NUMBER_OF_PRIMES, a constant defined at compile time.

Thus, we have:

unsigned primes[NUMBER_OF_PRIMES];

If the size was fixed, I could pre-compute the primes and initialize the array as usual:

unsigned primes[NUMBER_OF_PRIMES] = { 2, 3, 5, 7, ... };

But that would be rather ugly if NUMBER_OF_PRIMES were, let's say, greater than 200. I want some way to run a function at runtime, but before main(), that will put those primes numbers there. Of course I could do:

unsigned* primes = make_primes(NUMBER_OF_PRIMES);

which would allocate the necessary memory and run before main. The main problem is: this array would be in a header file, but it's value would contain something that's hidden inside the corresponding .c file. what I thought I could do is:

/* Header file */
unsigned primes[NUMBER_OF_PRIMES];

/* C file */
int nothing = initialize_primes(); /* This function would write the 
values to the array, using things that are not available in the
header (it is in the .c, so it can reference them), and return anything */

Another way is obviously putting initialize_primes() in the header file, and asking the user to call it inside the main function (like some libraries' init() function). However, I'd like not to force main() to contain a call to this function.

My question is if there is any elegant way to do this that is universally accepted/used, or if this is ridiculous and I should ask main() to call an init() function to avoid unnecessary, obscure code.

As I said, I'm not working with anything related to primes; this is just an example with the same challenge.

Best regards.

like image 838
Gabriel Avatar asked Oct 23 '11 22:10

Gabriel


2 Answers

Using an init function is the proper way to go. Don't forget to add a fini function, too, so people can release the memory if they want to (without knowing what your library does it might or might not be important but generally it's good to be able to free all memory - for example, when a library is used by a loadable module it usually doesn't want to leak memory when it's unloaded).

like image 197
ThiefMaster Avatar answered Oct 04 '22 22:10

ThiefMaster


One trick that you can do is this:

// primes.c
static unsigned *p = NULL;

const unsigned *primes(void)
{
    if(p == NULL)
      {
        p = malloc((PRIMES_COUNT + 1) * sizeof *p);
        *p++ = 0; // if user accidentally frees p now, it will segfault
        // populate p
        atexit(primes_cleanup);
      }
    return p;
}

void primes_cleanup(void)
{
    if(p)
      {
        free(p - 1); // correct way to free the pointer
        p = NULL;    // now calling _cleanup twice is fine
      }
}

The user uses primes()[N] to get the desired element, and it's guaranteed to be initialized. Even if you store the pointer and use it later, it's guaranteed to work (unless someone cast away the const and changed your list when you weren't looking). If you still want variable-like syntax, use this:

// primes.h
const unsigned *primes(void);
void primes_cleanup(void);
#define PRIMES (primes())

To the end-user, PRIMES will look like any other variable. Win.

like image 26
Chris Lutz Avatar answered Oct 04 '22 21:10

Chris Lutz