Jump to content

  • Log In with Google      Sign In   
  • Create Account

We're offering banner ads on our site from just $5!

1. Details HERE. 2. GDNet+ Subscriptions HERE. 3. Ad upload HERE.






Code snippet for typesafe void* userdata

Posted by Servant of the Lord, in Code snippets 28 July 2013 · 633 views

C++
Just tossed this piece of code together - it wraps void* points with asserts() to ensure you are getting the same type that you originally set.
Normally I shy away from void* pointers as I feel like they are indicators of bad design, but sometimes you need to use them, and when you do, they might as well be type-safe.

This is kinda a lightweight alternative to boost::any, but not designed by a peer-reviewed team of professionals who also have seats on the C++ standards committee. Posted Image

Requires C++11's typeid(), and RTTI enabled.
#include <typeinfo>
#include <vector>
#include <cassert>
 
//Quick macro to replace my real Assert() function, just for this file.
#define Assert(condition, message) assert(condition && message)
 
//Holds any type of user-given pointer that can be cast to void, and does run-time checks on the type of the data when retrieving it.
//Ownership is *not* taken. It is the original owner's responsibillity to ensure the lifetime of the object when
//someone tries to retrieve the pointer later. The Userdata class itself never de-references the pointer.
class Userdata
{
public:
    Userdata() = default;
        
        template<typename UserType>
        Userdata(UserType *data)
        {
                this->Set(data);
        }
        
        //Sets a user-spe
        template<typename UserType>
        void Set(UserType *data)
        {
                //Save the pointer (cast to void*), and the type's RTTI information, so we can validate on retrieval.
                this->type_info = &typeid(UserType);
                this->userdata = data;
        }
        
        template<typename UserType>
        UserType *Get()
        {
                //Validate that we've actually set *anything*.
                Assert(this->type_info, "We never set any data, so we can't retrieve any.");
                
                //Validate that we are getting the same type that we initially set.
                Assert((*this->type_info) == typeid(UserType), "The types don't match - we can't retrieve the userdata safely.");
                
                return static_cast<UserType*>(this->userdata);
        }
        
        void Reset()
        {
                this->type_info = nullptr;
                this->userdata = nullptr;
        }
 
private:
        //A pointer to the global compiler-specific (but standardized) type_info for this type (the type that Userdata's void* points to).
        //C++11 guarantees that "The lifetime of the object referred to by [typeid()'s return value] extends to the end of the program." (N3337 5.2.8)
        const std::type_info *type_info = nullptr; 
        
        //A pointer to the user-defined data.
        void *userdata = nullptr;
};
 
//Manages a FILO stack of void pointers in a type-safe way.
//This class does not take any ownership of the data pointed to.
class UserdataStack
{
public:
        UserdataStack() = default;
        ~UserdataStack() = default;
        
        template<typename UserType>
        void Push(UserType *userdata)
        {
                this->stack.push_back(Userdata(userdata));
        }
        
        template<typename UserType>
        UserType *Get()
        {
                Assert(!this->stack.empty(), "The stack is empty - we can't retrieve any userdata.");
                return this->stack.back().Get<UserType>();
        }
        
        void Pop()
        {
                if(!this->stack.empty())
                {
                        this->stack.pop_back();
                }
        }
    
    //Pops the stack, and returns the pointer previously pointed to.
    template<typename UserType>
        UserType *Take()
        {
                UserType *data = this->Get<UserType>();
                this->Pop();
                
                return data;
        }
        
        void Clear()
        {
                this->stack.clear();
        }
        
private:
        std::vector<Userdata> stack;
};
Compilable code on Ideone.com

Note: By design, the Userdata/UserdataStack classes don't take ownership of the data. I guess that's another difference than with boost::any - though you could always just pass boost::any some pointers.




Maybe a sweet snippet?

Done. I can't see any way to get to 'Sweet snippets' from the main site without following that link you just posted. Is it not integrated into the rest of the site yet? The dates on the test submissions are from 8 months ago.

It was Mike's project and to be honest I'm not sure where it's at. He hasn't been asking me to push them at all so... just looked at the page and yea still seems to have a few bugs in it. I'll let him know about it.

Well, if it's not ready or low priority, I could just toss it into an article instead, though it'd just be a copy+paste of the code, not a real article.

Recent Entries

Recent Comments

Latest Visitors

PARTNERS