开发者

Keep a central list of subclasses but avoiding static instances

I have a slightly nasty setup which I am trying to figure out a good way to overhaul.

I have a class Fractal with a couple of pure virtual functions to do work. Each instance also has a human-readable name. I want to build a menu of all these subclasses so the user can switch between them - but, being lazy, I want to avoid having to both define each instance in its source file and list them all again in another. In other words, I want to build up this list of subclasses dynamically at runtime.

What I have done (and have working) so far is:

  • def开发者_StackOverflow社区ine a class FractalRegistry which is a singleton encapsulation of a std::map<std::string,Fractal*> (where the keys are the instance names),
  • have the base Fractal constructor register each instance by name with that registry (and for completeness the base ~Fractal deregister them),
  • statically instantiate each base class once (with its name, which is a constructor argument).

So for each new fractal I write something like this (paraphrased):

class SomeFractal : public Fractal {
  public:
    SomeFractal(std::string name, std::string desc) : Fractal(name,desc) {}
    virtual void work_function(...) { ... }
}
SomeFractal sf_instance("Some fractal", "This is some fractal or other");

and the instance is added to the central list by the base class constructor, so I don't need to list it myself.

However, this leads to having a load of static instances lying around, which appear to vanish if I move this code into a library. (Yes I can add a horrid empty function to each compile unit so I can force its inclusion, or resort to linker trickery such as -Wl,--whole-archive, but these don't seem like the right answer either.)

Is there a better way? What I guess I'm looking for is a way to write these Fractal implementations - all of which have the same interface, so I thought subclasses of a base class would be ideal - and to keep and populate this central registry of them but without leaving myself the landmine of static instances.

What have I missed? I should state that I've been working with C for years but don't really have the zen of C++, so there might well be something that would do the job staring me in the face... (If I was writing this in C I'd think about writing a second-order macro composite which both declared some function pointers and populated a table with them and the fractals' names and descriptions, but that's an even more Cish thing to do and it really doesn't seem right for C++.)

Edit: What I am hoping to achieve is an elegant way of rearranging my code which makes it easy to add new fractal types and automatically populates a central list of them, but doesn't force the programmer to create a static instance of every fractal.


Recall that static libraries are pre-C++ technology, so it’s not unreasonable for their implementation to assume that unreferenced code is unwanted (as indeed it still is in C++ when one is not playing this particular trick).

As such you need to specify the object files explicitly for each executable that needs them, assuming you don’t want to explore more complicated approaches involving shared libraries, plugins, and so on. This should not be onerous: you must already have a list of the objects that go into the library, so instead of building a library add that list to the linker command line of your executables.


You'd need some kind of object to be created during app start up to handle the registration of the Fractals. Usually when I need to do this, I create a separate Registrar class, that handles the registration of types with a factory, and create an instance of that in the cpp file of each type the factory should know about.

registrar.h

class Registrar
{
    Registrar(const std::string& name, 
              const std::string& desc, 
              Fractal*(*creator)())
    {
        FractalFactory::register(name, desc, creator);
    }
};

#define REGISTER_FRACTAL(name, desc, type)                  \
    namespace {                                             \
        Fractal *create##type() {                           \
            return new type();                              \
        }                                                   \
        Registrar register##type(name, desc, create##type); \
    }

myfractal.cpp

class MyFractal : public Fractal
{
    // fractal code
};

REGISTER_FRACTAL("MyFractal", "Creates a cool pattern", MyFractal);
0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