简体   繁体   中英

How to check if a void* is to a pointer on a valid instance of an object type?

I'm searching for the most common and robust way to check if a void* can be convert in a given C++ object type. You can see below some information about the context.

When I define a C API for a DLL, I often use void* to hide the C++ object I use behind (something like below)

typedef void* Instance_t;
int createInstance(Instance_t* pInst);
int processing(Instance_t inst, uint8_t* pBuf, size_t bufLength);

When I createInstance the code cast a pointer like that:

int createInstance(Instance_t* pInst)
{ 
   MyClass* ptr = new MyClass();
   *pInst = (void*)(ptr);
   //.... etc
   return 0;
 }

But the question is how can we later in all the other C function we define, check if the void* value we receive is a valid MyClass* . I think we can't as none of the C++ casting operator is really type safe is that case (even dynamic_cast ).

for now my best solution is to use a C cast (or a reinterpret_cast ) and if everything is ok with a call to a IsValid function define with MyClass.

Have you a better way to do that check ?

You can't do that, unless you (say) allocate all your MyClass instances from a memory pool, and check the address you get passed is a pointer into to that memory pool. Or maintain a list of valid instances.

However, if you need to pass round an opaque pointer just make the client use

struct MyClass;
typedef struct MyClass *Instance_t;

This will compile cleanly and give you a reasonable amount of peace of mind. As long as you're only using the pointer, the compiler is happy. It's when it dereferences it, the compiler needs to know what the pointer actually points to.

I don't think you can do this, and I don't think you should be doing this. A void * is just a pointer to some location in memory. Almost by definition, there's no way to know what it points to.

But why are you typecasting everything to void * 's, why not use protected and private methods on your class if you want to prevent users from fiddling with the internals of your class?

There is no way to check that an untyped pointer points to a valid object of any particular type. If you use void* , you are throwing away type checking. Instead, you could declare the type in the C header, and use a pointer to that (incomplete) type rather than void* .

struct Instance;
int createInstance(struct Instance** pInst);
int processing(struct Instance* inst, uint8_t* pBuf, size_t bufLength);

Then in C++, you can define and use the class.

// Probably better to use "struct" rather than "class" in case
// some compilers complain about mixing class keys.
struct Instance {
    // Whatever
};

int createInstance(Instance** pInst) {
    *pInst = new Instance;
    // and so on
}

There's no way to determine if a void* points to a valid C++ class. Unless you've got RTTI enabled then there's no metadata associated with a class, and even then there are plenty of cases in C++ where a void* isn't pointing to a class. For example:

int x=10;
void *ptr = &x;

Here ptr is pointing to a raw value. There's no RTTI associated with the integer so how could you query it to determine anything>

When I need to export some objects from my CPP lib to C code, I do:

typedef void * OBJ1;
typedef void * OBJ2;

OBJ1 createObj1();
OBJ2 createObj2();

void doObj1(OBJ1 obj);

So in do function I exactly knew what object to expect

The short answer: it can be difficult.

The issues are numerous, but basically boil down to the very low-level nature of operations accessible in C and C++ (note: accessible, but not necessarily legal). The very role of void* is that any pointer can be coerced to it, but should you be using another type anyway abuse of reinterpret_cast could still lead to troubles.


A simple solution is to use tagging. Essentially, put a type id in the pointer so that you can always know the original type of the object. It's onerous (as each and every type need be modified), but otherwise easy to deploy.

typedef enum {
    SP_ATag,
    SP_BTag,
    SP_CTag,
    ...
} SP_Tag_t;

// External Tag
typedef struct {
    SP_Tag_t tag;
    void* p;
} SP_Any_t;

// Internal Tag
struct A {
    SP_Tag_t tag;
    ...;
};

...

typedef union {
    A* a;
    B* b;
    C* c;
} SP_Any_t;

Then, instead of using void* , you use SP_Any_t .

Advantages :

  • lightweight
  • external solution does not require modifying existing classes
  • internal solution should be binary compatible with existing C code

Disadvantages :

  • single place to declare all types
  • corrupting the tag is easy (whether accidental or intentional)

A more involved solution, which can be a good debug help, is to introduce a per type registry . The downside is that you need to instrument existing types for it to work, still it's easy enough, and that it involves much more runtime overhead . But hey: it works !

template <typename> class Registrable;

//
// class Registry
//
class Registry {
public:
template <typename> friend class Registrable;

template <typename T>
static T* Cast(void*);

private:
struct TagType {};

using Key = std::pair<TagType const*, void*>;
using Store = std::set<Key>;

template <typename T>
static void Register(Registrable<T>* t);

template <typename T>
static void Unregister(Registrable<T>* t);

static Store& Get();
}; // class Registry

template <typename T>
T* Registry::Cast(void* const pointer) {
TagType const* const tag = &Registrable<T>::Tag;

if (Get().count(std::make_pair(tag, pointer)) == 0) { return nullptr; }

return static_cast<T*>(reinterpret_cast<Registrable<T>*>(pointer));
}

template <typename T>
void Registry::Register(Registrable<T>* t) {
TagType const* const tag = &T::Tag;
void* const pointer = reinterpret_cast<void*>(t);

Get().insert(std::make_pair(tag, pointer));
}

template <typename T>
void Registry::Unregister(Registrable<T>* t) {
TagType const* const tag = &T::Tag;
void* const pointer = reinterpret_cast<void*>(t);

Get().erase(std::make_pair(tag, pointer));
}

Registry::Store& Registry::Get() { static Store S; return S; }

//
// class Registrable
//
template <typename T>
class Registrable {
public:
static Registry::TagType const Tag;

Registrable();
~Registrable();

Registrable(Registrable&&) = default;
Registrable& operator=(Registrable&&) = default;

Registrable(Registrable const&) = default;
Registrable& operator=(Registrable const&) = default;
}; // class Registrable

template <typename T> Registry::TagType const Registrable<T>::Tag;

template <typename T>
Registrable<T>::Registrable() { Registry::Register(this); }

template <typename T>
Registrable<T>::~Registrable() { try { Registry::Register(this); } catch(...) {} }

do not use pointers but handles

  • if the memory is on the dll side do not pass pointers but handles to your objects only
  • if the memory for your objects is allocated by the application and passed to the dll keep a table of those pointers and treat them as handles also

this works for a single dll. of course it does not work if pointers from 1.dll are passed to 2.dll via application. in this case you are on thinnest possible ice anyway.

I have a solution with some limitations that uses RTTI...

if your instances all derive from a virtual base class, then you can reinterpret cast to that base class safely, and then dynamic cast to your other class...

class Object
{
   virtual ~Object() {}
};

class A : public Object
{
  static bool IsOfThisClass(void *data)
  { 
     return dynamic_cast<A*>((Object*)data) != 0;
  }
}

calling A::IsOfThisClass(someData) will return true if someData is of class A.

it's not the kind of hack you want to expose to a user, since it only works if void* points to a class derived from Object, but it can be a useful building block in controlled situations.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM