0

Well, I am using a Map to store any kind of pointer (void*), and it is being used in a scope object. here is the scope class.

class Scope
{
  protected:
    Scope * parent;
    MyMap* map;
  public:
    virtual void setParent(Scope* p)=0;
    virtual Scope* getParent()=0;
    virtual void setOwner(void * owner)=0;
    virtual void * getOwner()=0;
    virtual Symbol * get(char* name)=0;
    virtual Symbol * get(char* name, Signature * sig)=0;
    MyMap* getMap()const;
 };

and there are 2 classes OrderedScope and DisorderedScope which implement the Scope class.

In my project I'm trying to store all data as void* and then I retrieve them and cast them to the apropriate type. when I cast an object on its type, I found that some data was lost. here is a photo of what I got. enter image description here

just to clarify Package class has Scope. And in that scope I am storing objects of type Functions. So when I want to add a function to it I should retrieve the package object first then I can use the add function to insert the new function.

I don't know if I showed the problem correctly, but I hope so. your help is appreciated.

David Gorsline
  • 4,933
  • 12
  • 31
  • 36
  • 5
    Why don't post the code instead of a photo of the code? Why have you tagged this as `yacc` - I don't see anything related to `yacc` in your question? – user93353 May 19 '13 at 07:40
  • 9
    Any solution using `void *` to store arbitrary items is likely to be a suspect design... – Mats Petersson May 19 '13 at 07:42
  • What's a `Map`? Is it like a `std::map`? Why are you storing `void*` instead of storing a pointer to a common base class and using polymorphism? – jamesdlin May 19 '13 at 07:43
  • 2
    Have you tried stepping into the calls to `getParent()` and `getOwner()` to see what they are returning, where they're getting the data from and if the data has been initialized or not. the `0xcdcdcdcd` is a strong indicator you are returning a pointer value from an uninitialized variable. – Captain Obvlious May 19 '13 at 07:52
  • 1
    Something not related to this question: avoid using C cast and use reinterpret_cast instead. – Jiwan May 19 '13 at 08:07
  • there's more like that :] avoid using raw pointers, avoid explcicit new/delete, use std::string not char* – stijn May 19 '13 at 08:12
  • thanks. @jamesdlin I used void* because there is no base class to every object I might put into it. – creative Creative May 19 '13 at 08:42
  • I wonder why debugger showed "Unable to read memory". even though I am sure that the package object is already in the map and it has a scope whose owner is the package itself. – creative Creative May 19 '13 at 08:47
  • 1
    "Unable to read memory" usually means that the pointer under scrutiny does not point to a valid memory. – n. m. could be an AI May 19 '13 at 09:05
  • How do you know which type to cast to? – n. m. could be an AI May 19 '13 at 09:07
  • Why not avoid using `void*` for this purpose all together, and use templates instead? – Hakan Serce May 19 '13 at 10:35
  • 2
    While I agree that C-style casts are evil, suggesting reinterpret_cast as replacement isn't much better. To get from void* back to the real type, the correct cast is static_cast, which is the reverse of the implicit conversion to void*. – Ulrich Eckhardt May 19 '13 at 12:29
  • 1
    Kind of extreme opinion about casts: When you need to cast something that is not a primitive type, rethink your design because something is not as good as it could be. – confusopoly May 20 '13 at 13:38
  • @UlrichEckhardt A `reinterpret_cast` from `void *` to another pointer type does the same thing as a `static_cast`. (This is what C++11 says; before that, it was not required by the standard, but was what compilers were doing already.) –  May 20 '13 at 14:58
  • A reinterpret_cast is still sending the wrong message and it still converts between more types than necessary for this case, so I still consider it the wrong choice. – Ulrich Eckhardt May 20 '13 at 16:01

1 Answers1

3

Void pointers are a risky mechanism, as they disable the compiler's type-checking for that pointer. Therefore if you make a mistake (such as casting a void pointer to the wrong type), you won't get a compile-time error, you will only notice the problem as run-time misbehavior, such as data corruption.

So, to answer the question: if you want to use void pointers and avoid data corruption, you have to make very very sure that when you cast a void-pointer to a (SomeType *), that the memory location the void-pointer points to does in fact contain a valid (SomeType *) and nothing else. In particular, you have to make sure that the (SomeType *) your void pointer is pointing to is still valid, and hasn't been deleted/freed between the time you created the void-pointer and the time you are attempting to cast and use it. You also need to be aware of the potential gotchas involved when combining void pointers with multiple inheritance, as a class with multiple superclasses can have a different "this" pointer depending on which superclass you are viewing it as, and casting to a void-pointer won't automatically store the correct "this" pointer for you (since the compiler can't know which "this" pointer you're going to be interested in later).

This is all doable, but your program's logic has to be 100% correct in all cases. There's no shortcut to this other than to code everything very carefully, study the code looking for errors, and test it thoroughly. Code-checkers like valgrind and Purify can also be helpful in finding errors whose symptoms aren't always obvious just by running the code.

That said, the commenters above are right -- there are generally better/safer alternatives to using void-pointers. These alternatives (such as templates and/or a common base class) are much less error-prone, because they let the compiler detect your errors for you at compile-time rather than forcing you to hunt them down one by one at run-time. Also, consider using smart pointers (e.g. shared_ptr) rather than raw pointers, as this will offload the handling object lifetimes to the compiler, which can handle that task more reliably and consistently than hand-rolled code can.

Community
  • 1
  • 1
Jeremy Friesner
  • 70,199
  • 15
  • 131
  • 234