dynamic_cast with RTTI disabled - c++

I'm curious to know what happens when compiling code with a dynamic cast whith RTTI disabled
(either with -fno-rttion GCC or with /GR- on visual studio). Does the compiler "falls back" to static_cast ? Since (at least on VS) it does only issue a warning, what will the compiled code do ?
More specifically, what bad things could happen if I compile without RTTI a code where I'm sure that there are no error possible with dynamic_cast (i.e. where dynamic_cast could be safely replaced by a static_cast) like this one :
class A{ /*...*/ } ;
class B : public A {
int foo() { return 42 ;}
} ;
//...
A * myA = new B() ;
int bar = (dynamic_cast<B*>(myA))->foo() ;

Reading the standard, in 5.2.7/6 we find that unless the target is an unambiguous base of the source, source must be a polymorphic type. Then in 10.3/1
Virtual functions support dynamic binding and objectoriented
programming. A class that declares or inherits a virtual function is
called a polymorphic class.
In other words the standard doesn't seem to say anything about your question. In this case, the standard doesn't allow for a compiler to turn off RTTI so for each compiler you need to check its documentation to see what would happen. Based on this reading, I think this is a compiler question, not a C++ language question as the tag indicates.
Alternately you can avoid the problem completely by just using static_cast when you know it's sufficient.

In MSVC, if your code is not compiled with RTTI enabled, a __non_rtti_object exception will be thrown, if the cast cannot be performed without a run-time check.

The easiest way to find out is to try it.
What you will find is that some of your dynamic casts will be flagged as illegal. Some won't. For example, the conversion is known at compile time when you use dynamic cast to upcast to an unambiguous base class.
Addendum
Re "Since (at least on VS) it does only issue a warning ..."
Ignore warnings at your peril. The best thing to do is to ensure that your code compiles without warnings, with warning levels set very high (and possibly converted to errors). Second best is to look at each and every warning you get and ensure that nothing untoward happens. In this case, something untoward will happen. You really should not care how that untoward event is implemented. What you should care about is getting rid of it.

Just try it:
#include <iostream>
#include <typeinfo>
#include <typeindex>
#include <memory>
#include <vector>
#include <array>
#include <string>
class Base {
public:
virtual ~Base() {
}
};
class A: public Base {
};
class B: public Base {
};
using namespace std;
int main() {
A *a = new A;
auto *ptr = dynamic_cast<B*>(a);
if (!ptr)
std::cout << "failed to cast" << std::endl;
return 0;
}
Without -fno-rtti, the program compiles and the output is:
failed to cast
With -fno-rtti, the program failed to compile:
main.cpp:25:35: error: ‘dynamic_cast’ not permitted with -fno-rtti
auto* ptr = dynamic_cast<B*>(a);
^
You can also test this online here: https://onlinegdb.com/pYTQu2ne2

Related

Why is RTTI needed for non-polymorphic typeid?

