Safely casting pointer types, purpose of static_cast, etc.

Discussion in 'C++' started by jason.cipriani@gmail.com, Jun 5, 2008.

  1. Guest

    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
    , Jun 5, 2008
    #1
    1. Advertising

  2. Jayesh Shah Guest

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

    On Jun 4, 4:21 pm, ""
    <> wrote:
    > 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
    Jayesh Shah, Jun 5, 2008
    #2
    1. Advertising

  3. Jerry Coffin Guest

    In article <d5ce22d0-225b-42e4-8d6c-
    >,
    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.

    --
    Later,
    Jerry.

    The universe is a figment of its own imagination.
    Jerry Coffin, Jun 5, 2008
    #3
  4. James Kanze Guest

    On Jun 5, 1:21 am, ""
    <> wrote:
    > 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.

    --
    James Kanze (GABI Software) email:
    Conseils en informatique orientée objet/
    Beratung in objektorientierter Datenverarbeitung
    9 place Sémard, 78210 St.-Cyr-l'École, France, +33 (0)1 30 23 00 34
    James Kanze, Jun 5, 2008
    #4
  5. James Kanze Guest

    On Jun 5, 1:21 am, ""
    <> wrote:

    [...]
    > 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*.

    --
    James Kanze (GABI Software) email:
    Conseils en informatique orientée objet/
    Beratung in objektorientierter Datenverarbeitung
    9 place Sémard, 78210 St.-Cyr-l'École, France, +33 (0)1 30 23 00 34
    James Kanze, Jun 5, 2008
    #5
  6. Guest

    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
    , Jun 8, 2008
    #6
    1. Advertising

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

It takes just 2 minutes to sign up (and it's free!). Just click the sign up button to choose a username and then you can ask your own questions on the forum.
Similar Threads
  1. Suzanne Vogel
    Replies:
    5
    Views:
    585
    Alf P. Steinbach
    Aug 29, 2003
  2. Bo Peng
    Replies:
    11
    Views:
    1,050
    Victor Bazarov
    Oct 20, 2006
  3. Replies:
    11
    Views:
    1,379
    James Kanze
    Jun 7, 2007
  4. ittium
    Replies:
    4
    Views:
    275
    Goran
    Dec 9, 2011
  5. junyangzou
    Replies:
    13
    Views:
    241
Loading...

Share This Page