Reputation: 203
I have a system that looks something like this:
Master.h
extern const Activators[2];
Master.cpp
#include <TypeOne.h>
#include <TypeTwo.h>
const Activators[2] = { &TypeOne::Create, &TypeTwo::Create };
Where you can imagine TypeOne and TypeTwo are classes with a static Create method that returns a new instance.
I'm looking for a way to decompose this system such that there doesn't need to be a single file that creates a link-time dependency on all of the types.
I'd like to be able to link together a unit test with just TypeOne's object file and a version of the static Activators array that is only filled with the function pointer to TypeOne's Create method.
Is there a way in C++ to create a statically-defined array and then fill individual slots in that array across compilation units? Ideally I'd be able to have something like this:
Master.cpp
const Activators[2];
TypeOne.cpp
Activators[0] = &TypeOne::Create;
TypeTwo.cpp
Activators[1] = &TypeTwo::Create;
Upvotes: 2
Views: 831
Reputation: 66961
The way C++ initializes globals is really weird, and technically the other answers thus far have undefined behavior, though will probably work on your machine/compiler anyway. Basically, the problem is that when the program starts, the implementation is only required to initialize the globals in main.cpp and it's headers. When your code calls a function that's defined in another cpp/header combo (translation unit), only then is C++ required to initialize the globals in that one translation unit.
The easiest (safe) workaround in your particular case, is to simply do the initialization in the header. If a file has include "TypeOne.h"
, it will initialize Activators[0]
itself. To be portable, it's important that the translation unit (cpp file) that contains int main()
also includes the headers for all of these that you need to use. Otherwise, you aren't strictly guaranteed that they'll be initialized before main
begins.
in TypeOne.h
#include "master.h"
class TypeOne {
static std::unique_ptr<TypeOne> Create();
//stuff
};
static const auto TypeOneInitialized = Activators[0] = &TypeOne::Create;
If you have a cpp who shouldn't depend on TypeTwo, simply don't include it's header.
Upvotes: 1
Reputation: 81986
Yes. Although you need to be very careful.
TypeOne.cpp
namespace {
class BeforeMain {
BeforeMain() {
Activators[0] = &TypeOne::Create;
}
};
BeforeMain obj;
}
TypeTwo.cpp
namespace {
class BeforeMain {
BeforeMain() {
Activators[1] = &TypeTwo::Create;
}
};
BeforeMain obj;
}
Then, other than this, just don't access the array until main()
is called.
Personally though, I'd rather see Activators
be a std::vector<T>
, and then have each BeforeMain
use std::vector<T>::push_back()
.
Upvotes: 0
Reputation: 206717
I would provide a functional interface to add Activators
and use it from TypeOne.cpp and TypeTwo.cpp.
In Activators.h:
void addActivator(Activator activator);
In Activators.cpp:
static std::vector<Activator> activators{};
void addActivator(Activator activator)
{
activators.push_back(activator);
}
In TypeOne.cpp:
struct Initializer
{
Initializer()
{
addActivator(&TypeOne::Create);
}
};
static Initializer initializer;
In TypeTwo.cpp:
struct Initializer
{
Initializer()
{
addActivator(&TypeTwo::Create);
}
};
static Initializer initializer;
Upvotes: 0
Reputation: 19052
Assuming that Activators
are a polymorhpic (where Type1
and Type2
both derive from Activators
) type, I would approach the problem like this.
std::vector<std::unique_ptr>>& activators();
std::vector<std::unique_ptr>>& activators()
{
static std::vector<std::unique_ptr>> the_array(2);
return the_array;
}
Then in individual compilation units you can assign whatever you want:
#include "Activators.h"
struct setup_activator_type_1
{
setup_activator_type_1()
{
activators()[0].reset(new Type1);
}
};
static setup_activator_type_1 type1_static_initializer;
Upvotes: 0