I have the following piece of code :
template<typename T>
class genericHandler{public: using evt_t = T;};
template<typename T>
class specialHandler : public genericHandler<T> { /* more stuff */ };
int main(int argc, char *argv[]) {
std::any any_var = specialHandler<int>{};
auto f = [&any_var](auto evtHandler) {
using EventType = typename std::remove_reference<decltype(evtHandler)>::type ::evt_t;
if(any_var.type() == typeid(EventType)) { std::cout << "yes" << std::endl; } else { std::cout << "no" << std::endl; }
};
auto h = specialHandler<int>{ };
f(h);
}
Try it on Coliru
When called, evtHandler is of non-polymorphic derived type specialHandler. According to cppreference, we have :
When applied to an expression of polymorphic type, evaluation of a
typeid expression may involve runtime overhead (a virtual table
lookup), otherwise typeid expression is resolved at compile time.
When I compile with gcc and -fno-rtti, I get the following error message :
cannot use 'typeid' with '-fno-rtti'
RTTI is run-time type information, which should not be needed in the case of non-polymorphic typeid that can be deduced at compile-time. Did I miss something ?
You're asking two questions.
RTTI is run-time type information, which should not be needed in the case of non-polymorphic typeid ...
But this is a compiler switch, not a language feature, so you should check the compiler documentation:
-fno-rtti
Disable generation of information about every class with virtual functions for use by the C++ runtime type identification features (dynamic_cast and typeid). If you don't use those parts of the language, you can save some space by using this flag. Note that exception handling uses the same information, but it will generate it as needed. The dynamic_cast operator can still be used for casts that do not require runtime type information, i.e. casts to void * or to unambiguous base classes.
(my emphasis).
So the switch disables the entire typeinfo system to save space. If you want typeinfo (or you want to use a standard library facility that uses typeinfo), then don't explicitly disable typeinfo with a compiler option.
... non-polymorphic typeid that can be deduced at compile-time
Edit, as Mr. Wakely points out, the issue is that you're explicitly using typeid in your own code.
Although std::any may be capable of erasing the stored type from the top-level object without RTTI, this is implementation-dependent, and it definitely can't implement std::any::type() without using the typeid/typeinfo you told GCC not to generate.
Oh, and I forgot
Did I miss something ?
Yes, to ask your actual question, which is
I'm erasing it because I need to solve a circular template dependency, sadly I am not free to 'simply not erase it' here. Do I have any alternative that doesn't use RTTI
Sure, type erasure doesn't depend on RTTI.
Only automated type erasure depends on RTTI and, as above, not all of that. You can either avoid std::any::type() or write your own discriminated union by hand - you'd need to enumerate your types, but only the enumeration itself needs to be visible to all users of the DU.
It is possible to altered your code not use std::any::type(). In such case everything works as expected (after fixing some other issue in your code):
#include <iostream>
#include <any>
template<typename T>
class genericHandler{public: using evt_t = T;};
template<typename T>
class specialHandler : public genericHandler<T> { /* more stuff */ };
int main(int argc, char *argv[]) {
std::any any_var = specialHandler<int>{};
auto f = [&any_var](auto evtHandler) {
using EventType = typename std::remove_reference<decltype(evtHandler)>::type ::evt_t;
std::cout << (std::any_cast<specialHandler<EventType>>(&any_var) != nullptr ? "yes" : "no") << std::endl;
};
auto h = specialHandler<int>{ };
f(h);
f(specialHandler<double>{});
}
Works when RTTI is disabled:
http://coliru.stacked-crooked.com/a/20855b15701605f1

Dynamic_cast compatibility?

I was reading about dynamic_cast and then I encountered the following statement (from cplusplus.com):
Compatibility note: This type of dynamic_cast requires Run-Time Type
Information (RTTI) to keep track of dynamic types. Some compilers
support this feature as an option which is disabled by default. This
needs to be enabled for runtime type checking using dynamic_cast to
work properly with these types.
After the example:
// dynamic_cast
#include <iostream>
#include <exception>
using namespace std;
class Base { virtual void dummy() {} };
class Derived: public Base { int a; };
int main () {
try {
Base * pba = new Derived;
Base * pbb = new Base;
Derived * pd;
pd = dynamic_cast<Derived*>(pba);
if (pd==0) cout << "Null pointer on first type-cast.\n";
pd = dynamic_cast<Derived*>(pbb);
if (pd==0) cout << "Null pointer on second type-cast.\n";
} catch (exception& e) {cout << "Exception: " << e.what();}
return 0;
}
What does the author mean by "this type of dynamic_cast"? Isn't dynamic_cast only used for polymorphic classes(almost)? And he mentions this RTTI as something that is needed for the dynamic cast to work, does that mean that you have to use dynamic_cast with caution because you do not know if it is supported fully by the compiler and therefore makes it riskier than the other casting operators which do not need this RTTI?
The compatibility note relates to the immediately preceding paragraph (and code example):
But dynamic_cast can also downcast (convert from pointer-to-base to pointer-to-derived) polymorphic classes (those with virtual members) if -and only if- the pointed object is a valid complete object of the target type.
And it's true: downcasting requires an object of polymorphic type, and RTTI to traverse the object's inheritance tree at runtime.
The other type of dynamic_cast is explained in the paragraph before that:
This naturally includes pointer upcast (converting from pointer-to-derived to pointer-to-base), in the same way as allowed as an implicit conversion.
No RTTI is required here as an object's base(s) is/are always known statically.
So you only need to fully read the surrounding text in order to understand the context of the words you're reading.
I would note, however, that in my experience a compiler with RTTI disabled by default is basically unheard of. I'm not saying that none exist — there may be some niche, industry-specific compilers targeting embedded platforms that do this to save the programmer a few bytes in their Makefile. But the compilers that most people use (GCC, Clang, Visual Studio, ICC, Comeau) all, to the best of my knowledge, pack RTTI as standard and leave it on until you ask for it to be turned off.
The author in the section mentioned by you referred to the cases when you are using dynamic_cast with polymorphic types: to be a little bit more precise, when you write something like dynamic_cast<X*>(p).
In cases like that, you are going to need Run-Time Type Information so that dynamic_cast can be used at all (see the example below).
You can make the compiler disable the generation of such information about every class with virtual functions by using the mentioned compiler option, -fno-rtti, but it's rarely recommended.
"Other" cases are about the usage of dynamic_cast for void*s.
For example, consider the following code:
class A {
public:
virtual ~A() = default;
};
class B : public A {};
int main()
{
A *p = new B();
void *pv = dynamic_cast<void*>(p);
//B *pb = dynamic_cast<B*>(p);
delete p;
return 0;
}
If you compile the code with g++ test.cpp -std=c++11 -fno-rtti, it's gonna be just fine. But, if you do the same after uncommenting B *pb = dynamic_cast<B*>(p);, the compiler is going to give the following error message for this specific line: error: ‘dynamic_cast’ not permitted with -fno-rtti. Note that the cast to void* works even if using -fno-rtti (which has been set either manually or by default).
rtti is expensive at runtime and some embedded systems compile with flags, disabling it. All it gives you are dynamic_cast and typeid.
Therefore, I interpret
because you do not know if it is supported fully by the compiler
as
because your code could have been compiled with rtti disabled.

