When is using 'typeid' the best solution?
Asked Answered
A

6

28

There are many reasons not to use typeid. Other than for using members of type_info (implementation defined behavior), it is usually (always?) possible to provide similar functionality using other C++ language features, eg: overloading, virtual functions etc.

So, excluding usage that relies on the implementation defined behavior, does anybody have a real world example where typeid is the best solution?

Adelbert answered 19/7, 2011 at 17:2 Comment(3)
Nice question; none that I can think of :-)Knockknee
As a non-C++ developer I have always rather wondered why it would be implementation defined behavior instead of just returning the fully qualified class name...Halicarnassus
@Christopher: for templates, the fully qualified class name could become rather involved. I have template vectors holding about 20 types in my current application, that makes for quite a name (several lines on my console when an error occurs). The specs give implementations some wiggle room to prevent this explosion.Cochran
O
14

boost::any uses typeid to implement any_cast.

template<typename T> any_cast(const any& other) {
   if(typeid(T) != other.type()) throw bad_any_cast();

   //...actual cast here...
}

You can't be sure T is polymorphic, so dynamic_cast is out of the question, and the enclosed type within the boost::any call is lost by now, so none of the other casts can provide any sort of type safety.

Overtake answered 19/7, 2011 at 20:38 Comment(1)
I'm going with this as being the nearest to a real answer. It's the closest to a real code situation where typeid is better than the alternatives. Although, that is based on it throwing an exception rather than say "Asserting" - and I wonder if that is just because boost prefers to fail with exceptions rather than assertions.Adelbert
V
17

So, excluding usage that relies on the implementation defined behavior, does anybody have a real world example where typeid is the best solution?

I sometimes use it in debug outputs, to verify that a template argument passed to my function is indeed of a given type. This makes sense in my case since the actual template argument passed to my function is generated by a specialised metafunction and I want to make sure that the right metafunction is used.

Verein answered 19/7, 2011 at 17:12 Comment(5)
+1 for a pretty decent use case, but I'm not sure it's the killer use of typeid.Adelbert
@Konrad: I use a combination of typeid and demangling for logging. Very helpful.Cochran
@MatthieuM. I’ve used that too, in one project but at some point it became easier to overload or override some class_name method for each class.Verein
@Konrad: I use it to generate the class name actually, but I don't have deep hierarchies, usually the base class children are finals, so I just have to stick a shim in-between the base and a child to get the name "magically" generated.Cochran
Yeah, I used typeid once as part of a std::logic_error derived exception for testing. I had a template code that included a container that did some special bounds-checking logic, and I would throw this exception if I tried to put the container into an invalid state. I used typeid to let me quickly see what the class name is. I didn't do any special demangling, so I saw lots of stuff like GGobLedeGooKKMyClassNameOoGaBooGGaA, but it was just for debugging stuff so I didn't really care.Oldham
A
14

When implementing multimethods (or multiple dispatch), where the actual call is chosen from e.g. a map, using std::type_info* as a key.

Adrianneadriano answered 19/7, 2011 at 20:23 Comment(3)
Damn, I knew I had forgotten an example. I actually thought about this yesterday but thought it didn’t need typeid, though clearly it does.Verein
Is there any other way to implement a key based on class type? Just curious?Halicarnassus
@Christopher An alternative can be 'hand-rolled' RTTI, where the types involved have e.g. a get_type_token function member to identify them (brittle & strongly coupled solution). This also requires fundamental types to be boxed, or to have a token to be otherwise passed along them.Adrianneadriano
O
14

boost::any uses typeid to implement any_cast.

template<typename T> any_cast(const any& other) {
   if(typeid(T) != other.type()) throw bad_any_cast();

   //...actual cast here...
}

You can't be sure T is polymorphic, so dynamic_cast is out of the question, and the enclosed type within the boost::any call is lost by now, so none of the other casts can provide any sort of type safety.

