Is it ok to use a static variable to initialize/register variables? - c++

Language: C++
Toolkit: Qt4
The toolkit I'm using has a static method called int QEvent::registerEventType() to register my own event types. When I subclass this QEvent I need to supply the base class this value. QEvent::QEvent(int type).
Is it ok to use a static variable to call this before application starts? Consider the following:
//This is all in my .cpp file
static int myEventType; //This will contain my registered type
/*If I create a static instance of this class the constructor
gets called before the main() function starts.
*/
class DoRegisterMyEventType {
public:
DoRegisterMyEventType() {
myEventType = QEvent::registerEventType();
}
};
static DoRegisterMyEventType doRegisterMyEventType;
//Here is the constructor for MyEvent class which inherits QEvent.
MyEvent::MyEvent()
: QEvent(myEventType)
{
}
How 'evil' is this? I could wrap the whole thing in a namespace to prevent polluting the global namespace.

Since C++'s initialization across TUs is a big grey area with much implementation leeway, I prefer to scrap it completely and be explicit about what gets done when. (This rejection of initialization order due to lack of guarantees is similar to how singleton classes reject global objects.) Specifically, this means any global state (global variables, static data members, and function-local statics) that cannot be initialized with constant-expressions must be initialized in exactly one TU, and that TU is the one that implements main.
In the manual case, this means inserting and updating code in the translation unit that contains main and in main itself. The most common example of such code is calling srand(time(0)) to seed the std::rand PRNG.
You can refactor that manual code management using the preprocessor:
// the implementation file for main, could be named main.cpp
#include "whatever_declares_the_real_main.hpp"
#include "global_objects.inc"
int main(int argc, char* argv[]) try {
#include "main_init.inc"
return the_real_main(argc, argv);
// main.cpp has well-defined responsibility:
// initialize global state before passing control to another function, and
// handle return-code or exceptions
// you can modify this, depending on your preference and desired API
// for example:
return the_real_main(std::vector<std::string>(argv+1, argv+argc));
return the_real_main(parse_args(argv+1, argv+argc));
// just make sure to keep main.cpp's responsibility well-defined and
// relatively simple
}
// example handling; depending on your specifics, you might do something
// different, or know how to provide more information:
catch (std::exception& e) {
std::cerr << "abnormal termination: " << e.what() << '\n';
return 1;
}
catch (...) {
std::cerr << "abnormal termination.\n";
return 1;
}
These .inc files are neither headers nor implementation files. The exact file extension doesn't matter as long as you don't use something which is commonly used for headers or implementation files, such as .h, .hpp, .cc, .cpp, and so forth. You can generate global_objects.inc and main_init.inc based off file-naming conventions, using include guards so that dependencies may be included (just as include guards work for headers).
For example, both of these files correspond with myevent.hpp and would be placed alongside that header:
// file "myevent.global_inc"
#ifndef INCLUDE_GUARD_37E6F5857F8F47918A7C83F29A9DA868
#define INCLUDE_GUARD_37E6F5857F8F47918A7C83F29A9DA868
#include <QEvent.hpp> // or whatever headers you need
#include "myevent.hpp" // declares the variable defined just below
// (remember you use 'extern' to declare objects without defining them)
int your_namespace::myEventType = QEvent::registerEventType();
#endif
// file "myevent.main_inc"
#ifndef INCLUDE_GUARD_4F1B93D0F4D3402B802CBA433241AA81
#define INCLUDE_GUARD_4F1B93D0F4D3402B802CBA433241AA81
// nothing needed in this case, from what you've shown so far
// this is where you place expressions that would otherwise require a dummy
// global variable to make sure they are executed, but this also allows use
// of temporary variables while includes handle dependency order:
#include "something_else.main_inc" // fake example dependency, which must
{ // be executed first
int temp;
some_func(&temp);
other_func(temp); // not easy to transform this into a global's init
// expression, yet defining it this way is natural, because it's exactly
// how you would do it inside a function
}
#endif
Note that if you only require static data initialization with constant-expressions, then that is preferred over all other techniques. The primary restriction for that initialization is not being able to make a function call (but it's actually more complex), so it doesn't apply in your case; this is the only kind of global variable initialization that C can do, if you want to find out more.

Static level initialization is a huge compiler-dependent grey area, as others have mentioned. However, function level initialization is not a grey area and can be used to your advantage.
static inline int GetMyEventType()
{
static int sEventType = QEvent::registerEventType();
return sEventType;
}
MyEvent::MyEvent()
: QEvent(GetMyEventType())
{
}
This solution has the property that registerEventType is guaranteed to be called before you need your event type even if you construct MyEvent during static initialization, which is good, but it does open you up to thread-safety issues if it's possible for MyEvent to be constructed on multiple threads.
Here's a thread-safe version, based on boost::call_once:
#include "boost/thread/once.hpp"
static boost::once_flag sHaveRegistered = BOOST_ONCE_INIT; //This is initialized statically, effectively at compile time.
static int sEventType = -1; //-1 is not a valid event
static void DoRegister()
{
sEventType = QEvent::registerEventType();
}
static inline int GetMyEventType()
{
boost::call_once(sHaveRegistered, &DoRegister);
return sEventType;
}

I use the "static register object" pattern quite a bit, but you must be aware of one big problem - you must ensure that the thing you are registering with, which itself is likely to be static, is created before the thing you are registering. As C++ does not guarantee the order of static construction between translation units, this can be problematic. One solution is to use the so called Meyer Singleton:
class Registry {
public:
static Registry & Instance() {
static Registry r;
return r;
}
...
private:
Registry() {
...
}
};
As all references to the Registry must go through the Instance() method, you are guaranteed the required construction order.

Related

Learning C++ and having a problem correclty separating class interface from implementation

I'm learning C++ using Xcode and have written several small programs including a hangman game but I'm having trouble every time I try to separate a class into definition and implementation. I made a simple case that shows my problem. Short version is it seems that I need to specify a type in the implementation file even though it is already defined in the header file. I get "C++ requires a type specifier for all declarations" on lines 12 and 13 in my example. But if I change line 12, for example, to
int xmlelem::atrb_count = 0;
it gets the error "non-static data member defined out-of-line". In other cases I have got an error saying that I was trying to redefine something. I think I'm missing a fundamental concept somewhere. I did not see this particular issue in the handful of similar questions I looked at.
xmlelem.hpp
// xmlelem.hpp
// learn header
//
//
#ifndef xmlelem_hpp
#define xmlelem_hpp
#include <stdio.h>
#include <string>
#endif /* xmlelem_hpp */
class xmlelem {
private:
int atrb_count;
std::string tag_name;
public:
xmlelem(std::string tag);
void add_atrib();
std::string output();
};
xmlelem.cpp
// xmlelem.cpp
// learn header
//.
//
#include "xmlelem.hpp"
#include "string"
#include <iostream>
// line 11
xmlelem::atrb_count = 0;
xmlelem::tag_name = "";
xmlelem::xmlelem(std::string tag){
tag_name = tag;
}
void xmlelem::add_atrib(){
atrb_count++;
}
std::string xmlelem::output(){
std::string build = "<";
build = build + tag_name + " " + std::to_string(atrb_count);
build = build + ">";
return build;
}
and main.cpp
// main.cpp
// learn header
//
//
#include <iostream>
#include "xmlelem.hpp"
using namespace std;
int main(){
xmlelem clip("test)");
std::cout << clip.output() << " test \n";
}
Let's take a look at the (second) error message.
non-static data member defined out-of-line
There are two parts to the error: "non-static data member" and "defined out-of-line". These are incompatible, so one of them must be changed. Furthermore, only one of them should be changed, or else you may run into a different problem. Decide which of the two parts is correct for your situation.
Keep "defined out-of-line"
When the line
int xmlelem::atrb_count = 0;
is encountered at namespace scope (that is, in neither a function nor a class/struct/union definition), it is an out-of-line definition. This definition tells the compiler to reserve, right at that spot, enough space for an int. Then whenever any xmlelem object accesses the atrb_count member, it will access this particular space. So there is one int shared by all objects.
However, this behavior corresponds to a static member. To make the declaration agree with the implementation, the keyword static needs to be added.
class xmlelem {
private:
static int atrb_count;
/* rest of the class definition */
};
Keep "non-static"
A non-static data member is stored inside each object of the class. Each object can do what it wants with its copy of the data without impacting other objects. So telling the compiler to reserve space outside the objects is contradictory. Simply removing the out-of-line definition is enough to get rid of the error message, but presumably you wanted that initialization to occur somewhere, right?
The initialization of non-static data members can be done either in-line or in a constructor. An example of moving the initialization in-line is the following.
class xmlelem {
private:
int atrb_count = 0;
/* rest of the class definition */
};
This is sometimes reasonable, but the stated goal was to separate the interface from the implementation. Therefore, it might be undesirable for the initial value of 0 to appear in the header file, as it does in the above. The alternative is to move the initial value to the constructor (to each constructor, if you had more than one).
xmlelem::xmlelem(std::string tag) :
atrb_count(0),
tag_name(tag)
{
}
(I've also taken the liberty of moving the initialization of tag_name into the initialization list.)
Remember, if you have more than one constructor, this needs to be done in each constructor that actually utilizes the default value (for an exception, think "copy constructor"). Repeated code is a drawback; it is up to you to decide if the gains are worth the cost.
Remember that you are declaring a class. A class is an abstract concept. When you do this xlemem::atrb_count = 0;, you are having a concrete value on an abstract concept. Doesn't make sense, right? You don't think of a particular color when you think of the general concept of dog. Any initiliazations should be done inside the constructor, because only in the constructor is that we create a concrete object.
Therefore, you should eliminate lines 11 and 12 where you initialize these 2 attributes and your constructor code should be changed to:
xmlelem::xmlelem(std::string tag){
tag_name = tag;
atrb_count = 0;
}
Note that it isn't necessary to initialize a string to "".

Conditionally create an object in c++

I am writing a program that has the option to visualize the output of an algorithm I am working on - this is done by changing a const bool VISUALIZE_OUTPUT variable defined in a header file. In the main file, I want to have this kind of pattern:
if(VISUALIZE_OUTPUT) {
VisualizerObject vis_object;
}
...
if(VISUALIZE_OUTPUT) {
vis_object.initscene(objects_here);
}
...
if(VISUALIZE_OUTPUT) {
vis_object.drawScene(objects_here);
}
However, this clearly won't compile since vis_object goes out of scope. I don't want to declare the object before the condition since it is a big object and it needs to available for multiple points in the code (I can't just have one conditional statement where everything is done).
What is the preferred way of doing this?
Declare the object on the heap and refer to it by using a pointer (or
unique_ptr)?
Declare the object on the heap and make a reference to it
since it won't ever change?
Some other alternative?
A reference will not be useable here, because at declaration it should refere to an already existing object, and live in a scope englobing all your if(VISUALIZE_OUTPUT). Long story short, the object will have to be created unconditionally.
So IMHO a simple way would be to create it on the heap and use it through a pointer - do not forget do delete it when done. The good point is that the pointer could be initialized to nullptr, and so it could be unconditionnaly deleted.
But I think that the best way would be to encapsulate everything in an object created in highest scope. This object would then contain methods to create, use internally and finally destroy the actual vis_object. That way, if you do not need it, nothing will be actually instanciated, but the main procedure will not be cluttered with raw pointer processing.
I would use Null_object_pattern:
struct IVisualizerObject
{
virtual ~IVisualizerObject() = default;
virtual void initscene(Object&) = 0;
virtual void drawScene(Object&) = 0;
// ...
};
struct NullVisualizerObject : IVisualizerObject
{
void initscene(Object&) override { /* Empty */ }
void drawScene(Object&) override { /* Empty */}
// ...
};
struct VisualizerObject : IVisualizerObject
{
void initscene(Object& o) override { /*Implementation*/}
void drawScene(Object& o) override { /*Implementation*/}
// ...
};
And finally:
std::unique_ptr<IVisualizerObject> vis_object;
if (VISUALIZE_OUTPUT) {
vis_object = std::make_unique<VisualizerObject>();
} else {
vis_object = std::make_unique<NullVisualizer>();
}
// ...
vis_object->initscene(objects_here);
//...
vis_object->drawScene(objects_here);
I'll give a few options. All have upsides and downsides.
If it is NOT possible to modify VisualizerObject, as I noted in comments, the effect could be achieved by using the preprocessor, since the preprocessor does not respect scope, and the question specifically seeks controlling lifetime of an object in a manner that crosses scope boundaries.
#ifdef VISUALIZE_OUTPUT
VisualizerObject vis_object;
#endif
#ifdef VISUALIZE_OUTPUT
vis_object.initscene(objects_here);
#endif
The compiler will diagnose any usage of vis_object that are not in #ifdef/#endif.
The big criticism, of course, is that use of the preprocessor is considered poor practice in C++. The advantage is that the approach can be used even if it is not possible to modify the VisualizerObject class (e.g. because it is in a third-party library without source code provided).
However, this is the only option that has the feature requested by the OP of object lifetime crossing scope boundaries.
If it is possible to modify the VisualizerObject class, make it a template with two specialisations
template<bool visualise> struct VisualizerObject
{
// implement all member functions required to do nothing and have no members
VisualizerObject() {};
void initscene(types_here) {};
};
template<> struct VisualizerObject<true> // heavyweight implementation with lots of members
{
VisualizerObject(): heavy1(), heavy2() {};
void initscene(types_here) { expensive_operations_here();};
HeavyWeight1 heavy1;
HeavyWeight2 heavy2;
};
int main()
{
VisualizerObject<VISUALIZE_OUTPUT> vis_object;
...
vis_object.initscene(objects_here);
...
vis_object.drawScene(objects_here);
}
The above will work in all C++ versions. Essentially, it works by either instantiating a lightweight object with member functions that do nothing, or instantiating the heavyweight version.
It would also be possible to use the above approach to wrap a VisualizerObject.
template<bool visualise> VisualizerWrapper
{
// implement all required member functions to do nothing
// don't supply any members either
}
template<> VisualizerWrapper<true>
{
VisualizerWrapper() : object() {};
// implement all member functions as forwarders
void initscene(types_here) { object.initscene(types_here);};
VisualizerObject object;
}
int main()
{
VisualizerWrapper<VISUALIZE_OUTPUT> vis_object;
...
vis_object.initscene(objects_here);
...
vis_object.drawScene(objects_here);
}
The disadvantage of both of the template approaches is maintenance - when adding a member function to one class (template specialisation) it is necessary to add a function with the same signature to the other. In large team settings, it is likely that testing/building will be mostly done with one setting of VISUALIZE_OUTPUT or the other - so it is easy to get one version out of alignment (different interface) to the other. Problems of that (e.g. a failed build on changing the setting) are likely to emerge at inconvenient times - such as when there is a tight deadline to deliver a different version of the product.
Pedantically, the other downside of the template options is that they don't comply with the desired "kind of pattern" i.e. the if is not required in
if(VISUALIZE_OUTPUT)
{
vis_object.initscene(objects_here);
}
and object lifetimes do not cross scope boundaries.

Reusing objects in functions defined in C++ header

I have a function library in a header file, which includes the following function:
// Get a normally distributed float value in the range [0,1].
inline float GetNormDistrFloat()
{
std::random_device _RandomDevice;
std::normal_distribution<float> _NormalDistr(0.5, 2.0);
float val = -1;
do { val = _NormalDistr(_RandomDevice); } while(val < 0.0f || val > 1.0f);
return val;
}
This works well, however, I don't want to create the std::random_device and std::normal_distribution objects every time I call this function GetNormDistrFloat().
What is the "best" (correct) way in C++ to deal with this? I tried to just move those two object definitions outside the function, but that led to linker errors. Do I have to create a .cpp file for this header and initialize the objects there?
You could mark them as static variables which makes them behave almost like globals but only accessible inside the function:
void bar() {
static Foo foo_instance;
// Foo gets initialized only once
}
The main difference is the initialization. Globals get initialized at startup and static variables at their first access.
You can also make them globals, just make sure you do not define them in a header file, instead declare them as external:
// Header file
extern Foo foo_instance;
// Cpp file
Foo foo_instance;
Initialization of local static objects is thread-safe, however everything else is not.
I'm am not a fan of other solutions mentioned here; such as using globals or static locals. For one, state in functions is not a good idea as it's implicit and not obvious when reading code. It also makes things more complicated if you want to use the function from multiple threads. And it also makes testing more complicated. Instead, the "correct" way to handle state is to do the boring thing and create a class:
class NormDistrFloatGenerator
{
public:
NormDistFloatGenerator(const std::random_device& device,
const std::normal_distribution<float>& normal)
: m_device(device)
, m_normal(normal)
{}
float get_float() { // use member variables with same logic as in question }
private:
std::random_device m_device;
std::normal_distribution<float> m_normal;
};
At least if you write this class, you can test it properly, or use it in multiple threads. You only have to initialize this class once, and then you can repeatedly generate floats. If you really want to have something convenient, you can then do:
NormDistFloatGenerator& void makeGlobalFloatGenerator() {
static NormDistFloatGenerator(std::random_device, std::normal_distribution<float>(0.5, 2.0);
}
// at namespace scope
auto& g_float_generator = makeGlobalFloatGenerator();
You can then use g_float_generator everywhere. I'd really encourage you to avoid this approach. And even more so avoid the shortcuts others are suggesting.

Hiding queryable program state without using a class?

Consider a library exporting a distinct interface for initialization, which has to be called by the user before using anything else the library provides. In this step, certain system state is queried and stored in corresponding variables. This cannot be mapped to constants, but should also not be prone to changes due to writes from external sources, i.e. the system state should be queryable in different translation units but not writable.
One obvious example is a timestamp marking the system startup. This cannot be a compile-time constant, but should also never be writable.
This could be realized with a class implementing only static functions and using private, static members:
// system.h
#include <chrono>
using sys_time_point = std::chrono::system_clock::time_point;
class System
{
public:
System () = delete;
// possibly other deleted functions
static bool init () noexcept;
static bool ready () noexcept;
static const sys_time_point& initTime() noexcept;
private:
static bool initState_;
static sys_time_point initTime_;
};
// system.cpp
bool System::initState_ = false;
sys_time_point System::initTime_ = std::chrono::system_clock::now();
The thing is, I consider the latter approach an inadequate design choice because the functions, although possibly dependent on each other, define more or less miscellaneous functionality for querying system state, not modifying or accessing private state of user-defined type.
I'd much rather go for the second approach. Suppose a namespace System which groups the same functionality as the prior class did
// System.h
#include <chrono>
namespace System
{
using sys_time_point = std::chrono::system_clock::time_point;
bool init () noexcept; // needs to be called
bool ready () noexcept; // can be used by other lib components and lib clients
const sys_time_point& initTime() noexcept; // returns system startup time point
// other stuff here ...
}
// System.cpp
namespace System
{
namespace
{
bool sysInitState = false;
sys_time_point sysInitTime = std::chrono::system_clock::now();
}
bool init() noexcept
{
// init code here ... set and return sysInitState accordingly
}
bool ready() noexcept
{
return sysInitState;
}
const sys_time_point& initTime() noexcept
{
return sysInitTime;
}
}
Using the unnamed namespace, I suppress linking to the variables externally in other translation units. AFAIK, there is no way to access them in other translation units except using the functions in namespace System. Writing is also not possible due to const refs or returns by value - except for the scenario where an evil programmer might const_cast<> the const refs to non-const refs.
My questions would now be:
is the above, second solution correct at all?
is the above, second solution viable?
are there other solutions which might be more safe or easier to implement?
Thanks everyone!
Obscuring the private information in a namespace works, but I recommend putting them in a struct and storing one copy of that struct in a local variable.
// System.cpp
namespace {
struct SysInit
{
bool state;
sys_time_point time;
SysInit()
: state(false)
, time (std::chrono::system_clock::now())
{ }
static SysInit& instance()
{
static SysInit rval;
return rval;
}
};
}
void init() noexcept
{
SysInit::instance().state = true;
}
bool ready() noexcept
{
return SysInit::instance().state;
}
const sys_time_point& initTime() noexcept
{
return SysInit::instance().time;
}
The reason for this peculiar trick is there is no order of initialization for globals in different .cpp files. If one of your user's .cpp files calls init() in your example before sysInitTime gets initialized, init may use bad values, or worse, the sysInitTime initializer might change its value, which your library doesn't want.
Static local variables are guaranteed to be initialized once, the first time a function gets called. By storing the data in a static local variable rather than a global, we ensure you have constructed values ready to work with. By putting them in a struct together with one function that returns the whole group, we make it easier on the developer to prove that they are all indeed constructed and up to date (not essential for the algorithm, but it makes it much easier to make sense of the code).
A similar pattern is used by boost, but instead of putting them in an anonymous namespace in a .cpp, they put the variables inside a namespace boost::detail. Boost openly states that if you start mucking around inside boost::detail undefined behavior can occur. They do this because many of their libraries are header only, and don't have a .cpp file to work with. I bring it up because having a detail namespace has become an accepted way of saying "no-touch" to implementation details.

Static member initialization using CRTP in separate library

After digging the web, I found some reference to a powerful pattern which exploits CRTP to allow instantiation at run-time of static members:
C++: Compiling unused classes
Initialization class for other classes - C++
And so on.
The proposed approach works well, unless such class hierarchy is placed into an external library.
Doing so, run-time initialization no more works, unless I manually #include somewhere the header file of derived classes. However, this defeats my main purpose - having the change to add new commands to my application without the need of changing other source files.
Some code, hoping it helps:
class CAction
{
protected:
// some non relevant stuff
public:
// some other public API
CAction(void) {}
virtual ~CAction(void) {}
virtual std::wstring Name() const = 0;
};
template <class TAction>
class CCRTPAction : public CAction
{
public:
static bool m_bForceRegistration;
CCRTPAction(void) { m_bForceRegistration; }
~CCRTPAction(void) { }
static bool init() {
CActionManager::Instance()->Add(std::shared_ptr<CAction>(new TAction));
return true;
}
};
template<class TAction> bool CCRTPAction<TAction>::m_bForceRegistration = CCRTPAction<TAction>::init();
Implementations being done this way:
class CDummyAction : public CCRTPAction<CDummyAction>
{
public:
CDummyAction() { }
~CDummyAction() { }
std::wstring Name() const { return L"Dummy"; }
};
Finally, here is the container class API:
class CActionManager
{
private:
CActionManager(void);
~CActionManager(void);
std::vector<std::shared_ptr<CAction>> m_vActions;
static CActionManager* instance;
public:
void Add(std::shared_ptr<CAction>& Action);
const std::vector<std::shared_ptr<CAction>>& AvailableActions() const;
static CActionManager* Instance() {
if (nullptr == instance) {
instance = new CActionManager();
}
return instance;
}
};
Everything works fine in a single project solution. However, if I place the above code in a separate .lib, the magic somehow breaks and the implementation classes (DummyAction and so on) are no longer instantiated.
I see that #include "DummyAction.h" somewhere, either in my library or in the main project makes things work, but
For our project, it is mandatory that adding Actions does not require changes in other files.
I don't really understand what's happening behind the scene, and this makes me uncomfortable. I really hate depending on solutions I don't fully master, since a bug could get out anywhere, anytime, possibly one day before shipping our software to the customer :)
Even stranger, putting the #include directive but not defining constructor/destructor in the header file still breaks the magic.
Thanks all for attention. I really hope someone is able to shed some light...
I can describe the cause of the problem; unfortunately I can't offer a solution.
The problem is that initialisation of a variable with static storage duration may be deferred until any time before the first use of something defined in the same translation unit. If your program never uses anything in the same translation unit as CCRTPAction<CDummyAction>::m_bForceRegistration, then that variable may never be initialised.
As you found, including the header in the translation unit that defines main will force it to be initialised at some point before the start of main; but of course that solution won't meet your first requirement. My usual solution to the problems of initialising static data across multiple translation units is to avoid static data altogether (and the Singleton anti-pattern doubly so, although that's the least of your problems here).
As explained in Mike's answer, the compiler determines that the static member CCRTPAction<CDummyAction>::m_bForceRegistration is never used, and therefore does not need to be initialised.
The problem you're trying to solve is to initialise a set of 'plugin' modules without having to #include their code in a central location. CTRP and templates will not help you here. I'm not aware of a (portable) way in C++ to generate code to initialise a set of plugin modules that are not referenced from main().
If you're willing to make the (reasonable) concession of having to list the plugin modules in a central location (without including their headers), there's a simple solution. I believe this is one of those extremely rare cases where a function-scope extern declaration is useful. You may consider this a dirty hack, but when there's no other way, a dirty hack becomes an elegant solution ;).
This code compiles to the main executable:
core/module.h
template<void (*init)()>
struct Module
{
Module()
{
init();
}
};
// generates: extern void initDummy(); Module<initDummy> DummyInstance
#define MODULE_INSTANCE(name) \
extern void init ## name(); \
Module<init ## name> name ## Instance
core/action.h
struct Action // an abstract action
{
};
void addAction(Action& action); // adds the abstract action to a list
main.cpp
#include "core/module.h"
int main()
{
MODULE_INSTANCE(Dummy);
}
This code implements the Dummy module and compiles to a separate library:
dummy/action.h
#include "core/action.h"
struct DummyAction : Action // a concrete action
{
};
dummy/init.cpp
#include "action.h"
void initDummy()
{
addAction(*new DummyAction());
}
If you wanted to go further (this part is not portable) you could write a separate program to generate a list of MODULE_INSTANCE calls, one for each module in your application, and output a generated header file:
generated/init.h
#include "core/module.h"
#define MODULE_INSTANCES \
MODULE_INSTANCE(Module1); \
MODULE_INSTANCE(Module2); \
MODULE_INSTANCE(Module3);
Add this as a pre-build step, and core/main.cpp becomes:
#include "generated/init.h"
int main()
{
MODULE_INSTANCES
}
If you later decide to load some or all of these modules dynamically, you can use exactly the same pattern to dynamically load, initialise and unload a dll. Please note that the following example is windows-specific, untested and does not handle errors:
core/dynamicmodule.h
struct DynamicModule
{
HMODULE dll;
DynamicModule(const char* filename, const char* init)
{
dll = LoadLibrary(filename);
FARPROC function = GetProcAddress(dll, init);
function();
}
~DynamicModule()
{
FreeLibrary(dll);
}
};
#define DYNAMICMODULE_INSTANCE(name) \
DynamicModule name ## Instance = DynamicModule(#name ".dll", "init" #name)
As Mike Seymour stated the static template stuff will not give you the dynamic loading facilities you want. You could load your modules dynamically as plug ins. Put dlls containing an action each into the working directory of the application and load these dlls dynamically at run-time. This way you will not have to change your source code in order to use different or new implementations of CAction.
Some frameworks make it easy to load custom plug ins, for example Qt.