what is this base: means? is it a standard c++ key word

The code as follows:
#include <iostream>
struct A {
virtual void foo() { std::cout << "hello world!" << std::endl; }
};
struct B : public A {
void foo() override { base:foo(); }
};
void main() {
A* p = new B();
p->foo();
delete p;
}
what is the 'base:' means? is it a standard key word in c++?
platform: win10 + vs2015 update3;
project type: win32 console project;
A somewhat known idiom would involve something like
struct B : public A {
typedef A base;
void foo() override { base::foo(); }
};
What you have there looks superficially similar, but it is not that. In your case base: is just a label, which affects nothing. Unless the code you presented is an intentional puzzle/joke, it might actually be an attempt to reproduce what I posted above by a person who just doesn't rememebr all the details.
In any case, base is not a keyword in C++ (and is not a keyword in VS2015 either).
base is not a keyword in C++. It's just an ordinary identifier with no special meaning.
If the code is exactly what you've shown us, then the base: is a goto label -- and completely pointless, since there are no references to it. You can demonstrate this yourself by (temporarily) adding goto base; after base:foo();. (I just tried it myself and didn't get any additional diagnostics. Of course that would be an infinite loop.)
It's likely that the author of the code didn't intend it to be a goto label, and might have thought that base would refer to the parent class. Perhaps base: was a typo for base:: (which would be an error -- perhaps base is a keyword in some other language)?
It's impossible to be sure what the author intended. The use of void main(), which is illegal in C++ (it needs to be int main()) suggests that the author doesn't know C++ very well. He or she probably tried to do something that refers to the parent class, wrote it incorrectly, and happened by chance to write something that happens to compile.

error: cannot dynamic_cast ... (target is not pointer or reference)

