Home > Net >  Is there a way to append to global unordered_map from C file on compilation?
Is there a way to append to global unordered_map from C file on compilation?

Time:09-18

The problem

I want to make a little module system for my project, which will basically be a base class that users may extend with their code, include in the main project and be able to use this module in runtime.

E.g. we have a Renderer module, the base class defines all the mandatory functions to implement:

class Renderer
{
public:
  virtual void BeginFrame(
    /* all the possible parameters: cameras, scene metadata, etc. */) = 0;

  virtual void Draw(uint32_t vertexCount,
                    uint32_t firstVertex,
                    uint32_t instanceCount = 1,
                    uint32_t firstInstance = 0) = 0;
  // All the other overloads for different draw metadata
  // ...

  virtual void EndFrame() = 0;
};

Now I try to implement these function in Vulkan Renderer module - implementation doesn't matter here. What I want to do is to have some global storage for modules (seems unordered_map is a way to go), where you have a module identifier (probably a string which names the module) and function which creates this module and returns unique_ptr of base Renderer class to use in the client application.

What I've tried

Right now I define an extern unordered_map in my Renderer.hpp and the idea is to insert module metadata (identifier and a function to create one) into this map.

//
// Renderer.hpp
//

using RendererModuleCreator = std::function<std::unique_ptr<Renderer>()>;

// Append your custom renderer module here with key being any string you would
// like to name your module.
extern std::unordered_map<std::string, RendererModuleCreator> s_RendererModules;

//
// VulkanRenderer.hpp
//

// Somewhere in the header file
void*
AppendVkRenderer()
{
  s_RendererModules.insert(
    { "VkRenderer", []() { return std::make_unique<VulkanRenderer>(); } });
  return nullptr;
}

//
// VulkanRenderer.cpp
//

// As global variable, should execute the function and append the module to global store.
void* appendCall = AppendVkRenderer();

The above solution only works if you somehow reference VulkanRenderer in compilation units for client application. Otherwise, the code doesn't get executed even if the header file is included. If I try to declare appendCall in the header, I face linker errors because of symbol redefinition.

Is there a way to make this system work?

CodePudding user response:

If you are using C 17 you could use a static inline (otherwise useless) member variable in some class to make sure the function is called, e.g. like this:

class Foo {
    static inline void * bar_ = AppendVkRenderer();
};

But you might have to modify parts of your code to make sure s_RendererModules is initialized before AppendVkRenderer is called.

  • Related