Overtake answered 19/7, 2011 at 20:38 Comment(1)
I'm going with this as being the nearest to a real answer. It's the closest to a real code situation where typeid is better than the alternatives. Although, that is based on it throwing an exception rather than say "Asserting" - and I wonder if that is just because boost prefers to fail with exceptions rather than assertions.Adelbert
M
7

Write a dynamic tree where you can on the runtime modify structure of the tree where there are different types in each link, it'll need typeid. dynamic_cast is not enough.

Edit: Here's some details:

class I {
public:
   virtual std::string type() const=0;
   virtual void *value() const=0;
};
template<class T>
class Impl : public I
{
public:
    Impl(T t) : t(t) { }
    std::string type() const { return typeid(T).name(); }
    void *value() const { return &t; }
private:
    T t;
};

And then build a tree out of these:

template<class Node, class Link>
class Tree { };

With link type being the I* interface... Since the above works for any values of type T1,T2,T3,T4, we could also with similar classes for any functions T->T1, T->T2, T->T3, T->T4, and use that function type as the Node of the tree. Now you have proper expressions described in dynamic tree.

Montero answered 19/7, 2011 at 17:14 Comment(7)
Ok...so you get +1 for at least making it sound as if there's a really good use! ;) Can you flesh this out a bit?Adelbert
Not convinced. I need more detail but I bet I can achieve the same via an interface.Backhand
@tp1: Please check if I'm understanding you correctly: This design can be used where T1, T2 etc. are not related by inheritance and we require to build the tree dynamically. In order to go from a node in the tree to the type, we need to map from the result of 'std::string::type()' to the real type again?Adelbert
@richard corden: Well, the type is only used in the derived class. And anything that browses the tree can only use the base class. So the base class needs all member functions which are required by the algorithms that browse the tree. They just cannot directly use the type for anything. They can possibly pass void*'s to another nodes in the tree, but need to first check that the types are the same. Normally it's used so that you have a collection of different actions or functions that accept different types, and you filter out those functions which have wrong type.Montero
@tp1: Hmmm....so then I have to wonder what 'typeid' gives us? If I understand your last comment you're saying that class I implements the required interface anyway - in which case we could just use the visitor pattern and we'd write visitors which will operate on the nodes they care about and ignore the nodes they don't. No typeid or void* required.Adelbert
@richard corden: well, visitor needs to be modified every time you add new type to the system.Montero
@tp1: Thanks for the reply and comments. I can imagine some reason that you may need this design - but TBH, I'm not sure what typeid really adds. For my purposes here, I don't think this is the killer example.Adelbert
T
2

You can use typeid to compare the actual types of two objects. Could be useful if you want to check equality of two objects, and first make sure they are of the exact same type (though I must say I haven't seen this done a lot, so there might be a good reason why this is not a good idea...).

Typhoon answered 19/7, 2011 at 18:20 Comment(4)
Exactly: can you cite a single use-case of this, and one which couldn’t be solved better / equally well using dynamic_cast?Verein
@Konrad Rudolph: Yes, there is. dynamic_cast will cast a derived class to a base class just fine, so if the comparison operator that gets called is in the base class, you won't get the functionality you want.Multiangular
@eran: Can you provide a real world example where this is necessary? Based on my experience so far, I would have said that a design that requires this level of "comparison" is not robust. I'm hear to be enlightened!Adelbert
@Konrad, Richard - I can't think of a real-world example, but since such a check is common in Java, I figured it might be useful on some cases in C++ as well. I think this difference between Java and C++ goes beyond the scope of this question, so I've asked another one.Typhoon
D
2

I use it to probe the class type of the exception in my catch all handler.

// fudge vtable existence (thrown exceptions must have one)
class StubException
{
    virtual ~StubException();
};

.....

catch(...)
{
    StubException *e = getExceptionObject(); // compiler/rt specific
    std::string s = typeid(e).name();

    ...

    throw;
}

The function getExceptionObject() is part of a small utility library I wrong to access additional information about exceptions. It comes in very handy when a function throws an exception I should be catch but don't. It has definitely saved a lot of frustration over the years since I immediately know the type of exception that needs coverage.

Directoire answered 19/7, 2011 at 18:39 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.