I'm trying to achieve the following (with incomplete succes): I want in a given namespace Parameters a list of "initializers" (which are objects derived from a simple interface that can be implemented anywhere, and are used to define which parameters the program will take at command line) to be registered before the main starts, or better, before a function parse(argv, argc) of the namespace Parameters is called (from the main). The goal is that I to have such initializers to be called in some automatic way.
The working solution:
Each initializer is a static variable (placed in the file to which the initializer belongs) derived form a virtual class "ParametersInitializer", and its constructor calls a function Parameters::registerInitializer() passing its own pointer. Inside registerInitializer I keep track of the pointers inside a simple linked list, by storing its head and tail as static variables inside Parameters.cpp. When calling parse(), and hence when every initialization went right (especially the std::map I use to store the parameters) I call some function for each registered initializer which adds the parameters.
Now, what I want is to use an object for this list. It does not work neither with std::list nor with a very simple list object I wrote. The problem seems to be the order of initialization of static variables, which I know is not defined between different files. But I assumed that whenever a static variable is accessed, it must had been previously initialized. Which seems not to be the case.
I tried to condensed my problem in the following files:
Expand|Select|Wrap|Line Numbers
- /********************************************************************/
- Parameters.h
- /********************************************************************/
- #ifndef PARAMETERS_H
- #define PARAMETERS_H
- // This is the prototype for the Initializers to be stored at creation time
- // and, later on, used by parse()
- class ParametersInitializer {
- public:
- ParametersInitializer();
- virtual ~ParametersInitializer();
- };
- // Namespace for parameter parsing
- namespace Parameters {
- // Function for registering initializers in the list
- void registerInitializer(ParametersInitializer * aInitializer);
- // Command line parser
- void parse(int argc, char ** argv);
- };
- #endif
- /********************************************************************/
- Parameters.cpp
- /********************************************************************/
- #include <iostream>
- #include "Parameters.h"
- #include "PointerList.h"
- // List of the initializers
- static PointerList Initializers;
- ParametersInitializer::ParametersInitializer()
- {
- // I would like to register here, but then it will acces virtual function
- // Parameters::registerInitializer(this);
- }
- ParametersInitializer::~ParametersInitializer()
- {
- }
- void Parameters::registerInitializer(ParametersInitializer * aInitializer)
- {
- // Registers the initializer in the static PointerList object
- Initializers.store((void *) aInitializer);
- cout << "Registering. Now there are " << Initializers.size() << " registered Initializers" << endl;
- }
- void Parameters::parse(int argc, char ** argv)
- {
- cout << "Parsing..." << endl;
- // Prints the number of registered initializers
- cout << "There are " << Initializers.size() << " registered Initializers" << endl;
- // Does the parsing of argc argv, code omitted
- }
- /********************************************************************/
- PointerList.h
- /********************************************************************/
- #ifndef POINTERLIST_H
- #define POINTERLIST_H
- #include <iostream>
- using namespace std;
- // This should store pointers in a linked list
- // In this example just stores the size of such list
- class PointerList{
- public:
- PointerList() : count(0)
- {
- cout << "Creating a PointerList object" << endl;
- }
- void store(void * pointer)
- {
- count++;
- }
- int size()
- {
- return count;
- }
- private:
- int count;
- };
- #endif
- /********************************************************************/
- somefile.cpp
- /********************************************************************/
- #include <iostream>
- #include "Parameters.h"
- using namespace std;
- // Derives from ParametersInitializer
- class Initializer_B : public ParametersInitializer {
- public:
- Initializer_B()
- {
- cout << "Creating an object of type Initializer_B" << endl;
- Parameters::registerInitializer(this);
- }
- };
- // Three static variables, for which the constructor will be called
- static Initializer_B tmp1;
- static Initializer_B tmp2;
- static Initializer_B tmp3;
- /********************************************************************/
- main.cpp
- /********************************************************************/
- #include <iostream>
- #include "Parameters.h"
- using namespace std;
- // Derives from ParametersInitializer
- class Initializer_A : public ParametersInitializer {
- public:
- Initializer_A()
- {
- cout << "Creating an object of type Initializer_A" << endl;
- Parameters::registerInitializer(this);
- }
- };
- // Static variable, for which the constructor will be called
- static Initializer_A tmp1;
- static Initializer_A tmp2;
- // Main function
- int main(int argc, char ** argv)
- {
- // Parses arguments
- Parameters::parse(argc, argv);
- return EXIT_SUCCESS;
- }
Now, if I compile these files (the compiler is gcc version 4.0.1 for Mac) and run the program, I obtain the following output:
Creating an object of type Initializer_B
Registering. Now there are 1 registered Initializers
Creating an object of type Initializer_B
Registering. Now there are 2 registered Initializers
Creating an object of type Initializer_B
Registering. Now there are 3 registered Initializers
Creating a PointerList object
Creating an object of type Initializer_A
Registering. Now there are 1 registered Initializers
Creating an object of type Initializer_A
Registering. Now there are 2 registered Initializers
Parsing...
There are 2 registered Initializers
As you can see, first the three Initializer_B static objects are created, then the static object PointerList, and finally the two Initializer_A static objects. And therefore three initializers are lost. Obviously you may have different outcomes, since the order is undefined. But I expected PointerList to be the first, since both Initializer_A and Initializer_B access the static object PointerList (from register()).
Is gcc's fault? Or is the order undefined nomatter which object access whichother? I know that the order of initialization is unspecified among different files, but I assumed that if static A uses static B, then static B will be initialized first (or in other words, that when a static object is accessed it have been already initialized).
How can I make all this working? Is the only option to manage such a list by simple static variables (with constant initializers) and directly from within Parameters.cpp?
Anoter (smaller) problem is that I have to call the function "registerInitializer" from the constructor of each derived class, because if I call it from the constructor of the base class "ParametersInitializer" (which would be a much cleaner solution), then when I access the pure virtual function he does not access those of the derived class, but the virtual ones (with a runtime errror). Any way out?
Thanks and sorry again for the long mail,
Sandro Bosio