3
votes

I have a C/C++ project with lots of modules that are meant to be added and removed among installations (they are like "plugins"). I wanted to greatly simplify the module initialization using a technique similar to Linux Kernel - whenever the kernel is linked with some .c module, the module_init() macro in the .c file "registers" the module at some global site so it can be callback'd later to get properly initialized.

Question: How do I do this without some nasty linking tricks?

I tried one approach, roughly following:

all modules have module_init(initfunc) macro inside that expands into something like

struct _initializer_ {
  _initializer_() {
    register_init_function(initfunc);
  }
} _init_instance_;

The register_init_function stores the initfunc pointer a list, so it can be initialized by the main program later.

Problem here is that the C global variables get all zeroed before use in uncontrollable order, so it happens that some modules are properly registered, while registration of others gets erased when the main module is "initialized" after them. I also tried some tricks with class-static vs. global variable initialization order, but either completely misunderstood the description or they don't work.

Question: Is there some method to somehow ensure initialization order, or a whole other method to do such module initialization?

[EDIT] straightforward explanation:

  • main.c has global variable Y
  • main.h describes that global variable or some possible way to modify it (function X())
  • module.c calls function X() (or any other Y-modifying thingy) to save something to variable Y as soon as possible, without being referenced from main.c.
  • main.c can after that see that someone called X() and saved stuff into Y.

Example purpose: gcc main.c modules/common/.c modules/some_specific_purpose/.c

2
I'm a little concerned by the very loose sense of accuracy of your question. 1) no such thing as "C/C++". 2) no such thing as a ".c module". 2) "module_init()" is not a "macro". 4/5) You don't "link" a ".c file" into the kernel. Perhaps you know what you mean, but programming is all about attention to detail... - Kerrek SB

2 Answers

1
votes

Q: Is there some method to somehow ensure initialization order

G++ has an init_priority attribute.

Some_Class  A  __attribute__((init_priority (2000)));
Some_Class  B  __attribute__((init_priority (543)));

The order would be B and then A

There is also a function attribute constructor which could be helpful. I am pretty sure that functions with that attribute cannot rely on global objects though.

2
votes

The usual approach to runtime behaviour changes of a program is to use dynamic loading: You compile some code into a shared library (e.g. with gcc -shared, producing a .so file on Linux and a DLL in Windows), and then use an operating system API function to load that library into the process space at runtime.

The Linux functions are called dlopen(), dlsym() and dlclose().

In Windows, you have LoadLibrary(), GetProcAddress() and FreeLibrary().

You can also use libtool's ltdl wrapper library that abstracts library loading for some sort of cross-platform portability.

In any event, the typical scenario is that you load the library/shared object, and then obtain a function pointer to a named, exported function, such as int (*initialize)(). You can then implement any sort of dynamic behaviour at will.

(Note that this is way, way simpler than anything the Linux kernel does with module loading, which is a very different and specialized procedure that isn't really of interest to userspace programming.)