Topic: When is bad_typeid thrown?


Author: "James Slaughter" <james@rethguals.net>
Date: Mon, 13 Aug 2001 22:14:31 GMT
Raw View
Or rather, to answer my question more directly, the expression is only known
to be null if it is evaluated and therefore the exception can only be thrown
for polymorphic types. Thanks -- I had a lapse of concentration that day :)

FWIW, The code I was playing with was my own variant of a post to
comp.lang.c++.moderated, by Luis Coelho on 23 December 1998 03:41 GMT for
determining whether a type is polymorphic:

template<class T> bool is_polymorphic()
{
  try {
    typeid(*static_cast<T*>(0));
    return false;
  } catch (std::bad_typeid const &) {
    return true;
  }
}

You can guess my next question: why is typeid so unique? It makes a very
interesting fringe case out of derefencing null. Also, the problems with
skipping evaluation of the expression for non-polymorphic types are
highlighted by Coelho's original version:

template<class T> bool is_polymorphic(T& val)
{
  bool   result = false;
  typeid(result = true, val);
  return result;
}

Maybe this operator should be considered harmful :)


Regards,
James.

"Anthony Williams" <anthwil@nortelnetworks.com> wrote:

> "James Slaughter" <james@rethguals.net> wrote:
> > I think there's an ambiguity in the standard regarding when
> std::bad_typeid
> > is thrown. Consider the following code with reference to 5.2.8/2 (and
> > 3.2/2):
> >
> >   typeid(*static_cast<T*>(0));
> >   assert(false); // Should we ever be here?
> >
[snip]
> > Is that last sentence supposed to apply only if the type is polymorphic?
>
> Yes, because of 3.2/2 (see below)

> [snip] This allows the compiler to optimise
> such a typeid statement into a static-variable-retrieval operation. Since
> the expression is not evaluated, its value is irrelevant, and unknown.
>


---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.research.att.com/~austern/csc/faq.html                ]





Author: "Anthony Williams" <anthwil@nortelnetworks.com>
Date: Tue, 14 Aug 2001 16:06:50 GMT
Raw View
"James Slaughter" <james@rethguals.net> wrote in message
news:997733535.18956.0.nnrp-08.3e31e0bb@news.demon.co.uk...
> Or rather, to answer my question more directly, the expression is only
known
> to be null if it is evaluated and therefore the exception can only be
thrown
> for polymorphic types. Thanks -- I had a lapse of concentration that day
:)
[SNIP]

> You can guess my next question: why is typeid so unique? It makes a very
> interesting fringe case out of derefencing null. Also, the problems with
> skipping evaluation of the expression for non-polymorphic types are
> highlighted by Coelho's original version:
>
> template<class T> bool is_polymorphic(T& val)
> {
>   bool   result = false;
>   typeid(result = true, val);
>   return result;
> }
>
> Maybe this operator should be considered harmful :)

I wasn't there at the time, but I'll guess.

If you ignore polymorphic types for a moment, then sizeof() and typeid() are
remarkably similar - they both accept an expression that is not evaluated,
the only important feature of it is its type - for sizeof(), the compiler
then returns the size of that type, for typeid() it returns a reference to a
std::type_info object. Now consider polymorphic types - the committee
obviously decided that switch-on-type was a desirable facility, and one that
people would write their own mechanism for if it wasn't standardized. It
also makes a logical extension of the typeid operator. However, in order to
work correctly, the expression must now be evaluated at runtime. Evaluating
the expression at runtime for non-polymorphic types would break code, and is
not necessary to obtain the result. Hence the expression is evaluated only
for polymorphic types, where it is necessary.

It is useful to know whether or not a type is polymorphic, when writing
templates, as they behave slightly differently - dynamic casts can only be
used on polymorphic types, for example.

I think it might make sense for typeid to always evaluate the expression,
just ignore the result for non-polymorphic types. Then you need an
alternative mechanism for detecting polymorphic vs non-polymorphic types.
However, it would also break lots of code.

Anthony
--
Anthony Williams
Software Engineer, Nortel Networks Optoelectronics
The opinions expressed in this message are not necessarily those of my
employer



---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.research.att.com/~austern/csc/faq.html                ]





