-2

I'm stuck trying to cast FROM a shared_ptr<void>. I know it is a shared_ptr<A> or a shared_ptr<B>, but I can not find how to check which one it is. A and B are 2 different not related classes.

I would like to do something like:

//x.valuePtr is a shared_ptr<void>
if(x.valuePtr is shared_ptr<A>){
  ... do things with the pointer to an A object
} else if (x.valuePtr is shared_ptr<B>){
  ... do things with the pointer to an B object
}

I have a property (of a class I can not change) which is of type shared_ptr<void>. In most cases I know which is the real type of that pointer so I can use static_pointer_cast without problems. For example:

// If I know x.valuePtr is shared_ptr<A>
x.valuePtr = std::make_shared<A>();

// I can use static_pointer_cast somewhere else in the application and it works fine
std::shared_ptr<A> a_ptr = std::static_pointer_cast<A> (x.valuePtr);

// Same happens when I know it is a shared_ptr<B>
x.valuePtr = std::make_shared<B>();
std::shared_ptr<B> b_ptr = std::static_pointer_cast<B> (x.valuePtr);

But now I have a problem because it can be either of them (a shared_ptr<A> or a shared_ptr<B>). If I use static_pointer_cast<A> (or <B>), that line compiles and throws no exception, but it does throw an exception as soon as I try to use something specific from the casted ptr if it was the wrong type. For example:

  // If x.valuePtr is shared_ptr<A>
  x.valuePtr = std::make_shared<A>();

  // But if I try to cast it to shared_ptr<B> somewhere in the application where it could be shared_ptr<A> or shared_ptr<B>
  std::shared_ptr<B> b_ptr = std::static_pointer_cast<B> (x.valuePtr); // this does no fail

  // It throws an exception when I try to use b_ptr. For example
  b_ptr->AMethodInB();

I tried doing different checks after the static_pointer_cast (for null, empty shared_ptr, etc) but nothing seems to work:

x.valuePtr = std::make_shared<A>();
std::shared_ptr<B> b_ptr = std::static_pointer_cast<B> (x.valuePtr); 
if(b_ptr == NULL || b_ptr.get() == NULL || !b_ptr){
   "It never gets into this line"
   "I would be able to try to cast it to shared_ptr<A>"
}

 b_ptr->AMethodInB(); // and keeps failing here

I also tried using the dynamic_pointer_cast<B>, but does no compile ("error: cannot dynamic_cast .... (source is not a pointer to class)"). Also I found it is not possible to dynamic_cast FROM void here

Is there any way I can do to check if the static_pointer_cast actually worked fine? Or to check somehow the real type of this shared_ptr<void>?

PD: I'm using c++11, gcc 4.8.2.

Community
  • 1
  • 1
MAB
  • 117
  • 2
  • 13
  • "it is a shared_ptr or a shared_ptr" -- that reads like nonsense. Please revise your question. Use the preview to verify that it looks the way you intend **before** hitting that "submit" button. – Kerrek SB Nov 26 '14 at 19:51
  • Sorry for the typos. Thanks for the edits :-) – MAB Nov 26 '14 at 19:56
  • There's no conversion from `void**` to `A*`. The question doesn't seem to make sense. – Kerrek SB Nov 26 '14 at 19:58
  • Are `A` and `B` related? If so, why do you go all the way down to `void*`? And how does `dynamic_pointer_cast` *not* fit your bill? Also, as Kerrek said, it should be `shared_ptr`. – Deduplicator Nov 26 '14 at 19:58
  • 2
    Do you really have a `shared_ptr` or is it a `shared_ptr`? You might be better off introducing a common base class for `A` and `B` and using a `shared_ptr` which can then be `dynamic_pointer_cast` to the appropriate type. – Praetorian Nov 26 '14 at 19:59
  • Yes sorry, its `shared_ptr` not `shared_ptr`. It is no fixed in the post. A and B are not related. I guess the valuePtr property is meant to be generic to hold a shared_ptr to any type of object, that why they did it that way. – MAB Nov 26 '14 at 20:06
  • @Deduplicator `dynamic_pointer_cast` doesn't work because `dynamic_pointer_cast(foo)` is specified to return `dynamic_cast(foo.get())`, and [expr.dynamic.cast]/2 says of `dynamic_cast(v)` "If `T` is a pointer type, `v` shall be a prvalue of a pointer to complete class type...". `void` is not a complete class type. – Casey Nov 26 '14 at 23:39
  • @Casey: It shall be a prvalue of a pointer to complete class type, **not** it shall be of type ... – Deduplicator Nov 26 '14 at 23:42
  • @Deduplicator Bah, typo. My comment should read: [expr.dynamic.cast]/2 says of `dynamic_cast(v)`: "If `T` is a pointer type, `v` shall be a prvalue of a pointer to complete class type...". `void` is not a complete class type, so a `void*` cannot be converted with `dynamic_cast`. – Casey Nov 26 '14 at 23:50
  • @Casey Don't forget §§1, which makes it obvious that we are really talking about the value, and `void*` is completely acceptable. – Deduplicator Nov 27 '14 at 00:18
  • @Deduplicator It's acceptable to `dynamic_cast` *to* `void*`, but not to `dynamic_cast` a `void*` to some other type, which is what the OP is trying to do. Doing so has undefined behavior since `void` is not a complete class type. – Casey Nov 27 '14 at 01:49
  • @Casey It's not UB. It's ill-formed, with a diagnostic required. – T.C. Nov 28 '14 at 08:33

2 Answers2

0

I feel strange about using std::shared_ptr<void> if you need to have type info.

May bee it is better to use template-based approach (some one name it type traits)?

struct A
{
    int id;
};

struct B
{
    int id1;
};

//Default template (neither A or B)
template<typename T>
struct Checker
{
    enum { isA = 0, isB = 0 };
};

//Template for A
template<>
struct Checker<A>
{
    enum { isA = 1, isB = 0 };
};

//Template for B
template<>
struct Checker<B>
{
    enum { isA = 0, isB = 1 };
};

//Convenience function (only thing it does in runtime is compare 2 ints)
template<typename T>
bool checkA(std::shared_ptr<T> p)
{
    return Checker<T>::isA == 1;
}

//Convenience function (only thing it does in runtime is compare 2 ints)
template<typename T>
bool checkB(std::shared_ptr<T> p)
{
    return Checker<T>::isB == 1;
}

//Convenience function
template<typename T>
void test(std::shared_ptr<T> p)
{
    std::cout
        << "is A : " << checkA(p)
        << " is B : " << checkB(p) << std::endl;
}

void test()
{
    //This is working
    std::shared_ptr<A> a = std::make_shared<A>();
    std::shared_ptr<B> b = std::make_shared<B>();
    test(a);
    test(b);

    //This is not working
    std::shared_ptr<void> a1 = std::make_shared<A>();
    std::shared_ptr<void> b1 = std::make_shared<B>();
    test(a1);
    test(b1);
}

The output is:

is A : 1 is B : 0
is A : 0 is B : 1
is A : 0 is B : 0
is A : 0 is B : 0
Holinov Ilya
  • 33
  • 1
  • 6
0

You should create a common base class (Base) for A and B. It is then possible to convert the shared_ptr<void> to shared_ptr<Base> with static_pointer_cast<Base>(...) and dynamic_pointer_cast the result to A or B.

It would probably be better to avoid the dynamic cast altogether by adding a pure virtual member function do_things to Base and overriding it in A and B with ... do things with the pointer to an A object and ... do things with the pointer to a B object.

Casey
  • 41,449
  • 7
  • 95
  • 125