3

Je considère une certaine solution où je voudrais initialiser une cellule d'un tableau qui est définie dans un autre module (il y aura beaucoup de modules initialisant une table). Le tableau ne sera pas lu avant l'exécution de main (donc il n'y a pas de problème avec l'ordre d'initialisation statique).Initialiser partiellement la variable définie dans l'autre module

Mon approche:

/* 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 
} 

compilateur ne supprimera pas à init constante parce que le constructeur a des effets secondaires. Ai-je raison? Est-ce que le mécanisme est OK? Sur GCC (-O3) tout va bien.

// EDIT
Dans un monde réel, il y aura beaucoup de modules. Je veux éviter un module supplémentaire, une place centrale qui rassemblera toutes les routines d'initialisation mineures (pour une meilleure évolutivité). Il est donc important que chaque module déclenche sa propre initialisation.

+0

Vous présumez toujours que lors de l'initialisation de chaque module, le tableau i [] est déjà alloué (non initialisé). –

+0

AFAIK espace de stockage statique est alloué (et mis à zéro) en une seule étape lorsque l'application démarre. – adf88

+0

UP: il s'agissait de ** global ** espace de stockage statique – adf88

Répondre

3

Cela fonctionne avec les compilateurs MSVC mais pas avec GNU C++ (au moins pour moi). L'éditeur de liens GNU supprimera tout le symbole non utilisé en dehors de votre unité de compilation. Je ne connais qu'une seule façon de garantir une telle initialisation - "id once" idiome. Pour 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; 
} 
... 
+0

Accepté. Eh bien, ce que j'essaie de faire est impossible. Les initialiseurs doivent être référencés en quelque sorte depuis l'extérieur de l'unité. – adf88

0

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, lien et créer et exécutable en utilisant g++ secondary.cpp main.cpp -o myfile

En général les constructeurs sont utilisés (et doivent être utilisés) pour initialiser les membres d'une seule classe.

+0

Il y a une importance - vous avez manqué que je veux initialiser une cellule d'une table. J'ai édité mon exemple afin qu'il soit visible maintenant dans le code. – adf88

+0

@ adf88: édité mon post. –

+0

J'ai aussi édité mon post - J'ai mieux expliqué ce que je voulais dire en écrivant "initialiser à l'intérieur d'un module". – adf88

0

Je ne pense pas que vous voulez le extern int i[10]; dans votre module principal, cependant, adf88.

0

Cela peut fonctionner, mais c'est dangereux. L'ordre de construction global/statique dans un seul module n'est pas défini, tout comme l'ordre de chargement du module (à moins que vous ne le gériez explicitement). Par exemple, vous supposez que lors de l'initialisation de secondary.c(), ctor run, i est déjà présent. Vous devez faire très attention de ne pas avoir deux modules initialisant les mêmes données communes, ou avoir deux modules effectuant des initialisations avec des effets secondaires qui se chevauchent.

Je pense qu'une conception plus propre pour répondre à un tel besoin est d'avoir le propriétaire des données communes (votre module principal) l'exposer comme un singleton global, avec une interface pour effectuer toutes les initialisations de données nécessaires. Vous disposez d'un emplacement central pour contrôler l'ordre d'initialisation, et peut-être même contrôler l'accès simultané (en utilisant des sections critiques ou d'autres primitives de concurrence). Le long des lignes de votre exemple simplifié, qui pourrait être -

/module principal (main.c)/

#include classe 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()); 
} 

Il est également préférable d'avoir les modules secondaires appellent ces interfaces parfois contrôlées dans l'exécution (et non pendant les c'tors mondiales passe).

(REMARQUE: vous avez nommé les fichiers en tant que fichiers C, mais étiqueté la question en tant que C++. Le code suggéré est C++, bien sûr).

+0

Comme je l'ai écrit "Le tableau ne sera pas lu avant l'exécution principale" donc il n'y a pas de problème avec l'ordre d'initialisation. Et je ne veux pas d'endroit central pour initialiser. Je veux que chaque initialisation mineure soit déclenchée à partir du module associé. – adf88

+0

Il ne s'agit pas seulement d'un problème d'ordre d'initialisation: pouvez-vous garantir que le tableau est alloué de manière paire * lors des initialisations de modules secondaires? –

+0

L'ordre d'initialisation au sein d'une même unité de traduction est bien défini selon 3.6.2. Je ne suis pas sûr de ce que vous appelez 'module' - donc ceci pourrait seulement être un commentaire sur comment il est à un niveau de granularité plus fin (en supposant 'module'> unité de traduction) –

0

Puis-je demander pourquoi vous utilisez un tableau (en cours d'exécution le risque de sortir des limites) quand vous pourriez utiliser un 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 
} 

Ce tableau est initialisé paresseusement lors du premier appel à la fonction.

Vous pouvez l'utiliser comme tel:

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

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

Il semble beaucoup plus facile et moins sujette aux erreurs. Ce n'est pas compatible avec le multithread jusqu'à C++ 0x cependant.

+0

Ce n'est pas le but. Il n'y a peut-être pas de risque ... – adf88

+0

S'il n'y a pas de risque, cela signifie que vos modules sont déjà étroitement couplés (c'est-à-dire qu'ils doivent savoir dans quel index écrire) et dans ce cas il vaut mieux écrire dans un fichier source unique, pour être en mesure de vérifier réellement les indices sont d'accord en un coup d'oeil. Mon point était sur le découplage. –