Best way to for C++ types to self register in a list?
You can execute something before main once if a instantiation of a template is made. The trick is to put a static data member into a class template, and reference that from outside. The side effect that static data member triggers can be used to call the register function:
template<typename D>
struct automatic_register {
private:
struct exec_register {
exec_register() {
persistenceSystem::registerPersistableType(
D::getPersister()
);
}
};
// will force instantiation of definition of static member
template<exec_register&> struct ref_it { };
static exec_register register_object;
static ref_it<register_object> referrer;
};
template<typename D> typename automatic_register<D>::exec_register
automatic_register<D>::register_object;
Derive the class you want to be auto-registered from automatic_register<YourClass>
. The register function will be called before main, when the declaration of referrer
is instantiated (which happens when that class is derived from, which will implicitly instantiate that class from the template).
Having some test program (instead of the register function, a function do_it is called):
struct foo : automatic_register<foo> {
static void do_it() {
std::cout << " doit ";
}
};
int main() {
std::cout << " main ";
}
Yields this output (as expected):
doit main
C++ self registering class member
You can't do it fully automatically but you can get somewhere near:
#include <map>
#include <variant>
#include <string>
#include <memory>
template <typename T>
struct property {};
struct properties
{
std::map<std::string, std::variant<std::shared_ptr<property<int>>>> properties;
template <typename T>
std::shared_ptr<property<T>> make_prop(const std::string& name)
{
auto prop = std::make_shared<property<T>>();
properties[name] = prop;
return prop;
}
};
struct foo :
properties
{
std::shared_ptr<property<int>> x = make_prop<int>("x");
std::shared_ptr<property<int>> y = make_prop<int>("y");
};
int main () {
foo f;
}
depending on your requirements you might want std::any
rather than std::variant
.
You could add a macro for declaring properties:
#define MAKE_PROP(name, type) \
std::shared_ptr<property<type>> name = make_prop<type>(#name);
struct bar :
properties
{
MAKE_PROP(x, int);
MAKE_PROP(y, int);
};
Registering each C/C++ source file to create a runtime list of used sources
I wouldn't do that sort of thing right in the code. I would write a tool which parsed the project file (vcproj, makefile or even just scan the project directory for *.c* files) and generated an additional C source file which contained the names of all the source files in some kind of pre-initialized data structure.
I would then make that tool part of the build process so that every time you do a build this would all happen automatically. At run time, all you would have to do is read that data structure that was built.
Register a C++ class so that later a function can iterate over all registered classes
As it turns out what I want is impossible. The reason for that is "register" in this context means "put a type inside type sequence" and type sequences are immutable because they are types themselves. So one should either create this type sequence manually, or as some people suggested move the "registration" into runtime.
C++ how safe are self registering classes?
So after looking a bit further into the standard at the position where Angew pointed me before, I noticed footnote 34
in [basic.start.init]§4
of the standard that states:
A non-local variable with static storage duration having initialization with side-effects must be initialized even if it is not odr-used (3.2, 3.7.1).
And that actually addresses the problem which is mentioned here. The self registering class is not odr-used and modifies the state of the factory object, thus having an initialization with side-effects.
So per this footnote it is actually safe to do a self registration like the one mentioned by me and Skizz.
Edit: As Matt McNabb mentioned, I shouldn't have relied on the footnote. So here is the part of the specification that the footnote refers to: [Basic.stc.static] §2
If a variable with static storage duration has initialization or a destructor with side effects, it shall not be eliminated even if it appears to be unused, except that a class object or its copy/move may be eliminated as specified in 12.8.
C++ Library & Self registering classes: Factory map empty in client application
From the blog post, you are missing the static member registered
(also called "// The really fun part
"). Having and instantiating such a static variable in the base class forces it to be instantiated in all derived classes and this will register the class as a side effect.
EDIT: There is another very small but very important piece of code in the blog post:
Registrar() : Base(Key{}) { (void)registered; }
This will ensure that registered
is used. Because a static
variable is only instantiated the first time it is used, otherwise the function is not called.
In your case, adding the following to node_template
should work:
template <class derived>
struct node_template :
node,
factory::registrar<derived>
{
node_template(const std::string& uuid_string) :
node(uuid_string),
factory::registrar<derived>(uuid_string)
{
(void) registered;
}
static bool do_register() {
derived d; // I am not sure if one should in some way force this to not be optimized away.
return true;
}
inline static bool registered = do_register();
};
Related Topics
Opencv Fisheye Calibration Cuts Too Much of the Resulting Image
Extract C++ Template Parameters
Trying to Pass String Literals as Template Arguments
Why Is the New Operator Allowed to Return *Void to Every Pointer-Type
C++ 2 Dimensional Array with Variable Size Rows
Disable Sleep Mode in Windows Mobile 6
C++: Std::Move with Rvalue Reference Is Not Moving Contents
How to Overload Array Index Operator for Wrapper Class of 2D Array
System':A Namespace with This Name Does Not Exist
Visual Studio 2015 Code Analysis C6386 Warns of Buffer Overrun
Should You Overload Swap in the Std Namespace
Should "Delete This" Be Called from Within a Member Method
C++: Function Pointer to Functions with Variable Number of Arguments
What Does C4250 Vc++ Warning Mean
What Exactly Is a Namespace and Why Is It Necessary
C++ 128/256-Bit Fixed Size Integer Types