Author: "James Slaughter" <james@rethguals.net>
Date: Sun, 12 Aug 2001 14:47:41 GMT
Raw View
I think there's an ambiguity in the standard regarding when std::bad_typeid
is thrown. Consider the following code with reference to 5.2.8/2 (and
3.2/2):

  typeid(*static_cast<T*>(0));
  assert(false); // Should we ever be here?

This appears to fall under the jurisdiction of the aforementioned section
[expr.typeid]:

---
2 When typeid is applied to an lvalue expression whose type is a polymorphic
class type (10.3), the result refers to a type_info object representing the
type of the most derived object (1.8) (that is, the dynamic type) to which
the lvalue refers. If the lvalue expression is obtained by applying the
unary * operator to a pointer 62) and the pointer is a null pointer value
(4.10), the typeid expression throws the bad_typeid exception (18.5.3).
---

Is that last sentence supposed to apply only if the type is polymorphic? I
first read that paragraph as a list of (two, separate) facts about typeid:
(1) If T is polymorphic, the type_info for the most derived type is
returned.
(2) If expr is *v and v == 0, std::bad_typeid is thrown.

Clause 3.2/2, which is part of [basic.def.odr] says that a typeid operand
won't be evaluated unless it is polymorphic (which is itself weird), but
that doesn't seem important because the other clause doesn't say anything
about evaluating the expression (which may or may not have undefined
results, depending on the outcome of Core Issue 232).

Regards,
James Slaughter



---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.research.att.com/~austern/csc/faq.html                ]





Author: "Anthony Williams" <anthwil@nortelnetworks.com>
Date: Mon, 13 Aug 2001 10:25:37 GMT
Raw View
"James Slaughter" <james@rethguals.net> wrote in message
news:997626372.24523.0.nnrp-12.3e31e0bb@news.demon.co.uk...
> I think there's an ambiguity in the standard regarding when
std::bad_typeid
> is thrown. Consider the following code with reference to 5.2.8/2 (and
> 3.2/2):
>
>   typeid(*static_cast<T*>(0));
>   assert(false); // Should we ever be here?
>
> This appears to fall under the jurisdiction of the aforementioned section
> [expr.typeid]:
>
> ---
> 2 When typeid is applied to an lvalue expression whose type is a
polymorphic
> class type (10.3), the result refers to a type_info object representing
the
> type of the most derived object (1.8) (that is, the dynamic type) to which
> the lvalue refers. If the lvalue expression is obtained by applying the
> unary * operator to a pointer 62) and the pointer is a null pointer value
> (4.10), the typeid expression throws the bad_typeid exception (18.5.3).
> ---
>
> Is that last sentence supposed to apply only if the type is polymorphic?

Yes, because of 3.2/2 (see below)

> I
> first read that paragraph as a list of (two, separate) facts about typeid:
> (1) If T is polymorphic, the type_info for the most derived type is
> returned.
> (2) If expr is *v and v == 0, std::bad_typeid is thrown.
>
> Clause 3.2/2, which is part of [basic.def.odr] says that a typeid operand
> won't be evaluated unless it is polymorphic (which is itself weird), but
> that doesn't seem important because the other clause doesn't say anything
> about evaluating the expression (which may or may not have undefined
> results, depending on the outcome of Core Issue 232).

For non-polymorphic types, the dynamic type is always the same as the static
type, so the expression passed as an operand to typeid is not evaluated if
its type is a non-polymorphic type. This allows the compiler to optimise
such a typeid statement into a static-variable-retrieval operation. Since
the expression is not evaluated, its value is irrelevant, and unknown.

For polymorphic types, the typeid is evaluated at runtime, so that the
typeid of a reference to a polymorphic base class is actually the typeid of
the referred-to-object, which may be a derived class. This allows
polymorphic switch-on-type operations. In order for the compiler to know
which object to retrieve the typeid for, it has to evaluate the expression.
If the result of the expression is a reference obtained by dereferencing of
a NULL pointer, then std::bad_typeid is thrown.

Anthony
--
Anthony Williams
Software Engineer, Nortel Networks Optoelectronics
The opinions expressed in this message are not necessarily those of my
employer



---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.research.att.com/~austern/csc/faq.html                ]