Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to get a type from type_info for template parameters

I have a tree where each node basically looks like this:

struct node
{
    std::unordered_set<object*> objects;
    std::map<std::type_index,node> children;
};

When i iterate over the tree to add a new type, i want to do a check :

std::is_base_of<base,derived>

However, the only information i have for the derived type is a type_index/type_info*.

Is there anyway i can transform the type_info* to a template argument?

If not, what are my other options? I guess could call decltype(*objects.begin()), but that would require each set in every node to never be empty.

like image 790
David Avatar asked Dec 06 '22 08:12

David


2 Answers

Is there anyway i can transform the type_info* to a template argument?

No, there is no way. Templates are a compile-time thing, RTTI is run-time. No connection between the two.

I guess could call decltype(*objects.begin()), but that would require each set in every node to never be empty.

It would not require that. decltype doesn't evaluate its argument - it doesn't need to. It only needs to check the types. You can happily do decltype(*a_null_pointer) without invoking UB, since the expression is never evaluated - this is a so-called unevaluated context. sizeof belongs in the same category.

Note that this doesn't buy you much, though - you'll just get object*& back. You generally can't get a type from run-time information without preparing the mapping first.

like image 184
Xeo Avatar answered Dec 14 '22 12:12

Xeo


If I understood you correctly, you want a type inheritance_checker such that each instation of it is associated to a type but inheritance_checker itself is not. Something similar to type_ifo but that can check inheritance relationships at runtime. For example, you want the following to work:

class A {};
class B : public A {};

// Note that a and b have the same type but they are "related" to distinct types.
inheritance_checker a = inheritance_checker::create<A>();
inheritance_checker b = inheritance_checker::create<B>();

assert(   a.is_base_of  (b) );
assert(   a.derives_from(a) ); // derives from or is equal to
assert( ! b.is_base_of  (a) );
assert(   b.derives_from(b) ); // derives from or is equal to

If this is the case, I can suggest you something that, unfortunately, can be quite slow! It depends on exceptions.

The basic idea is that if you throw a B* then a catch (A*) is a match. Hence, we give to inheritance_checker the ability to throw and catch pointers to the type given at construction time. But inheritance_checker is not a template and therefore this capacity must be provided in a type-erased way. This can be done by storing pointers to functions that have a fixed signature that doesn't depend on the type passed at construction but are capable of throwing/catching pointers to the given type. A possible implementation of inheritance_checker is given below.

#include <cassert>

class inheritance_checker {

    typedef void (*thrower_t)();
    typedef bool (*catcher_t)(thrower_t);

public:

    template <typename T>
    static inheritance_checker create() {
        return inheritance_checker(concrete_thrower<T>, concrete_catcher<T>);
    } 

    bool is_derived_from(const inheritance_checker& other) const {
        return other.catcher_(thrower_);
    }

    bool is_base_of(const inheritance_checker& other) const {
        return catcher_(other.thrower_);
    }

private:

    template <typename T>
    static void concrete_thrower() {
        throw static_cast<T*>(nullptr);
    }

    template <typename T>
    static bool concrete_catcher(thrower_t thrower) {
        try         { thrower();   }
        catch (T*)  { return true; }
        catch (...) {              }
        return false;
    }

    inheritance_checker(thrower_t thrower, catcher_t catcher) :
        thrower_(thrower), catcher_(catcher) {
    }

    thrower_t thrower_;
    catcher_t catcher_;
};

class A {};
class B : public A {};
class C : public B {};
class D {};

int main() {

    auto a = inheritance_checker::create<A>();
    auto b = inheritance_checker::create<B>();
    auto c = inheritance_checker::create<C>();
    auto d = inheritance_checker::create<D>();

    assert( a.is_base_of(a));
    assert( a.is_base_of(b));
    assert( a.is_base_of(c));
    assert(!a.is_base_of(d));

    assert( a.is_derived_from(a));
    assert(!a.is_derived_from(b));
    assert(!a.is_derived_from(c));
    assert(!a.is_derived_from(d));

    assert(!b.is_base_of(a));
    assert( b.is_base_of(b));
    assert( b.is_base_of(c));
    assert(!b.is_base_of(d));

    assert( b.is_derived_from(a));
    assert( b.is_derived_from(b));
    assert(!b.is_derived_from(c));
    assert(!b.is_derived_from(d));

    assert(!c.is_base_of(a));
    assert(!c.is_base_of(b));
    assert( c.is_base_of(c));
    assert(!c.is_base_of(d));

    assert( c.is_derived_from(a));
    assert( c.is_derived_from(b));
    assert( c.is_derived_from(c));
    assert(!c.is_derived_from(d));

    assert(!d.is_base_of(a));
    assert(!d.is_base_of(b));
    assert(!d.is_base_of(c));
    assert( d.is_base_of(d));

    assert(!d.is_derived_from(a));
    assert(!d.is_derived_from(b));
    assert(!d.is_derived_from(c));
    assert( d.is_derived_from(d));
}

If you wish, you might add a type_info* member to inheritance_checker to get the extra functionality that type_info provides.

Notice the symmetry between is_base_of and derives_from. Actually, you can remove one of them.

I suggest you to read this article.

like image 43
Cassio Neri Avatar answered Dec 14 '22 13:12

Cassio Neri