Best Way to for C++ Types to Self Register in a List

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



Leave a reply



Submit