Safely casting pointer types, purpose of static_cast, etc.

J

jason.cipriani

There have been some recent threads about casting pointers to and from
void* that have me rethinking some of my usual practices. I have a
couple of questions.

1. What is the purpose of C++'s static_cast<>? In other words, is
there any real difference between statements like (with non-pointer
types):

double a = 3.4;
int b = (int)a; // <--- this
int c = static_cast<int>(a); // <---

2. What about static cast with void*'s and pointers to class types, is
there any difference here, and also, are these conversions all safe:

Object *a = new Object;
void *b = a;
Object *c = (Object *)b;
Object *d = static_cast<Object *>(b);

In that code is there any difference between the conversion when
initializing c and d? And, are c/d guaranteed to be valid pointers to
the same object a points to?

3. If c/d are not guaranteed to be valid pointers, what is the correct
way to do that conversion in a situation where a void* must be used as
an intermediate variable to hold a pointer to an object (e.g. when
passing through a layer of C code)? For example, when creating a
thread with pthread_create, a void* parameter can be passed to the
thread function. So, then, is the following code guaranteed to always
do what I want on any platform:

=== BEGIN EXAMPLE ===

class A {
public:
void CreateThread ();
private:
void * MyThreadProc_ ();
static void * SThreadProc_ (void *);
};

// creates a thread
void A::CreateThread () {
pthread_t tid;
// 4th param is void* param to pass to SThreadProc_.
pthread_create(&tid, NULL, &SThreadProc_, this);
}

// static thread function calls ((A*)va)->MyThreadProc_();
void * A::SThreadProc_ (void *va) {
A *a = (A *)va; // <--- is this always safe?
return a->MyThreadProc_();
}

=== END EXAMPLE ===

Thanks,

Jason
 
J

Jayesh Shah

There is not much difference but static casting is more
restrictive(safer) and noticeable.
 
J

Jerry Coffin

(e-mail address removed)>, (e-mail address removed)
says...

[ ... ]
1. What is the purpose of C++'s static_cast<>? In other words, is
there any real difference between statements like (with non-pointer
types):

double a = 3.4;
int b = (int)a; // <--- this
int c = static_cast<int>(a); // <---

There is no difference between these statements. For that matter,

int c = 3.4;

is allowed, and does exactly the same thing as well.

The reason for static_cast over a C-style cast is mostly that it is more
restricted, and does NOT support some more dangerous conversions that C-
style casts can do. For example, a C-style cast can do roughly the same
thing as a const_cast, casting away const-ness, or it can do roughly the
same thing as a reinterpret_cast, treating a pointer as if it pointed to
a different type of operand.
2. What about static cast with void*'s and pointers to class types, is
there any difference here, and also, are these conversions all safe:

Object *a = new Object;
void *b = a;
Object *c = (Object *)b;
Object *d = static_cast<Object *>(b);

In that code is there any difference between the conversion when
initializing c and d? And, are c/d guaranteed to be valid pointers to
the same object a points to?

Yes, the conversions to c and d are the same. A C-style cast does the
same thing as a static_cast when/if a static_cast can do the conversion.
A static_cast can't do anything new that a C-style cast can't. The
advantage of a static_cast is solely that it is more restricted, so you
can't, for one example, accidentally cast away const-ness and/or
reinterpret what a pointer points at -- for example:

int const *a;
char *b = (char *)a; // perfectly legal
char *b = static_cast<char *>(a); // not allowed

The C-style cast is casting away the const-ness AND reintrepting what's
pointed at as a char instead of an int. A static_cast simply can't do
that -- if you really want to cast away const-ness, you need to use
const_cast. If you want to reinterpret what's pointed at, you have to
use reinterpret_cast. If you want to do both, you have to use both:

char *c = const_cast<char *>(reinterpret_cast<char const *>(a));

The C-style cast can do so many different kinds of conversions, with
nothing to distinguish between them, that it's easy to accidentally do a
conversion you don't want along with the one(s) you did. The new-style
casts attempt to prevent that.
 
J

James Kanze

There have been some recent threads about casting pointers to
and from void* that have me rethinking some of my usual
practices. I have a couple of questions.
1. What is the purpose of C++'s static_cast<>?

To limit the types of casts which can be done. You can't
accidentally cast away const with it, for example. More
importantly, in a class hierarchy, a C style cast is a
static_cast if it is to a base or a derived, but a
reinterpret_cast (i.e. type punning) if it is to a sibling. (An
attempt to use static_cast in this case will cause compiler
error.) Also, static_cast (and dynamic_cast) respect the access
specifiers in an inheritance hierarchy; C style casts don't.
In other words, is there any real difference between
statements like (with non-pointer types):

