77

In C++, the T q = dynamic_cast<T>(p); construction performs a runtime cast of a pointer p to some other pointer type T that must appear in the inheritance hierarchy of the dynamic type of *p in order to succeed. That is all fine and well.

However, it is also possible to perform dynamic_cast<void*>(p), which will simply return a pointer to the "most derived object" (see 5.2.7::7 in C++11). I understand that this feature probably comes out for free in the implementation of the dynamic cast, but is it useful in practice? After all, its return type is at best void*, so what good is this?

Kerrek SB
  • 464,522
  • 92
  • 875
  • 1,084
  • 8
    Just a guess, but couldn't that be used to unambiguously determine object identity? – Björn Pollex Nov 14 '11 at 15:24
  • @BjörnPollex: But so would `p`... is there a situation where `p1 == p2`, but `dynamic_cast(p1) != dynamic_cast(p2)`? – Kerrek SB Nov 14 '11 at 15:25
  • 8
    Ah, I see what you mean: We could have `p1 != p2`, but in fact they point the same object. I guess if we had an index keyed on `void *` that'd make sense. (Though the void pointer itself would no longer be usable.) – Kerrek SB Nov 14 '11 at 15:27
  • I get "error: cannot cast ptr type Base* to void* (source type is not polymorphic)." when I try to compile code with that cast - does it work on your system? And sorry for deleting original comment - I wanted to double check something :) – John Humphreys Nov 14 '11 at 15:33
  • 2
    @BjörnPollex: You should beef that comment up into an answer -- it sounds like a reasonable idea, surely worth having a post. – Kerrek SB Nov 14 '11 at 15:41
  • Whoops, my bad :) hadn't thought that through far enough - thanks for the pointers. – John Humphreys Nov 14 '11 at 16:08
  • @Anonymous downvoter, care to explain your objection to this question? – Kerrek SB Nov 20 '11 at 22:23
  • I don't think this is worthy of posting as an answer to a bounty question, but this seems like one way to acquire an opaque handle that can be passed out of an ABI. – John Dibling Nov 22 '11 at 16:49
  • @JohnDibling: An interesting thought is always worth a post :-) If you could show a small, hypothetical example, I'd be curious to see it. – Kerrek SB Nov 22 '11 at 16:55

7 Answers7

73

The dynamic_cast<void*>() can indeed be used to check for identity, even if dealing with multiple inheritance.

Try this code:

#include <iostream>

class B {
public:
    virtual ~B() {}
};

class D1 : public B {
};

class D2 : public B {
};

class DD : public D1, public D2 {
};

namespace {
    bool eq(B* b1, B* b2) {
        return b1 == b2;
    }

    bool eqdc(B* b1, B *b2) {
        return dynamic_cast<void*>(b1) == dynamic_cast<void*>(b2);
    }
};

int
main() {
    DD *dd = new DD();
    D1 *d1 = dynamic_cast<D1*>(dd);
    D2 *d2 = dynamic_cast<D2*>(dd);

    std::cout << "eq: " << eq(d1, d2) << ", eqdc: " << eqdc(d1, d2) << "\n";
    return 0;
}

Output:

eq: 0, eqdc: 1
Christian Rau
  • 45,360
  • 10
  • 108
  • 185
mitchnull
  • 6,161
  • 2
  • 31
  • 23
  • I'm awarding the bounty to this question mainly because I feel that none of the other answers added anything profoundly new. That wasn't the original intention, but I didn't want to let the bounty go to waste. If anyone comes up with a new good answer (or edits an existing answer), I'll be happy to start another "reward" bounty! – Kerrek SB Nov 23 '11 at 17:29
7

Bear in mind that C++ lets you do things the old C way.

Suppose I have some API in which I'm forced to smuggle an object pointer through the type void*, but where the callback it's eventually passed to will know its dynamic type:

struct BaseClass {
    typedef void(*callback_type)(void*);
    virtual callback_type get_callback(void) = 0;
    virtual ~BaseClass() {}
};

struct ActualType: BaseClass {
    callback_type get_callback(void) { return my_callback; }

    static void my_callback(void *p) {
        ActualType *self = static_cast<ActualType*>(p);
        ...
    }
};

