Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Casting from unknown pointer to class; how to check validity?

I am using the LPARAM (essentially a long, unless we're on a 64-bit system in which case it is a long long) member of an LVITEM (called lParam) to store a pointer to the object that coincides with that entry in the ListView.

When I want to edit that item, I want to cast that LPARAM to a MyClass*, which works fine so long as the lParam is correctly set to be equal to a MyClass* in the first place, but I'd like to do some kind of checking to make sure it is, in fact, a MyClass that this number is pointing to.

Currently I have this:

LVITEM lv;
// lv is filled in by LVM_GETITEM
classPtr = static_cast<MyClass*>((void*)lv.lParam);
if ( !classPtr )
    return false;

Now, I'm not exactly clear: does static_cast return NULL if the argument is not a valid pointer? I assume not, since that's what dynamic_cast is for, but I'm not entirely certain. And, if I am correct in this assumption, is there any way to check that classPtr is valid before I attempt to access its members and cause a crash...

like image 794
KRyan Avatar asked Jul 03 '11 22:07

KRyan


2 Answers

static_cast<> performs no runtime checking of types, so what you have won't work, at least not the way you think it does. All static_cast<> does is pointer arithmetic based on information known at compile time.

dynamic_cast<> might work, but it requires that the class has at least one virtual function (for RTTI) and it'll surely fail badly if LPARAM isn't even a pointer to something that's a class type.

There is no general way to determine, given an arbitrary pointer or integral value like an LPARAM, that it actually points to an instance of MyClass.

What I would do in your specific situation is to wrap the list view in its own class and expose methods that works with MyClass only. Possibly like this:

class MyListView
{
public:
    MyListView() { /* create the list view HWND */ }
    ~MyListView() { /* destroy the list view HWND */ }

    void Add(MyClass* listItem) // Only way to add items to list view
    {
        // Add to list view
    }

    MyClass* Get() const
    {
        LVITEM lv;
        // lv is filled in by LVM_GETITEM

        // Assume that this will work, since the only way to add
        // list view items is through Add(), and Add() only accepts
        // an instance of MyClass. Therefore the list view will only
        // have pointers to instances of MyClass.
        return static_cast<MyClass*>((void*)lv.lParam);
    }

    // ...

private:
    // Set to private so users can't modify
    // the list view without our consent.
    HWND listView;
};

The advantage of this method is that now you have complete control over the list view control and its interface, and thus this will always work (bugs and evil/incompetent programmers notwithstanding). You can even make it a template so that it works with classes other than MyClass.

like image 152
In silico Avatar answered Nov 14 '22 21:11

In silico


You can't check it at all in pure standard C++. You can only do a probabilistic check using the Windows API. So, in practice you can only guarantee it, by making your code correct.

If you could check it you would still not know that it was the correct MyClass instance.

So, the only good way is to make your code guaranteed correct. That's easier when you limit access to things. C++ has many features to help you limit access, e.g. const; use them.

Cheers & hth.,

like image 39
Cheers and hth. - Alf Avatar answered Nov 14 '22 22:11

Cheers and hth. - Alf