views:

90

answers:

5

I'm considering a certain solution where I would like to initialize a cell of an array that is defined in other module (there will be many modules initializing one table). The array won't be read before running main (so there is not problem with static initialization order).

My approach:

/* secondary module */

 extern int i[10]; // the array

 const struct Initialize {
  Initialize() { i[0] = 12345; }
 } init;


/* main module */

 #include <stdio.h>


 int i[10];

 int main()
 {
  printf("%d\n", i[0]); // check if the value is initialized
 }

Compiler won't strip out init constant because constructor has side effects. Am I right? Is the mechanism OK? On GCC (-O3) everything is fine.

//EDIT
In a real world there will be many modules. I want to avoid an extra module, a central place that will gathered all minor initialization routines (for better scalability). So this is important that each module triggers its own initialization.

+4  A: 

EDIT

 /*secondary module (secondary.cpp) */

  int i[10]; 
  void func()
  {
       i[0]=1;

  }

.

 /*main module (main.cpp)*/

  #include<iostream>

  extern int i[];
  void func();
  int main()
  {
     func();
     std::cout<<i[0]; //prints 1
  }

Compile, link and create and executable using g++ secondary.cpp main.cpp -o myfile

In general constructors are used(and should be used) for initializing members of a class only.

Prasoon Saurav
There is one importance - you missed that I want to initialize a cell of a table. I edited my example so it's visible now in the code.
adf88
@adf88: edited my post.
Prasoon Saurav
I edited my post too - I explained better what I wanted to say writing "initialize inside a module".
adf88
A: 

I don't think you want the extern int i[10]; in your main module, though, adf88.

Brian Hooper
A: 

This might work, but it's dangerous. Globals/statics construction order within a single module is undefined, and so is module loading order (unless you're managing it explicitly). For example, you assume that during secondary.c Initialize() ctor run, i is already present. You'd have to be very careful not to have two modules initialize the same common data, or have two modules carry out initializations with overlapping side effects.

I think a cleaner design to tackle such a need is to have the owner of the common data (your main module) expose it as a global singleton, with an interface to carry out whichever data initializations needed. You'd have a central place to control init-order, and maybe even control concurrent access (using critical sections or other concurrency primitives). Along the lines of your simplified example, that might be -

/main module (main.c)/

#include class CommonDat { int i;

public:
    const int GetI()    { return i;}
    void SetI(int newI) { i = newI; }
    void incI()         
     {
      AcquireSomeLock();
      i++;
      ReleaseTheLock();
     }
}

CommonDat g_CommonDat;
CommonDat*  getCommonDat()   { return &g_CommonDat; }

int main(void)
{
     printf("%d",getCommonDat()->GetI()); 
}

It's also preferable to have the secondary modules call these interfaces at controlled times in runtime (and not during the global c'tors pass).

(NOTE: you named the files as C files, but tagged the question as c++. The suggested code is c++, of course).

Ofek Shilon
As I wrote "The array won't be read before running main" so there is no problem with initialization order. And I don't want a central place to initialize. I want each minor initialization to be triggered from associated module.
adf88
This is not only an initialization order issue - can you guarantee that the array is even *allocated* during secondary modules initializations?
Ofek Shilon
The order of initialization within a single translation unit is well defined according to 3.6.2. I am not sure what you refer to as 'module' --so this might only be a comment on how it is at a finer granularity level (assuming 'module' > translation unit)
David Rodríguez - dribeas
I interpreted 'module' in the OP as a binary - say, dll in windows. Is the order of dependent dll loads defined? This is actually an honest question - there may be such a spec that I'm unaware of.
Ofek Shilon
Oh, I see the OP commented that he meant translation unit. So the init order concern is irrelevant indeed.
Ofek Shilon
+3  A: 

This works with MSVC compilers but with GNU C++ does not (at least for me). GNU linker will strip all the symbol not used outside your compilation unit. I know only one way to guarantee such initialization - "init once" idiom. For examle:

init_once.h:

template <typename T>
class InitOnce
{
    T *instance;
    static unsigned refs;
public:
    InitOnce() {
        if (!refs++) {
            instance = new T();
        }
    }

    ~InitOnce() {
        if (!--refs) {
            delete instance;
        }
    }
};
template <typename T> unsigned InitOnce<T>::refs(0);

unit.h:

#include "init_once.h"

class Init : public InitOnce<Init>
{
public:
    Init();
    ~Init();
};
static Init module_init_;

secondary.cpp:

 #include "unit.h"
 extern int i[10]; // the array

 Init::Init()
 {
     i[0] = 12345;
 }
 ...
Sergey
Accepted. Well then what I'm trying to do is impossible. Initializers must be referenced somehow from outside the unit.
adf88
A: 

May I ask why you use an array (running the risk of getting out of bounds) when you could use a std::vector ?

std::vector<int>& globalArray()
{
  static std::vector<int> V;
  return V;
}

bool const push_back(std::vector<int>& vec, int v)
{
  vec.push_back(v);
  return true; // dummy return for static init
}

This array is lazily initialized on the first call to the function.

You can use it like such:

// module1.cpp
static bool const dummy = push_back(globalArray(), 1);

// module2.cpp
static bool const dummy = push_back(globalArray(), 2);

It seems much easier and less error-prone. It's not multithread compliant until C++0x though.

Matthieu M.
It's not the point. There may be no such risk...
adf88
If there is no such risk, it means your modules are already tightly coupled (ie they need to know which index to write in) and in this case you're better off writing this in a single source file, to be able to actually check the indices are okay at a glance. My point was about decoupling.
Matthieu M.