2

So here is the problem I'm trying to solve, I'm programming in C.

We have a function that can initialize a struct for you.

typedef struct {
  int val1;
  int val2;
} custom_t;

custom_t init_custom() {
  custom_t temp;

  temp.val1 = 5;
  temp.val2 = 5;


  return temp;
}

And you would just use it like so:

custom_t some_name = init_custom();

I have 4 functions that takes the custom_t as input and can do some work with it.

In another file I have a lot of library functions that will run in a multithreaded enviroment. These library functions will all need to do work on the same custom_t variable, no matter the thread.

The library functions won't get the custom_t variable passed to it, because the goal is that another user should be able to use the library functions without thinking of the custom_t variable.

I'm thinking that I have to make the custom_t variable global in the namespace where I define the library functions but I an error saying that global variables must be const.

I am unsure how to achieve this and I would appreciate all the help I can get. If my explanation wasn't good enough feel free to ask any questions and I will try to elaborate.

EDIT: Fixed the variable init typo

Jonathan Leffler
  • 730,956
  • 141
  • 904
  • 1,278
Coolmatt69
  • 23
  • 3
  • 2
    C does not require variables with external linkage to be `const`, so whatever error you got must be specific to a particular usage or context. Present a [mcve] if you want help with that. – John Bollinger Aug 24 '16 at 22:39
  • On the other hand, we do expect *specific* questions here. "How do I go about ?" rarely qualifies. – John Bollinger Aug 24 '16 at 22:40
  • Is it possible you had a call assigning `init_custom` to a global variable within a header? Otherwise: if you have a library that isn't thread safe and you attempt to make it so by implementing thread safety on the data structure you hand to it then, well, usually things won't end well. You can make the accesses sufficiently safe that nobody ever writes at the same time as somebody else is reading but who's to say what the code is assuming about coherence and atomicity of accessing different parts of the structure? – Tommy Aug 24 '16 at 22:42

2 Answers2

3

With custom_t = init_custom(); you're trying to set a typename (i.e. custom_t).

Just call it something else:

custom_t my_global_custom = init_custom();

But, to access this from multiple threads and library functions, assuming you'll need to write to it, you'll need to wrap access to this in a mutex:

pthread_mutex_t custom_mutex = PTHREAD_MUTEX_INITIALIZER;
custom_t my_global_custom;

my_global_custom = init_custom();

// how each thread must access it
pthread_mutex_lock(&custom_mutex);
func_that_uses_my_global_custom();
pthread_mutex_unlock(&custom_mutex);

UPDATE:

My example wasn't intended to be literally an initializer but an assignment:

pthread_mutex_t custom_mutex = PTHREAD_MUTEX_INITIALIZER;
custom_t my_global_custom;
custom_t my_global_2;

custom_t
init_custom(void)
{
    custom_t temp;

    temp.val1 = 5;
    temp.val2 = 5;

    return temp;
}

void
init_custom2(custom_t *temp)
{

    temp->val1 = 5;
    temp->val2 = 5;
}

int
main(void)
{

    // either one of these should work ..
    my_global_custom = init_custom();
    init_custom2(&my_global_2);

    // start some threads ...

    return 0;
}

void *
thread_func(void *)
{

    // how each thread must access it:
    pthread_mutex_lock(&custom_mutex);
    func_that_uses_my_global_custom();
    pthread_mutex_unlock(&custom_mutex);

    return (void *) 0;
}

UPDATE #2:

But do you know any way to initialize my_global_custom outside the main function? Or is that just not possible?

Another way [under gcc at least], is to create a contructor function. Given the above functions and definitions, move the init calls into:

void __attribute__((constructor))
my_global_constructor(void)
{

    my_global_custom = init_custom();
    init_custom2(&my_global_2);
}

Nothing needs to [and nothing should] call this function. It will be called automatically before main is called because it's now a special function.

These are often used by libraries that want to do some init, but don't want to burden main with having to know to call (e.g.) init_liba(); init_libb(); ... In this case, it is called at the "right" time for the library [based upon linkage, etc.].

There is also a __attribute__((destructor)) than can be used to "destroy" things [after main returns, IIRC].

For more on this, see: How exactly does __attribute__((constructor)) work?

Personally, I now use the above attribute, but, for nostalgia, I like the older .init/.fini sections.

Community
  • 1
  • 1
Craig Estey
  • 30,627
  • 4
  • 24
  • 48
  • If `my_global_custom` is declared at file scope, which it must be if it is to have external linkage, then its initializer must be a compile-time constant. It cannot be initialized via a function call. – John Bollinger Aug 24 '16 at 22:42
  • Hey the custom_t = init_custom(); was a typo, it was meant to be used like you said. And the variable is being protected by a mutex. My problem is that I can't create the my_global_custom with a function because it gives the error saying that globals have to be a const – Coolmatt69 Aug 24 '16 at 22:44
  • @JohnBollinger That is exactly my problem. How do I go about it then? – Coolmatt69 Aug 24 '16 at 22:49
1

As clarified in you comment on @CraigEstey's answer, the problem is not that the variable needs to be const, but that its initializer needs to be a compile-time constant. One approach to this sort of problem is to define a static initialization value via a macro instead of as a function return value. For example, in some appropriate header file declare

typedef struct {
    int val1;
    int val2;
} custom_t;

#define CUSTOM_T_INITIALIZER { .val1 = 5, .val2 = 5 }

You can then initialize variables of type custom_t like so:

custom_t some_name = CUSTOM_T_INITIALIZER;

, including at file scope.

Update:

If you need to assign a value to your file-scope or static-duration variable that is not compile-time constant (including if it has an aggregate type and the value desired for one of its members is not a compile-time constant), then you cannot use an initializer for that purpose. Instead, you must arrange to assign the wanted value to that variable after the program starts, perhaps by calling an initialization function of some sort.

John Bollinger
  • 160,171
  • 8
  • 81
  • 157
  • That's a cool trick, did not know that. Thank you. What do I do if val1 needs to be initialized with a function? – Coolmatt69 Aug 24 '16 at 23:05
  • @Coolmatt69, if you want any object with static storage duration, including any variable declared with file scope, to take a value that is not a compile-time constant, then you must assign that value to it as part of the normal execution of your program. You cannot use an initializer for that purpose. – John Bollinger Aug 25 '16 at 13:12