开发者

Check if a ptr belongs to a virtual class?

开发者 https://www.devze.com 2023-02-21 08:08 出处:网络
My code was acting wonky and i was able to mini reproduce it with the code below. (codepad link) From http://www.cppreference.com/wiki/keywords/dynamic_cast

My code was acting wonky and i was able to mini reproduce it with the code below. (codepad link)

From http://www.cppreference.com/wiki/keywords/dynamic_cast

If you attempt to cast to a pointer type, and that type is not an actual type of the arg开发者_如何转开发ument object, then the result of the cast will be NULL.

From my understanding this_test should be null. It isnt. How do i check if that dummy ptr is actually a ptr to a dummy object?

#include <ios>
struct Dummy{ virtual void dummyfn(){} };

int main(){
Dummy* this_test = dynamic_cast<Dummy*>((Dummy*)0x123);
//assert(this_test==0);
cout << std::hex << this_test<<endl;
return 0;
}

output:

0x123


Wishful thinking... :)

I believe dynamic_cast only works for downcasts in polymorphic cases, not any cast whatsoever. It's not like the compiler stores type information for every single variable, so it can't do what you're thinking -- I'm pretty sure it's undefined behavior.


The issue is that dynamic_cast expects either:

  • a null pointer
  • a valid pointer

Here you can only offer it garbage, so it is useless, and not the cast you want.

If you are getting a void*, then you can use reinterpret_cast (better than a C-cast, because more visible) to cast it into another type:

void* p = 0x123;
Dummy* dummy = reinterpret_cast<Dummy*>(p);

Note: the presence or absence of virtual methods goes unnoticed here


EDIT: if you can modify the objects being passed...

Then try to use a common base class:

struct Base: private boost::noncopyable { virtual ~Base() = 0 }; Base::~Base() {}

And define the following helpers:

template <typename T>
void* to_void(T* t) {
  Base* base = t;
  return reinterpret_cast<void*>(base);
}

template <typename T>
T* from_void(void* p) {
  Base* base = reinterpret_cast<Base*>(p);
  return dynamic_cast<T*>(base);
}

The former is extremely important because of the possible pointer adjustment (which will probably only occur in the case of Multiple Inheritance).

Note: it's possible to use a fast_cast here if you don't use virtual inheritance or other RTTI stuff

template <typename T, typename U>
T* fast_cast(U* u) {
#ifdef NDEBUG
  return static_cast<T*>(u);
#else
  return dynamic_cast<T*>(u);
#endif
}

If this is not possible the following solutions are possible, but they are going to feel hacky I fear.

Since dynamic_cast is not going to work properly here, you have to actually come up with your own type checking mechanism.

One method could be to use a "repository" in which you register the void* pointers you get, and the associated type_info object.

typedef std::map<void*, std::type_info const*> Repository;

template <typename Dest>
Dest* dynamic_check(void* p, Repository const& rep) {
  Repository::const_iterator it = rep.find(p);
  assert(it != rep.end() && "dynamic_check: no such entry");

  assert(typeid(Dest) == *(it->second) && "dynamic_check: wrong type");

  return reinterpret_cast<Dest*>(p);
}

If this is not possible, then you could hack the C++ object model to your advantage. If you know that the object has at least one virtual method, then it necessarily has a virtual pointer on all compilers I know (VS, gcc, clang), and this pointer is the first 4/8 bytes of the object.

inline void* virtual_pointer(void* p) {
  assert(p != 0 && "virtual_pointer: null");
  return reinterpret_cast<void*>(*p);
}

template <typename T>
void* virtual_pointer(T const& t) {
  return virtual_pointer(reinterpret_cast<void*>(&t));
}

template <typename T>
void* virtual_pointer() {
  static void* pointer = virtual_pointer(T());
  return pointer;
}


template <typename Dest>
Dest* dynamic_check(void* p) {
  assert(virtual_pointer<Dest>() == virtual_pointer(p));
  return reinterpret_cast<Dest*>(p);
}

Note: both solutions suffer from the same shortcoming, they will only work if you precise the exact type (well, you could get away with it as long as two types share the same virtual table, which happens if a derived class does not override any virtual method, including the destructor).

This is far from the power of a true dynamic_cast.


You skipped one sentence from your quote:

The dynamic_cast keyword casts object from one pointer or reference type to another, performing a runtime check to ensure the validity of the cast.

The problem here is that 0x123 isn't a pointer to an object, so it just doesn't work.


Actually dynamic_cast only works on polymorphic types (usually this means they must have a vtable). Since you're using a C-cast to assert to the compiler that the type is Dummy*, it believes you. Since you're then doing an identity dynamic_cast on a random memory location it doesn't/isn't able to do the type checking.

But seriously, 99% of the time don't try to test that something is a particular type. Design your classes such that the base classes define an interface and the child classes implement it, allowing use without lots of casting (which is a design smell).


dynamic_cast does not perform any run-time checking when you use it for upcasts or identity-casts (casts to the same type). For such casts dynamic_cast behaves exactly the same as an ordinary static_cast. No difference whatsoever.

The page you linked does not mention that, i.e. is not even a remotely complete specification of dynamic_cast, which makes it pretty useless.

C++ provides no means to determine whether a given pointer is actually a valid pointer to a given type. So, you are out of luck there. Implement your own checking method.

0

精彩评论

暂无评论...
验证码 换一张
取 消

关注公众号