C++ cpp files as modules

898 views Asked by At

Sorry if I am duplicating other question but I don't know how to google it. I want to add some minor modularity to my program: some .cpp files should be compiled as "modules". The main requirement is that I should be able to add modules by just adding new .cpp file to the project, without changing other files in any way.

Achieving this looks easy with dynamic loading libraries. Main program can scan some folder for all .dll files, load each of them and call exported "load" symbol from every module. In destructor main program can call "unload" symbols so modules can clean up.

I want the same but with monolithic program. Is there any way for .cpp files to register themselves so main program can call their init() functions at some point? Or for main program to find all such modules?

How it is done in Linux kernel? I know simple adding .c files makes them work somehow.

2

There are 2 answers

5
πάντα ῥεῖ On

You can add new .cpp files to a statically linked application without changing the existing code using a registration interface that is exposed from the main application (singleton).

Something like

App.h:

 struct IModule {
     virtual void init() = 0;
     virtual ~IModule() {}
 };

 class App {
 public:
      void registerModule(IModule* newModule); // Stores the interface
                                               // pointer of an additional
                                               // module
      static App& instance() {
           static App theInstance;
           return theInstance;
      }
 };

NewModule.cpp:

 #include "App.h"

 class NewModule : public IModule {
 public:
      void init();
 private:
      NewModule() {
          App::getInstance().registerModule(this);
      }

      static NewModule instance;
 };

 NewModule NewModule::instance;
0
JohnB On

You can use a static dummy variable inside every cpp file and initialize it by a lambda doing the initialization and registration. Trivial example:

// registration.h

void register_cpp (std::string name);
void print_cpps ();

// registration.cpp
namespace {
   std::vector<std::string> & names () {
      static std::vector<std::string> names_ {};
      return names_;
   }
}

void register_cpp (std::string name) {
   names ().push_back (name); // well, push_back(std::move (name)) would be more efficient
}

void print_cpps () {
    for (auto && name : names()) { std::cout << name << "\n"; }
}

// a.cpp

#include "registration.h"
static std::nullptr_t e = ([] () { register_cpp ("a"); return nullptr; }) ();

// b.cpp

#include "registration.h"
static std::nullptr_t e = ([] () { register_cpp ("b"); return nullptr; }) ();

// main.cpp
#include "registration.h"
int main () {
   print_cpps ();
   return 0;
}

I think you need names_ to be a static local variable in order to make sure that it is initialized before being accessed for the first time.