3

I would like to create a storage object to/from which I can set/get a property of various types in a software application. (Let's just say the property is a string.) If an attempt is made to retrieve a property for some type T for which no property was previously stored, I want the property returned for the most derived type that is a base class of T. And I would like all this without any special requirements on the class types for which properties are stored.

So basically, it should look something like this.

class Store
{
  public:
    template <class T> void put(const string& property) { /* MAGIC HERE */ }
    template <class T> string get() const { /* MORE MAGIC */ }
};

class X {};
class Y : public X {};
class Z : public Y {};

int main()
{
    Store store;
    store.put<X>("x");
    store.put<Z>("z");
    cout << store.get<X>() << endl;
    cout << store.get<Y>() << endl;
    cout << store.get<Z>() << endl;
    return 0;
}

And the output should look like this:

x
x
z

Is this even possible with C++? It would be a snap with java reflections.

Matthew Busche
  • 551
  • 4
  • 12
  • You might be able to do this using RTTI by iterating through `Store` and checking with `dynamic_cast`. There is no way to deduce the base type(s) of some type `T` (as far as I know). No traits support it and I can't imagine a template deduction construct that could find that information either. – François Andrieux May 04 '18 at 19:14
  • It seems there was a proposition to add such a trait to the standard but it was rejected : https://stackoverflow.com/questions/18435001/what-is-the-status-of-n2965-stdbases-and-stddirect-bases Though I'm not sure it would have been helpful here since you need to deduce base types at run time. – François Andrieux May 04 '18 at 19:16
  • It's hard to tell because we don;t have the full picture here, but it looks to me like you are adding properties by composing through inheritance, which is really janky design in general, and sign that you are approaching this from a weird angle from the get-go. –  May 04 '18 at 19:34

1 Answers1

1

The "traditional" way of doing this is to just put a type alias to the parent class (or classes in a tuple<>) and retrieve that using duck-typing.

class X {
  // can be resolved automatically with SFINAE, but having an explicit "no parent" 
  // allows to catch mistakes at compile-time more easily.
  using parent_t = void;
};

class Y : public X {
  using parent_t = X;
};

class Z : public Y {
  using parent_t = Y;
};

It's a fairly minor amount of boilerplate that has rarely caused much friction anywhere I've seen it used.

From then on, in your case, implementing Store with a std::unordered_map<std::type_index, std::string> is pretty trivial.