0

I have a parent class with several subclasses and was going to add a function that generated and returned a pointer to one of the subclasses randomly. Eg.,

class Parent { ... }
class Child1 : public Parent {...}
class Child2 : public Parent {...}
...
class ChildN : public Parent {...}

// returns one of the children, randomly
Parent* generate_random();

On one hand, I could write generate_random hardcoding the number of Child classes and updating this number each time I add a new subclass of Parent or remove an old one. But this seems a bit fragile. Similarly, I could even use a const variable to keep track of the number of child classes. Again, I'd have to update this manually whenever adding and removing classes. This also seems fragile.

In contrast, in a language like Python, I might put a decorator above the declaration of each subclass of Parent, that would increment a count of all child classes and use that. For instance:

_num_subclasses = 0
def _register_subclass(cls):
    global _num_subclasses
    _num_subclasses += 1

class Parent(object):
    ...

@_register_subclass
class Child1 (Parent):
    ...
@_register_subclass
class Child2 (Parent):
    ...
...

Is there a way to do something similar using the preprocessor? Or is there a way to run a method once and only once before or after a class declaration? (It would be great to get to the point where I could not only increment a counter for each class, but also register a factory for it in a singleton map.) Or, more generally, how do others deal with this type of issue? Please let me know if something in my question was unclear or needs to be rephrased.

Brian Tompsett - 汤莱恩
  • 5,753
  • 72
  • 57
  • 129
Behram Mistree
  • 4,088
  • 3
  • 19
  • 21
  • Why not use template or have a better design? I would opt for the latter – Ed Heal Jul 18 '14 at 21:14
  • My question may have been unclear. I'm not committed to this particular design at all. I'm generally asking what's an idiomatic clean way to provide this functionality. Or, more succinctly, "what I'm doing now seems gross, what *is* a better design?" If you have thoughts on what that better design is or a reference, I'd really appreciate it. – Behram Mistree Jul 18 '14 at 21:19
  • It is unclear what you are trying to achieve – Ed Heal Jul 18 '14 at 21:23
  • c++ has a predefined macro named [__COUNTER__][1] you could use that. [1]: http://stackoverflow.com/questions/22332103/preprocessor-counter-macro – qwertyuiop48458456845 Jul 18 '14 at 21:26
  • qwerty, I was not aware of counter. Thank you. You're right that that would probably get me most of the way there. MooingDuck's solution below is a little more elegant and general (allows creating factories, etc.) though, and I'd recommend others to check it out below. – Behram Mistree Jul 18 '14 at 21:56

1 Answers1

1

I know nothing of python, but lets examine that:

_num_subclasses = 0
def _register_subclass(cls):
    global _num_subclasses
    _num_subclasses += 1

@_register_subclass
class Child1 (Parent):

And translate that to C++(ish)

int _num_subclasses = 0;
template<class cls>
bool _register_subclass() {
    _num_subclasses += 1;
    return true;
}

class Child1 : public Parent {
};
static const bool register1 = _register_subclass<Child1>();

Usually however, one wants a list of the types. Usually vaguely like this:

typedef std::unique_ptr<Parent*> ParentPointer;
//an enum is recommended, but type_id can work, depending on what you're doing
std::unordered_map<std::type_info, std::function<ParentPointer()>> _subclass_generator;
template<class cls>
bool _register_subclass() {
    _subclass_generator[typeid(cls)] = 
        []()->ParentPointer{
            return ParentPointer(new cls{});
        };
    return true;
}

class Child1 : public Parent {
};
static const bool register1 = _register_subclass<Child1>();

And then you can use the _subclass_generator

int selection = std::rand() % _subclass_generator.size();
auto& generator = *std::advance(_subclass_generator.begin(), selection);
ParentPointer ptr = generator(); //generates a Child.
Mooing Duck
  • 64,318
  • 19
  • 100
  • 158
  • Mooing Duck, this is almost exactly what I was looking for and struggling with. I think I didn't realize that I could use the side effect in an assignment to mimic the decorator. Thank you. I'm going to accept the answer. But, could you clarify what you mean by your comment that an enum is recommended? It seems as though typeid does what it needs to and has the advantage of not requiring me to modify code in multiple places whenever I add and remove a class (with an enum, I modify the enum and the place where I actually declare the class). Again, thank you. – Behram Mistree Jul 18 '14 at 21:44
  • @BehramMistree: An enum allows you to number N derived classes from 0-N, which allows easy iteration and it's easy to select one randomly. However, I you say you can't (won't) do that, and that's fine. `type_info` should work fine, but selecting a random one is a little harder. I've explained how to do that in the answer. – Mooing Duck Jul 18 '14 at 21:53