I am trying to implement automatic class registration at runtime (I think this technique goes under the name of "register pattern"). In the following example I am storing an int
in a static member vector, but the goal would be to store function pointers that can be called later.
My understanding is that, since the member registeredClasses
in my code below is declared static
, there should be only one instance of it throughout the program. However this seems not to be the case:
classregister.h
#ifndef CLASSREGISTER_H
#define CLASSREGISTER_H
#include <vector>
#include <iostream>
class ClassRegister
{
public:
static std::vector<int> registeredClasses; //declaration
ClassRegister(int id) {
std::cerr << "registering " << id << " ";
registeredClasses.push_back(id);
std::cerr << "new size: " << registeredClasses.size() << "\n";
}
//code below just for testing
ClassRegister() {}
static int getSize() {
return registeredClasses.size();
}
};
#define REGISTER_CLASS(cls) \
static ClassRegister myClass_##cls(1);
#endif // CLASSREGISTER_H
classregister.cpp
#include "classregister.h"
std::vector<int> ClassRegister::registeredClasses; //definition
class1.h (class2.h follows the same pattern, omitted for brevity)
#ifndef CLASS1_H
#define CLASS1_H
#include "classregister.h"
class Class1
{
public:
Class1();
};
REGISTER_CLASS(Class1)
#endif // CLASS1_H
class1.cpp (class2.cpp follows the same pattern, omitted for brevity)
#include "class1.h"
Class1::Class1()
{
}
main.cpp
#include "classregister.h"
#include <iostream>
int main(int argc, char *argv[])
{
std::cerr << "registering classes works...\n\n";
std::cerr << "but reading from main() does not...\n";
std::cerr << "checking size directly: " << ClassRegister::registeredClasses.size() << "\n";
std::cerr << "checking size through function: " << ClassRegister::getSize() << "\n";
std::cerr << "checking size on instance: " << ClassRegister().getSize() << "\n";
std::cerr << "\n";
std::cerr << "registration from main() uses a different register...\n";
ClassRegister(2);
ClassRegister(3);
ClassRegister(4);
std::cerr << "\n";
std::cerr << "...which is not the one used in the header files\n";
std::cerr << "checking size directly: " << ClassRegister::registeredClasses.size() << "\n";
std::cerr << "checking size on instance: " << ClassRegister().getSize() << "\n";
std::cerr << "checking size through function: " << ClassRegister::getSize() << "\n";
std::cerr << "\n";
}
The issue I have is that it works only "sometimes". Initial output:
registering 1 new size: 1
registering 1 new size: 2
registering classes works...
but reading from main() does not...
checking size directly: 0
checking size through function: 0
checking size on instance: 0
registration from main() uses a different register...
registering 2 new size: 1
registering 3 new size: 2
registering 4 new size: 3
...which is not the one used in the header files
checking size directly: 3
checking size on instance: 3
checking size through function: 3
I removed class1.cpp and class2.cpp, declaring the constructors in the header files, eg. Class1() {}
. Regsitration did not work at all:
//no output here
registering classes works...
//...
Then I reverted the change and suddenly I get the expected output:
registering 1 new size: 1
registering 1 new size: 2
registering classes works...
but reading from main() does not... //it does work this time
checking size directly: 2
checking size through function: 2
checking size on instance: 2
registration from main() uses a different register...
registering 2 new size: 3
registering 3 new size: 4
registering 4 new size: 5
...which is not the one used in the header files
checking size directly: 5
checking size on instance: 5
checking size through function: 5
I have been researching on this issue, but failed to understand the cause. My guess is that it has something to do with the compilation order (but how does registration work then?) or with linkage / storage duration. Can someone shed some light on this?
I think that the trick you need here is in ClassRegister
instead of a static data member, use a function with a static local. This is sometimes called a singleton, although I don't know if that is really the correct term for it.
Code would probably look like:
typedef std::vector<int> register_type;
register_type& getRegister() {
static register_type registeredClasses; //declaration
return registeredClasses;
}