I'm learning exception handling in C++ and run into a problem. Here's the code:
#include<iostream>
#include<exception>
using namespace std;
class A
{
public:
virtual void f(void){}
};
class AA:public A
{
public:
void aa(void){};
};
int main(void)
{
A a;
try
{
dynamic_cast<AA>(a).aa();
}
catch(exception ex)
{
cout<<"["<<ex.what()<<"]"<<endl;
}
return 0;
}
So I thought the try catch will allow the function to execute and show me the content of the exception, but my compiler does not compile it. I'm using codeblock with GNU GCC. Please help me and show me what I need to do to get the code run as I intended. thanks a lot.
dynamic_cast can only cast to a pointer value or reference, which is exactly what the error is telling you.
From $5.2.7/1 of the C++ Standard.
The result of the expression dynamic_cast< T >(v) is the result of converting the expression v to type T. T shall be a pointer or reference to a complete class type, or “pointer to cv void.”
In order for dynamic_cast to throw an exception when the object cannot be converted you need to cast to a reference. Change it to the following:
dynamic_cast<AA&>(a).aa();
// ^^^ cast to reference.
As Johnsyweb pointed out dynamic_cast will always throw std::bad_cast when the conversion fails. Although std::bad_cast is derived from std::exception it is always a good idea to use the exception which best fits the expected fail condition. This prevents inadvertently interpreting other errors as an unsuccessful cast.
To apply this to your example it might look like the code below.
#include <iostream>
#include <typeinfo> // std::bad_cast
class A
{
public:
virtual void f(void){}
};
class AA:public A
{
public:
void aa(void){};
};
int main(void)
{
A a;
try
{
dynamic_cast<AA&>(a).aa();
}
catch(const std::bad_cast& ex)
{
std::cout << "["<<ex.what()<<"]" << std::endl;
}
return 0;
}
[Note, doing things like using namespace std; is strongly discouraged as it can cause conflicts with identifiers in the global namespace. I have removed it in the example above.]
You are getting a compiler error because your dynamic_cast is not on pointer or reference.
Change it to:
dynamic_cast<AA&>(a).aa();
... and you get the proper exception thrown.
On side note: Smart compilers like g++ would warn as well:
warning: dynamic_cast on an object (here a) can never succeed.
So it's better to limit such code for toying around. In production quality code, the dynamic_cast should be performed only on pointer/reference.
Your problem is not with exception handling, but with your dynamic cast:
'AA' is not a reference or pointer
dynamic_cast safely converts pointers and references to classes and not instances.
So you could do:
dynamic_cast<AA&>(a).aa();
...which will always fail and throw a std::bad_cast exception.
You should catch the most-specific type of exception that you are expecting and since the recommended way to catch is by reference, you should prefer:
catch (std::bad_cast const& ex)
Further reading: dynamic_cast conversion on cppreference.com.
I just dealt with the same error, but in my case I was going from a pointer to a pointer, so the other answers here did not apply. My error message was slightly different, however: error: cannot dynamic_cast 'f()' (of type 'class B*') to type 'class A*' (target is not pointer or reference to complete type).
The root cause in my case was much more simple and mundane.
Notice the addition of to complete type at the end. This caused me to remember that I did not include the header file for my class which I was using. It was not an unknown symbol because A* was forward declared with class A; in the header file, causing it to exist but not be complete, hence the error.
The solution in my case was to include the header file for the class I was casting to.
This is not the question asker's problem above, but as can be seen by my case can generate the same type of error.

dynamic_cast doubt from C++/Stroustrup : converting to protected base class

I know that following code gives compilation error :
class A{ public : virtual void name(){cout<<typeid(this).name()<<endl;}; };
class B:protected A{public : virtual void name(){cout<<typeid(this).name()<<endl;};};
void foo(B* b)
{
A * a = dynamic_cast<A*>(b); //Error : 'A' is an inaccessible base of 'B'
return;
}
But then why in the C++ Stroustrup book (15.4.1) he writes
class BB_ival_slider:public Ival_slider,protected BBslider{ //...
};
void f(BB_ival_slider*p)
{
// ok
BBslider* pbb2 = dynamic_cast<BBslider*>(p); // ok: pbb2 becomes 0
}
Shouldn't the line be compilation error ?
So either my gcc is wrong in flagging it as compilation error OR the unthinkable, stroustrup typo or most plausibly I have missed something...
The actual quote from 15.4.1 is:
class BB_ival_slider : public Ival_slider, protected BBslider {
// ...
};
void f(BB_ival_slider* p)
{
Ival_slider* pi1 = p; // ok
Ival_slider* pi2 = dynamic_cast<Ival_slider*>(p); // ok
BBslider* pbb1 = p; // error: BBslider is a protected base
BBslider* pbb2 = dynamic_cast<BBslider*>(p); // ok: pbb2 becomes 0
}
That is the uninteresting case. However, it is reassuring to know that dynamic_cast doesn't allow accidental violation of the protection of private and protected base classes.
So it would seem that the text describing the code is correct, but for the wrong reasons -- dynamic_cast doesn't allow accidental violation of the protection of private and protected base classes, but only because using it would be ill-formed and will result in a compiler error, not because using it will yield a null-pointer. And, of course, the code the text is describing is definitely incorrect.
Mistakes happen -- maybe it will be fixed in the 4th edition of the book. :-]
(Also, note that if BB_ival_slider declares f to be a friend, then the code will behave as described in the book. Perhaps this friend declaration was implied earlier in the chapter, but I don't have time right now to read over it carefully to check one way or the other.)
Perhaps he tested that code, perhaps not. (Lots of authors put untested code in their books.) If he did test it, keep in mind that not all compilers are created equal. g++ fails with error: 'BBslider' is an inaccessible base of 'BB_ival_slider'. clang fails with error: cannot cast 'BB_ival_slider' to its protected base class 'BBslider'. Other compilers: Who knows? Every compiler I know of has some problem with compliance with the standard.
I think if I am not finding any constructive evidence then I might just say
"Stroustrup was wrong" (that sounds scary :( )
I dont think compilers are allowed to spill the guts of class internal willingly(by defined standard). Unless they are put through knife. (evil pointer operation that is)