agg212
agg212

Reputation: 407

C++ RTTI Registry Pattern

I am interested in learning more about the advantages and limitations of RTTI in C++. Suppose I have the following scenario:

class C {};
class C0 : public C {};
class C1 : public C {};
...

void print(int id, C* c) {
    if (id == 0) dynamic_cast<C0 *>(c)->print();
    else if (id == 1) dynamic_cast<C0 *>(c)->print();
    ...
}

Is it possible to implement the above example using a registry pattern? For example, using something like this:

map<int, ?> registry;
void print(int id, C* c) {
    registry[id](c)->print();
}

Upvotes: 0

Views: 480

Answers (2)

Jarod42
Jarod42

Reputation: 217775

While using polymorphism and virtual method seems more appropriate, you may use something like the following to register and dispatch according to a id

class PrintCaller
{
public:
    template <typename T>
    std::size_t register_class()
    {
        m.push_back([](C* c) {
            auto* p = dynamic_cast<T*>(c);
            if (p) {
                p->print();
            } else {
                throw std::runtime_error("Incorrect type");
            }
        });
        return m.size() - 1;
    }

    void print(std::size_t id, C* c) const {
        if (id < m.size()) {
            m[id](c);
        } else {
            throw std::runtime_error("invalid id");
        }
    }

private:
    std::vector<std::function<void(C*)>> m;
};

Live example

Upvotes: 1

TheUndeadFish
TheUndeadFish

Reputation: 8171

This would most easily be solved by just making print a virtual function. Then you could simply have:

void print(C* c)
{
    c->print();
}

and it would do the right thing for all derived classes.

But if you want to keep print non-virtual, then as you recognize there's a question of how something like registry[id](c)->print(); could work. The map's value type is a compile-time fact, but you want a run-time difference in behavior.

Well, I can think of one way to do that... by using virtual functions. You would need to create a class that works as a wrapper for C with derived versions that match up with the types derived from C. Some template usage could probably make this somewhat decent to deal with. Then the map is declared in terms of pointers to the base but populated via the derived wrappers.

But in the end the takes a lot more complexity and offers no more benefit than could have been achieved by just making print itself virtual in the first place.

Upvotes: 3

Related Questions