void register_callback(BaseClass *p) {
   // service.register_listener(p->get_callback(), p); // WRONG!
   service.register_listener(p->get_callback(), dynamic_cast<void*>(p));
}

The WRONG! code is wrong because it fails in the presence of multiple inheritance (and isn't guaranteed to work in the absence, either).

Of course, the API isn't very C++-style, and even the "right" code can go wrong if I inherit from ActualType. So I wouldn't claim that this is a brilliant use of dynamic_cast<void*>, but it's a use.

Steve Jessop
  • 273,490
  • 39
  • 460
  • 699
  • 3
    Well, but `my_callback` could say `dynamic_cast(static_cast(p))`, non? In other words, we could use `BaseClass*` as the basis for the C wrapper. – Kerrek SB Nov 14 '11 at 16:10
  • @Kerrek: yes, I think you could. For that matter, you could add another virtual function `BaseClass::get_this_for_callback`, and let each derived class have complete control how to package the pointer. – Steve Jessop Nov 14 '11 at 16:19
  • @SteveJessop : Just curious, if `p` is being passed back through a `void*` in `my_callback`, why would it matter if `p` is cast to the most derived type as the second argument of `register_listener` in the presence of multiple inheritance? I ask because inside of `my_callback` you're doing a `static_cast` to an `ActualType*`, so it does not seem like it really matters if a pointer to the most derived type, or a pointer to the base-class is past to `my_callback` ... either way it will end up being a pointer to an `ActualType` object, right? – Jason Nov 20 '11 at 10:26
  • @Jason: no. With multiple inheritance, and assuming some bases aren't empty, then the address of at least one of the bases is different from the address of the most-derived object. The static cast from `void*` to `ActualType*` only results in the right pointer value if the input is the address of an `ActualType` object, so if `BaseClass` happens to be the base that's at a different address, then it will go wrong. – Steve Jessop Nov 20 '11 at 17:44
  • @Jason Even without MI: casting from `void*` to `T*` is only valid if the value came from `T*` to `void*`. `Derived*` to `Base*` to `void*` to `Derived*` has undefined behaviour (might probably work without MI). – curiousguy Nov 23 '11 at 09:08
  • @Steve This code... how to say it kindly? Children, please, don't do that. – curiousguy Nov 23 '11 at 09:14
  • @SteveJessop "_Of course, the API isn't very C++-style_" actually `std::ios_base` has an interface that uses `void*`, not exactly like you do, but in a very slightly more involved way: see "ios_base storage functions" [ios.base.storage] and "ios_base callbacks" [ios.base.callback]. But is iostream "C++ style"? – curiousguy Nov 24 '11 at 02:21
4

Casting pointers to void* has its importance since way back in C days. Most suitable place is inside the memory manager of Operating System. It has to store all the pointer and the object of what you create. By storing it in void* they generalize it to store any object on to the memory manager data structure which could be heap/B+Tree or simple arraylist.

For simplicity take example of creating a list of generic items(List contains items of completely different classes). That would be possible only using void*.

standard says that dynamic_cast should return null for illegal type casting and standard also guarantees that any pointer should be able to type cast it to void* and back from it with only exception of function pointers.

Normal application level practical usage is very less for void* typecasting but it is used extensively in low level/embedded systems.

Normally you would want to use reinterpret_cast for low level stuff, like in 8086 it is used to offset pointer of same base to get the address but not restricted to this.

Edit: Standard says that you can convert any pointer to void* even with dynamic_cast<> but it no where states that you can not convert the void* back to the object.

For most usage, its a one way street but there are some unavoidable usage.

It just says that dynamic_cast<> needs type information for converting it back to the requested type.

There are many API's that require you to pass void* to some object eg. java/Jni Code passes the object as void*.
Without type info you cannot do the casting.If you are confident enough that type requested is correct you can ask compiler to do the dynmaic_cast<> with a trick.

Look at this code:

class Base_Class {public : virtual void dummy() { cout<<"Base\n";} };
class Derived_Class: public Base_Class { int a; public: void dummy() { cout<<"Derived\n";} };
class MostDerivedObject : public Derived_Class {int b; public: void dummy() { cout<<"Most\n";} };
class AnotherMostDerivedObject : public Derived_Class {int c; public: void dummy() { cout<<"AnotherMost\n";} };

int main () {
  try {
    Base_Class * ptr_a = new Derived_Class;
    Base_Class * ptr_b = new MostDerivedObject;
    Derived_Class * ptr_c,*ptr_d;

        ptr_c = dynamic_cast< Derived_Class *>(ptr_a);
        ptr_d = dynamic_cast< Derived_Class *>(ptr_b);

        void* testDerived = dynamic_cast<void*>(ptr_c);
        void* testMost = dynamic_cast<void*>(ptr_d);
        Base_Class* tptrDerived = dynamic_cast<Derived_Class*>(static_cast<Base_Class*>(testDerived));
        tptrDerived->dummy();
        Base_Class* tptrMost = dynamic_cast<Derived_Class*>(static_cast<Base_Class*>(testMost));
        tptrMost->dummy();
        //tptrMost = dynamic_cast<AnotherMostDerivedObject*>(static_cast<Base_Class*>(testMost));
        //tptrMost->dummy(); //fails

    } catch (exception& my_ex) {cout << "Exception: " << my_ex.what();}
    system("pause");
  return 0;
}

Please correct me if this is not correct in any way.

Praveen
  • 331
  • 2
  • 9
  • +1 do you have a few links on that, as I am interested in such "low level stuff" – Sim Nov 17 '11 at 20:33
  • http://code.google.com/p/c-generic-library/ This link has code for generic data structure. – Praveen Nov 18 '11 at 07:30
  • 2
    You can't do a `dynamic_cast` on a `void*` according to the C++ standard section 5.2.7/2 ... So using `dynamic_cast` to recover the type of a `void*` in a generic data-structure won't work, even if that `void*` was generated through a `dynamic_cast` operation. The pointer the cast is being performed on must be a pointer to a complete class type. – Jason Nov 20 '11 at 10:08
  • Indeed. I'm afraid this answer is missing the point somewhat. I'm aware of general void pointers. The question, however, is specifically about `dynamic_cast` on a pointer to a polymorphic class. – Kerrek SB Nov 20 '11 at 12:35
  • Indeed `dynamic_cast<>` is one way street but you can get the object back from it if you are sure about the type info of the object. Apart from it there is no portable usage of it with out type info other than comparing the pointers. But it is there as language feature because it is possible to do so, Language can only warrant you about its usage. The same goes with `reinterpret_cast<>`. – Praveen Nov 21 '11 at 20:44
  • @Praveen: Thanks a lot for your update. I hope you don't take this the wrong way, but I'm still not convinced that this answer addresses the polymorphic nature of the question. The dynamic cast is a non-trivial operation that does a lot of work and changes the actual numerical value of the pointer. I could try and make the question more precise if it isn't clear what I'm driving at. My question specifically about polymorphic uses of dynamic-cast-to-void-pointer. – Kerrek SB Nov 22 '11 at 16:59
1

Expanding on @BruceAdi's answer and inspired by this discussion, here's a polymorphic situation which may require pointer adjustment. Suppose we have this factory-type setup:

struct Base { virtual ~Base() = default; /* ... */ };
struct Derived : Base { /* ... */ };

template <typename ...Args>
Base * Factory(Args &&... args)
{
    return ::new Derived(std::forward<Args>(args)...);
}

template <typename ...Args>
Base * InplaceFactory(void * location, Args &&... args)
{
    return ::new (location) Derived(std::forward<Args>(args)...);
}

Now I could say:

Base * p = Factory();

But how would I clean this up manually? I need the actual memory address to call ::operator delete:

void * addr = dynamic_cast<void*>(p);

p->~Base();              // OK thanks to virtual destructor

// ::operator delete(p); // Error, wrong address!

::operator delete(addr); // OK

Or I could re-use the memory:

void * addr = dynamic_cast<void*>(p);
p->~Base();
p = InplaceFactory(addr, "some", "arguments");

delete p;  // OK now
Community
  • 1
  • 1
Kerrek SB
  • 464,522
  • 92
  • 875
  • 1,084
1

it is usefull when we put the storage back to memory pool but we only keep a pointer to the base class. This case we should figure out the original address.

BruceAdi
  • 11
  • 1
  • Hm... can you elaborate on a situation where this would be useful? – Kerrek SB Nov 22 '11 at 03:10
  • This makes more sense to me now: You can say `p->~T();` followed by placement-new via some hinted factory like `T::create_inplace(dynamic_cast(copy_of_p));`. – Kerrek SB Aug 09 '12 at 14:18
0

This might be one way to provide an Opaque Pointer through an ABI. Opaque Pointers -- and, more generally, Opaque Data Types -- are used to pass objects and other resources around between library code and client code in such a way that the client code can be isolated from the implementation details of the library. There are other ways to accomplish this, to be sure, and maybe some of them would be better for a particular use case.

Windows makes a lot of use of Opaque Pointers in its API. HANDLE is, I believe, generally an opaque pointer to the actual resource you have a HANDLE to, for example. HANDLEs can be Kernel Objects like files, GDI objects, and all sorts of User Objects of various kinds -- all of which must be vastly different in implementation, but all are returned as a HANDLE to the user.

#include <iostream>
#include <string>
#include <iomanip>
using namespace std;


/*** LIBRARY.H ***/
namespace lib
{
    typedef void* MYHANDLE;

    void        ShowObject(MYHANDLE h);
    MYHANDLE    CreateObject();
    void        DestroyObject(MYHANDLE);
};

/*** CLIENT CODE ***/
int main()
{
    for( int i = 0; i < 25; ++i )
    {
        cout << "[" << setw(2) << i << "] :";
        lib::MYHANDLE h = lib::CreateObject();
        lib::ShowObject(h);
        lib::DestroyObject(h);
        cout << "\n";
    }
}

/*** LIBRARY.CPP ***/
namespace impl
{
    class Base { public: virtual ~Base() { cout << "[~Base]"; } };
    class Foo   : public Base { public: virtual ~Foo() { cout << "[~Foo]"; } };
    class Bar   : public Base { public: virtual ~Bar() { cout << "[~Bar]"; } };
};

lib::MYHANDLE lib::CreateObject()
{
    static bool init = false;
    if( !init )
    {
        srand((unsigned)time(0));
        init = true;
    }

    if( rand() % 2 )
        return static_cast<impl::Base*>(new impl::Foo);
    else
        return static_cast<impl::Base*>(new impl::Bar);
}

void lib::DestroyObject(lib::MYHANDLE h)
{
    delete static_cast<impl::Base*>(h);
}

void lib::ShowObject(lib::MYHANDLE h)
{
    impl::Foo* foo = dynamic_cast<impl::Foo*>(static_cast<impl::Base*>(h));
    impl::Bar* bar = dynamic_cast<impl::Bar*>(static_cast<impl::Base*>(h));

    if( foo ) 
        cout << "FOO";
    if( bar )
        cout << "BAR";
}
Robert Harvey
  • 178,213
  • 47
  • 333
  • 501
John Dibling
  • 99,718
  • 31
  • 186
  • 324
  • Interesting - but are you sure that `static_cast(dynamic_cast(pointer_to_base))` is correct? For example, the resulting pointer can no longer be dynamically-cast to to void-pointer, and in fact it's not usable anymore at all. – Kerrek SB Nov 22 '11 at 18:47
  • `dynamic_cast(new impl::Foo)` hug? – curiousguy Nov 23 '11 at 09:19
  • @Kerrek: I am pretty sure the code is correct, and does not exhibit UB. See for example: 5.2.9/10: "An rvalue of type “pointer to cv1 void” can be converted to an rvalue of type “pointer to cv2 T,” where T is an object type and cv2 is the same cv-qualification as, or greater cv-qualification than, cv1. A value of type pointer to object converted to “pointer to cv void” and back to the original pointer type will have its original value." – John Dibling Nov 23 '11 at 14:56
  • @JohnDibling: I think I managed to cause a crash with that construction, though. Imagine: If you have a `Base * p;` that points to a derived object `x`, then `static_cast(dynamic_cast(p))` is the same as `reinterpret_cast(&x)`, and *not* `static_cast(&x)`. – Kerrek SB Nov 23 '11 at 15:48
  • @Kerrek: Right -- you would need to `dynamic_cast` the `Base*` up (down? lol) to `Derived*`, and **then** get a `void*` – John Dibling Nov 23 '11 at 15:57
  • Many issues with this code: 1) `dynamic_cast (new T)` means `new T` by definition. 2) `reinterpret_cast (handle)` should be `static_cast (handle)` (`handle` is a `void*`) 3) after you convert `impl::Foo*` (resp `impl::Bar*`) to `void*`, the **only** thing you can do that does not involved UB is to convert it back to `impl::Foo*` (resp `impl::Bar*`); but you cannot do that, as you don't even know if you had a Foo or a Bar! 4) I will edit the code to make it correct – curiousguy Nov 24 '11 at 01:51
  • "_See for example: 5.2.9/10: "A value of type pointer to object converted to “pointer to cv void” and **back to the original pointer type** will have its original value._" This is not what you are doing; this is what **I** am doing in the edited version I propose you. – curiousguy Nov 24 '11 at 01:58
  • BTW, I don't know if the editing is the "right" way to propose a code change, but I don't know any other way! – curiousguy Nov 24 '11 at 02:00
  • @curiousguy: I have no objections to your edit if they bring my code closer to what I intended. Beware though. Many people would be very offended. – John Dibling Nov 24 '11 at 02:43
  • @JohnDibling "_if they bring my code closer to what I intended_" Did you intend to provide correct code that uses a HANDLE interface, or some code with `dynamic_cast` in it? – curiousguy Nov 24 '11 at 04:28
  • @curiousguy: I intended to provide code that used `dynamic_cast` because that was the basis of the OP. Originally I meerly commented to the OP that I though this could be a way to provide opaque pointers, but that I didn't think that comment was worthy of an answer. But then OP specifically asked me to post a more complete answer, so that's what I did. I am not 100% confident that my code was correct as far as the language is concerned, but I did research relevant sections in the Standard before posting, and came to the conclusion that it probably was correct. – John Dibling Nov 25 '11 at 15:36
  • @RobertHarvey why did you revert my **proposed** edit that was **accepted by the author**? – curiousguy Nov 25 '11 at 22:18
  • @JohnDibling why do you write `dynamic_cast(new impl::Foo);` instead of just `new impl::Foo`? – curiousguy Nov 25 '11 at 22:19
  • @curiousguy Because I can't figure out what's going on here, and neither could the flagger. If the sentence is wrong, just fix it. – Robert Harvey Nov 25 '11 at 23:13
  • @RobertHarvey "_I can't figure out what's going on here, and neither could the flagger._" the explanation is in the comments. The proposed code is not correct C++, and so I submitted a fix, and the author accepted the fix. And the fixed program does not even use `dynamic_cast`, so the phrase is meaningless, so I ``eleted it. – curiousguy Nov 26 '11 at 00:44
  • I did not accept the edit. As I've said, my intent was to demonstrate how `dynamic_cast` can be used to provide an opaque pointer. Since your edit does not use `dynamic_cast` it cannot accomplish this goal. In fact, with your edit my answer is no longer applicable to the original question. I don't know who flagged this or why, but it is clear that the edit should be rolled back. – John Dibling Nov 28 '11 at 14:34
  • Moreover, I am not convinced that my original code is not correct. – John Dibling Nov 28 '11 at 14:35
0

Don't do that at home

struct Base {
    virtual ~Base ();
};

struct D : Base {};

Base *create () {
    D *p = new D;
    return p;
}

void *destroy1 (Base *b) {
    void *p = dynamic_cast<void*> (b);
    b->~Base ();
    return p;
}

void destroy2 (void *p) {
    operator delete (p);
}

int i = (destroy2 (destroy1 (create ())), i);

Warning: This will not work if D is defined as:

struct D : Base {
    void* operator new (size_t);
    void operator delete (void*);
};

and there is no way to make it work.

curiousguy
  • 8,038
  • 2
  • 40
  • 58