Q1. Why does using NULL pointers with static_cast cause crashes while dynamic_cast and reinterpret_cast give a NULL pointer in return?
The problem occurred in a method similar to the one given below:
void A::SetEntity(B* pEntity, int iMyEntityType)
{
switch (iMyEntityType)
{
case ENTITY1:
{
Set1(static_cast<C*>(pEntity));
return;
}
case ENTITY2:
{
Set2(static_cast<D*>(pEntity));
return;
}
case ENTITY3:
{
Set3(static_cast<E*>(pEntity));
return;
}
}
}
Inheritance:
class X: public B
class Y: public B
class Z: public B
class C: public X, public M
class D: public Y, public M
class E: public Z, public M
Q2. Is static_casting from B to C/D/E valid? (this worked ok till the input became NULL)
I'm using gcc version 3.4.3
You can static_cast a null pointer - it will give you a null pointer.
In your snippet the problem is most possibly that you pass inconsistent values of pEntity and iMyEntityType into the function. So that when static_cast is done it blindly casts to the wrong type (not the same type as the actual object) and you get an invalid pointer that is later passed down the call stack and causes undefined behaviour (crashes the program). dynamic_cast in the same case sees that the object is really not of the expected type and returns a null pointer.
What compiler are you using? A static cast from a base type to a derived type might result in an adjustment to the pointer - especially likely if multiple inheritance is involved (which doesn't seem to be the case in your situation from your description). However, it's still possible without MI.
The standard indicates that if a null pointer value is being cast that the result will be a null pointer value (5.2.9/8 Static cast). However, I think that on many compilers most downcasts (especially when single inheritance is involved) don't result in a pointer adjustment, so I could imagine that a compiler might have a bug such that it wouldn't make the special check for null that would be required to avoid 'converting' a zero value null pointer to some non-zero value senseless pointer. I would assume that for such a bug to exist you must be doing something unusual to get the compiler to have to adjust the pointer in the downcast.
It might be interesting to see what kind of assembly code was generated for your example.
And for detailed information about how a compiler might layout an object that might need pointer adjustment with static casts, Stan Lippman's "Inside the C++ Object Model" is a great resource.
Stroustrup's paper on Multiple Inheritance for C++ (from 1989) is also a good read. It's too bad if a C++ compiler has a bug like I speculate about here - Stroustrup discusses the null pointer issue explicitly in that paper (4.5 Zero Valued Pointers).
For your second question:
Q2. Is static_casting from B to C/D/E valid?
This is perfectly valid as long as when you perform the cast of the B pointer to a C/D/E pointer the B pointer is actually pointing to the B sub-object of a C/D/E object (respectively) and B isn't a virtual base. This is mentioned in the same paragraph of the standard (5.2.9/8 Static cast). I've highlighted the sentences of the paragraph most relevant to your questions:
An rvalue of type “pointer to cv1 B”, where B is a class type, can be converted to an rvalue of type “pointer to cv2 D”, where D is a class derived (clause 10) from B, if a valid standard conversion from “pointer to D” to “pointer to B” exists (4.10), cv2 is the same cv-qualification as, or greater cv-qualification than, cv1, and B is not a virtual base class of D. The null pointer value (4.10) is converted to the null pointer value of the destination type. If the rvalue of type “pointer to cv1 B” points to a B that is actually a sub-object of an object of type D, the resulting pointer points to the enclosing object of type D. Otherwise, the result of the cast is undefined.
As a final aside, you can workaround the problem using something like:
Set1(pEntity ? static_cast<C*>(pEntity) : 0);
which is what the compiler should be doing for you.
static_cast cannot itself cause a crash - its behaviour at runtime is the same as reinterpret_cast. There is something wrong somewhere else in your code.
MyClass* p = static_cast<MyClass*>(0) works well.
New:
If you use multiple inheritance then static_cast may shift your pointer.
Consider the following code:
struct B1 {};
struct B2 {};
struct A : B2, B1 {
virtual ~A() {}
};
What is struct A?
A contains a table of virtual functions and B1 and B2.
B1 is shifted with respect to A.
To cast B1 to A compiler need to back shift.
If the pointer to B1 is NULL then shift gives invalid result.
static_cast is for situations when you know the cast can be done (either you cast to a parent class, or you have other ways of assessing the type of the class). There is no runtime check on the type (hence the static). On the other hand, dynamic_cast will check, at runtime, if the object is really of the type you want to cast it to. As for reinterpret_cast, it doesn't do anything but using the same memory for a different purpose. Note that reinterpret_cast should never be used to change from one class to another.
In the end, the reason static_cast on NULL pointer crashes, is because a static_cast with inheritance might requires a bit of pointer arithmetic form the compiler. This depend on how the compiler actually implement inheritance. But in case of multiple inheritance, it doesn't have a choice.
One way to see this is that the daughter class "contains" the parent class. It virtual table contains the one of the parent, but with added features. If the features are added at the beginning, then any cast to the parent class will point to a different place ... from where the features of the daughter class cannot be seen. I hope this make sense.
Note on pointer arithmetic
First, this is always be the case for multiple inheritance, but a compiler might choose to do so for single inheritance too.
Basically, if you look at the memory layout for an object content with virtual methods, you could do something like:
+---------------+----------------+
| ptr to vtable | members .... |
+---------------+----------------+
In case of single inheritance, this is pretty much enough. In particular, you can ensure that the vtable of any derived class starts with the vtable of the mother class and the first members are those of the mother class.
Now, if you have multiple inheritance, things are more complex. In particular, you probably can't merge vtables and members in a consistent way (at least not in the general case). So, say you inherit from classes A, B and C, you will probably have something like:
A B C
+----------------------+-----------+-----------+----------+-----------+-----+
| local vtable/members | vtable A | members A | vtable B | members B | ... |
+----------------------+-----------+-----------+----------+-----------+-----+
Such that if you point on A, you will see the object as an object of type A, plus the rest. But if you want to see the object as being of type B, you need to point to the address B, etc. Note, this might not be exactly what the system does, but that's the git of it.
Related
Have a look at is simple example:
struct Base { /* some virtual functions here */ };
struct A: Base { /* members, overridden virtual functions */ };
struct B: Base { /* members, overridden virtual functions */ };
void fn() {
A a;
Base *base = &a;
B *b = reinterpret_cast<B *>(base);
Base *x = b;
// use x here, call virtual functions on it
}
Does this little snippet have Undefined Behavior?
The reinterpret_cast is well defined, it returns an unchanged value of base, just with the type of B *.
But I'm not sure about the Base *x = b; line. It uses b, which has a type of B *, but it actually points to an A object. And I'm not sure, whether x is a "proper" Base pointer, whether virtual functions can be called with it.
static_cast (or an implicit derived-to-base-pointer conversion, which does exactly the same thing) is substantially different from reinterpret_cast. There is no guarantee that that the base subobject starts at the same address as the complete object.
Most implementations place the first base subobject at the same address as the complete object, but of course even such implementations cannot place two different non-empty base subobjects at the same address. (An object with virtual functions is not empty). When the base subobject is not at the same address as the complete object, static_cast is not a no-op, it involves pointer adjustment.
There are implementations that never place even the first base subobject at the same address as the complete object. It is allowed to place the base subobject after all members of derived, for example. IIRC the Sun C++ compiler used to layout classes this way (don't know if it's still doing that). On such an implementation, this code is almost guaranteed to fail.
Similar code with B having more than one base will fail on many implementations. Example.
The reinterpret_cast is valid (the result can be dereferenced) if the two classes are layout-compatible; that is
they both have standard layout,
they both have the same non-static data members
But the classes do not have standard layout because one of the requirements of StandardLayoutType it that the class has no virtual functions or virtual base classes.
Regarding the validity of pointers derived from conversions, the standard has this to say in the section on "Safely-derived pointers":
6.7.4.3 Safely-derived pointers
4. An implementation may have relaxed pointer safety, in which case the validity of a pointer value does not depend on whether it is a safely-derived pointer value. Alternatively, an implementation may have strict pointer safety, in which case a pointer value referring to an object with dynamic storage duration that is not a safely-derived pointer value is an invalid pointer value unless the referenced complete object has previously been declared reachable. [ Note: The effect of using an invalid pointer value (including passing it to a deallocation function) is undefined, see 6.7.4.2. This is true even if the unsafely-derived pointer value might compare equal to some safely-derived pointer value. —end note ] It is implementation-defined whether an implementation has relaxed or strict pointer safety.
Yes, It does have undefined behavior. The layout about suboject of Base in A and B is undefined. x may be not a real Base oject.
If A and B are a verbatim copy of each other (except for their names) and are declared in the same context (same namespace, same #defines, no __LINE__ usage), then common C++ compilers (gcc, clang) will produce two binary representations which are fully interchangeable.
If A and B use the same method signatures but the bodies of corresponding methods differ, it is unsafe to cast A* to B* because the optimization pass in the compiler could for example partially inline the body of void B::method() at the call site b->method() while the programmer's assumption could be that b->method() will call A::method(). Therefore, as soon as the programmer uses an optimizing compiler the behavior of accessing A through type B* becomes undefined.
Problem: All compilers are always at least to some extent "optimizing" the source code passed to them, even at -O0. In cases of behavior not mandated by the C++ standard (that is: undefined behavior), the compiler's implicit assumptions - when all optimizations are turned off - might differ from programmer's assumptions. The implicit assumptions have been made by the developers of the compiler.
Conclusion: If the programmer is able to avoid using an optimizing compiler then it is safe to access A via B*. The only issue such a programmer needs to tackle with is that non-optimizing compilers do not exist.
A managed C++ implementation might abort the program when A* is casted to B* via reinterpret_cast, when b->field is accessed, or when b->method() is called. Some other managed C++ implementation might try harder to avoid a program crash and so it will resort to temporary duck typing when it sees the program accessing A via B*.
Some questions are:
Can the programmer guess what the managed C++ implementation will do in cases of behavior not mandated by the C++ standard?
What if the programmer sends the code to another programmer who will pass it to a different managed C++ implementation?
If a case isn't covered by the C++ standard, does it mean that a C++ implementation can choose to do anything it considers appropriate in order to cope with the case?
In the following example the compiler accepts the static_cast downcast resulting in undefined behavior while I thought static_cast was all about safety (that C-style casts were unable to provide).
#include <iostream>
class Base {
public:
int x = 10;
};
class Derived1: public Base
{
public:
int y = 20;
};
class Derived2 : public Base
{
public:
int z = 30;
int w = 40;
};
int main() {
Derived1 d1;
Base* bp1 = static_cast<Base*>(&d1);
Derived2* dp1 = static_cast<Derived2*>(bp1);
std::cout << dp1->z << std::endl; // outputs 20
std::cout << dp1->w << std::endl; // outputs random value
}
You use dynamic_cast only really when you are not sure if the cast is going to succeed and you catch exceptions or check for nullptr. However if you are sure your downcasting is going to succeed, the language allows you to use static_cast (which is cheaper). If you were wrong, that is your problem. In an ideal world every cast would succeed in 100% of the time. But we don't live in an ideal world. It's a bit like array subscript. arr[5] means "I am absolutely sure this array has at least 6 elements. Compiler doesn't need to check". If your array was smaller than you expected, that's again your problem.
I thought static_cast was all about safety (that C style cast were unable to provide)
static_cast is safer than a C-style cast. But not because it's impossible to go wrong with it. It's safer because it's only less likely to go wrong. When we write a C-style cast, a compiler will go through this list to appease us:
When the C-style cast expression is encountered, the compiler attempts
to interpret it as the following cast expressions, in this order:
const_cast<new_type>(expression);
static_cast<new_type>(expression), with extensions: pointer or reference to a derived class is additionally allowed to be cast to pointer or reference to unambiguous base class (and vice versa) even if the base class is inaccessible (that is, this cast ignores the private inheritance specifier). Same applies to casting pointer to member to pointer to member of unambiguous non-virtual base;
static_cast (with extensions) followed by const_cast;
reinterpret_cast<new_type>(expression);
reinterpret_cast followed by const_cast.
The first choice that satisfies the requirements of the respective
cast operator is selected, even if it cannot be compiled (see
example). If the cast can be interpreted in more than one way as
static_cast followed by a const_cast, it cannot be compiled. In
addition, C-style cast notation is allowed to cast from, to, and
between pointers to incomplete class type. If both expression and
new_type are pointers to incomplete class types, it's unspecified
whether static_cast or reinterpret_cast gets selected.
The point in favoring static_cast to that, is that you have a finer grained control over the resulting cast, which does grant a measure of added safety. But it doesn't change the fact that the C++ object model requires that static_cast support casting even when undefined behavior is possible. Only dynamic_cast (not on the above list, by the way) has an added bit of safety for polymorphic types, but that's not without overhead.
I don't really know what to tell you. Why does it allow such a cast? For when you need/want one.
Don't want to use it? Don't! You could switch to dynamic_cast (more expensive), or don't cast.
C++ lets you do plenty of things that require thought and care. This is one of them.
But it is still safer than C. The static_cast won't let you cast bp1 to an UrgleBurgle*, for example.
Of course ultimately you can still use the C-style casts if you like. I mean, I wouldn't advise it, but you could. C++ is all about choice (usually between a terrible option and a slightly less terrible option).
How does static_cast work? If you are doing something like...
If D inherits from B via some unspecified hierarchy (not necessarily directly), and you do:
B* b = new D();
D* d = static_cast<D*>(b);
what is happening? Is it simply calculating an offset at compile time and applying that offset to the pointer? Or is there something happening at runtime in order to do the cast?
what is happening?
The compiler assumes that you know what you're doing, so that the pointer really does point to a D object, and changes the pointer type accordingly, adjusting the value if necessary to point to the complete D object rather than the B sub-object.
If you get it wrong, and use a D* pointer that doesn't really point to a D object, then you'll get undefined behaviour; so be careful.
Is it simply calculating an offset at compile time and applying that offset to the pointer?
Yes.
Or is there something happening at runtime in order to do the cast?
No; "static" implies that it uses only compile-time information. The only runtime activity is adding the fixed offset if necessary.
Use dynamic_cast if you want a runtime check that the conversion is valid (as long as the types are polymorphic). It will give a null pointer (or throw a bad_cast exception if you're casting a reference rather than a pointer) if there isn't really a D object there.
I'm familiar with how to do a dynamic cast in C++, as follows:
myPointer = dynamic_cast<Pointer*>(anotherPointer);
But how do you make this a "safe" dynamic cast?
When dynamic_cast cannot cast a pointer because it is not a complete object of the required class it returns a null pointer to indicate the failure.
If dynamic_cast is used to convert to a reference type and the conversion is not possible, an exception of type bad_cast is thrown instead.
Q But how do you make this a "safe" dynamic cast?
A It will be a safe dynamic cast as long as the argument to dynamic_cast is a valid pointer (including NULL). If you pass a dangling pointer or a value that is garbage, then the call to dynamic_cast is not guaranteed to be safe. In fact, the best case scenario is that the run time system throws an exception and you can deal with it. The worst case scenario is that it is undefined behavior. You can get one behavior now and a different behavior next time.
Most ways in which you might attempt to abuse dynamic_cast result in a compiler error (for example, trying to cast to a type that's not in a related polymorphic hierarchy).
There are also two runtime behaviours for times when you effectively use dynamic_cast to ask whether a particular pointer actually addresses an object of a specific derived type:
if (Derived* p = dynamic_cast<Derived*>(p_base))
{
...can use p in here...
}
else
...p_base doesn't point to an object of Derived type, nor anything further
derived from Derived...
try
{
Derived& d = dynamic_cast<Derived&>(*p_base);
...use d...
}
catch (std::bad_cast& e)
{
...wasn't Derived or further derived class...
}
The above is "safe" (defined behaviour) as long as p_base is either nullptr/0 or really does point to an object derived from Base, otherwise it's Undefined Behaviour.
Additionally, there is a runtime-unsafe thing you can do with a dynamic_cast<>, yielding Undefined Behaviour:
Standard 12.7/6: "If the operand of the dynamic_cast refers to the object under construction or destruction and the static type of the operand is not a pointer to or object of the constructor or destructor’s own class or one of its bases, the dynamic_cast results in undefined behavior.". The Standard provides an example to illustrate this.
I am trying to learn about static_cast and reinterpret_cast.
If I am correct the standard (9.2.18) says that reinterpret_cast for pod data is safe:
A pointer to a POD-struct object,
suitably converted using a
reinterpret_cast, points to its
initial member (or if that member is a
bit-field, then to the unit in which
it resides) and vice versa. [ Note: There might therefore be unnamed
padding within a POD-struct object, but not at its beginning, as necessary to achieve
appropriate alignment. — end
note ]
My question is how strictly to interpret this. Is, for example, layout-compatibility enough? and if not, why not?
To me, the following example shows an example where a strict 'only POD is valid' interpretation seems to be wrong.
class complex_base // a POD-class (I believe)
{
public:
double m_data[2];
};
class complex : public complex_base
{ //Not a POD-class (due to constructor and inheritance)
public:
complex(const double real, const double imag);
}
double* d = new double[4];
//I believe the following are valid because complex_base is POD
complex_base& cb1 = reinterpret_cast<complex_base&>(d[0]);
complex_base& cb2 = reinterpret_cast<complex_base&>(d[2]);
//Does the following complete a valid cast to complex even though complex is NOT POD?
complex& c1 = static_cast<complex&>(cb1);
complex& c2 = static_cast<complex&>(cb2);
Also, what can possibly break if complex_base::m_data is protected (meaning that complex_base is not pod)? [EDIT: and how do I protect myself/detect such breakages]
It seems to me that layout-compatibility should be enough - but this does not seem to be what the standard says.
EDIT:
Thanks for the answers. They also helped me find this,
http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2007/n2342.htm
I believe the following are valid because complex_base is POD
You are wrong. d[0] does not refer to the first member of a complex_base object. Its alignment may therefor not be good enough for a complex_base object, therefor such a cast is not safe (and not allowed by the text you quote).
Does the following complete a valid cast to complex even though complex is NOT POD?
cb1 and cb2 do not point to subobjects of an object of type complex, therefor the static_cast produces undefined behavior. Refer to 5.2.9p5 of C++03
If the lvalue of type "cv1 B" is actually a sub-object of an object of type D, the lvalue refers to the enclosing object of type D. Otherwise, the result of the cast is undefined.
It's not enough if merely the types involved fit together. The text talks about a pointer pointing to a POD-struct object and about an lvalue referring to a certain subobject.
oth complex and complex_base are standard-layout objects. The C++0x spec says, instead of the text you quoted:
Is POD-ness requirement too strict?
This is a different question, not regarding your example code. Yes, requiring POD-ness is too strict. In C++0x this was recognized, and a new requirement which is more loose, "standard-layout" is given. I do think that both complex and complex_base are standard-layout classes, by the C++0x definition. The C++0x spec says, instead of the text you quoted:
A pointer to a standard-layout struct object, suitably converted using a reinterpret_cast, points to its initial member (or if that member is a bit-field, then to the unit in which it resides) and vice versa.
I interpret that as allowing to cast a pointer to a double, which actually points to a complex member (member by inheritance), to be casted to a complex*. A Standard-layout class is one that either has no base classes containing non-static data, or has only one base-class containing non-static data. Thus there is an unique "initial member".
What can break is that non-POD class instances may have vtable pointers, in order to implement virtual dispatch, if they have any virtual functions, including the virtual dtor. The vtbl pointer will typicaly be the first member of the non-POD class.
(Technically, virtual dispatch doesn't have to implemented this way; practically it is. That's why the Standard has to be so strict about what qualifies as a POD type.)
I'm honestly not sure why just having a ctor ("8.5.1(1): "An aggregate is an array or class (clause 9) with no user-declared constructors (12.1)") disqualifies something from being a POD. But, it does.
In the particular case you have here, of course, there's no need for a reinterpret cast. Instead, just add a conversion operator to the base class:
class complex_base // a POD-class (I believe)
{
public:
double m_data[2];
operator double*() {
return m_data;
}
};
complex_base b; // create a complex_base
double* p = b;
Since a complex_base isn't a double*, the C++ compiler will apply one (and only one) user-defined conversion operator in order to assign b to p. That means that p = b invokes the conversion operator, yielding p = b.operator double*() (and note that that's actually legal syntax -- you can directly call conversion operators, not that you should), which of course does whatever it does, in this case return m_data.
Note that this is questionable, as we now have direct access to b's internals. In practice, we might return a const double*, or a copy, or a smart copy-on-write "pointer" or ....
Of course, in this case, m_data is public anyway, so we're not worse off than if we just wrote:
double* p = b.m_data;
We are a bit better off, actually, because clients of complex_base don't need to know how to convert it to a double.