Unless the types are pointers or references in an inheritance
hierarchy, there is not difference between a C style cast and a
static_cast, *if* the static_cast is legal. For non-pointer and
non-reference types, in fact, most code I've seen doesn't use
static_cast, but rather the function style casts, e.g.:
"MyClass( 43 )".
double a = 3.4;
int b = (int)a; // <--- this
int c = static_cast<int>(a); // <---

These two are totally indentical. Conceptually, I tend to think
of this as creating a new temporary object, rather than
accessing an existing object through a different type. In such
cases, I'll use a function style cast, or a C style cast with
the argument in parentheses, i.e. "int( a )" or "(int)( a )".
(The latter is necessary if the typename is not a single token,
e.g. "unsigned char".) In my mind, this is the syntax for
"creating a new, temporary object".

(Technically, a pointer cast also creates a new, temporary
object. But the new object is a pointer, and of course, for the
most part, when you use a pointer cast, you're concerned about
the access through the pointer.)
2. What about static cast with void*'s and pointers to class
types, is there any difference here, and also, are these
conversions all safe:
Object *a = new Object;
void *b = a;
Object *c = (Object *)b;
Object *d = static_cast<Object *>(b);
In that code is there any difference between the conversion
when initializing c and d? And, are c/d guaranteed to be valid
pointers to the same object a points to?

No difference, and yes. You can convert any pointer type to
void*, and convert it back *to* *the* *same* *type* without loss
of information. (Note that this more or less implies that void
pointers will be at least as large as any other pointer type.)
3. If c/d are not guaranteed to be valid pointers,

Irrelevant, because they are. I'd use static_cast in this case.
 
J

James Kanze

On Jun 5, 1:21 am, "(e-mail address removed)"

[...]
3. If c/d are not guaranteed to be valid pointers,

As I said, they are guaranteed to be valid, but I missed some
important issues in your example.
what is the correct way to do that conversion in a situation
where a void* must be used as an intermediate variable to hold
a pointer to an object (e.g. when passing through a layer of C
code)? For example, when creating a thread with
pthread_create, a void* parameter can be passed to the thread
function. So, then, is the following code guaranteed to always
do what I want on any platform:
=== BEGIN EXAMPLE ===
class A {
public:
void CreateThread ();
private:
void * MyThreadProc_ ();
static void * SThreadProc_ (void *);
};
// creates a thread
void A::CreateThread () {
pthread_t tid;
// 4th param is void* param to pass to SThreadProc_.
pthread_create(&tid, NULL, &SThreadProc_, this);
}
// static thread function calls ((A*)va)->MyThreadProc_();
void * A::SThreadProc_ (void *va) {
A *a = (A *)va; // <--- is this always safe?
return a->MyThreadProc_();
}
=== END EXAMPLE ===

First, this won't compile with a compliant compiler. The type
of the third parameter to pthread_create is ``extern "C" void*
(*) (void*)'', and a member function, even static, can never
have a type with ``extern "C"''. You *must* use a free function
for this.

Secondly, as I said in my previous answer, the type you get from
the void* *must* be the same type as you used to create it.
That's not a problem here, but it very much could be if you
derive. A common mistaken idiom is something like:

class ThreadBase
{
public:
virtual ~ThreadBase() {}
virtual void run() = 0 ;
} ;

extern "C" void*
threadStarter( void* p )
{
static_cast< ThreadBase* >( p )->run() ;
return NULL ;
}

class MyThread : public ThreadBase
{
public:
virtual void run() ;
// ...
} ;

and then somewhere:

MyThread t ;
pthread_t ti ;
pthread_create( &ti, NULL, &threadStarter, &t ) ;

This does *not* work. Or rather, it is undefined behavior,
which may seem to work in some frequent cases. The last line
must be:

pthread_create( &ti, NULL, &threadStarter,
static_cast< ThreadBase* >( &t ) ) ;

for the behavior to be guaranteed---the cast from void* is to
ThreadBase*, so the void* must have been created from a
ThreadBase*, and not a MyThread*.
 
J

jason.cipriani

Sorry, there's too much to quote, but thanks Jayesh, Jerry, James, for
the complete answers, and for keeping the non-J riff raff out as well.

- Jason
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,995
Messages
2,570,226
Members
46,816
Latest member
nipsseyhussle

Latest Threads

Top