#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;
};
//=====================================================
#include <string>
#include <iostream>
int main()
{
int myInt = 357;
float myFloat = 7.53f;
std::string myString = "Test";
UserdataStack stack;
stack.Push(&myInt);
stack.Push(&myFloat);
stack.Push(&myString);
//Asserts, because it's the wrong type.
//int *test = stack.Get<int>();
std::cout << "std::string:\t" << *(stack.Take<std::string>()) << " (should be " << myString << ")" << std::endl;
std::cout << "float:\t\t" << *(stack.Take<float>()) << " (should be " << myFloat << ")" << std::endl;
std::cout << "int:\t\t" << *(stack.Take<int>()) << " (should be " << myInt << ")" << std::endl;
//Asserts, because the stack is now empty:
//stack.Take<int>();
return 0;
}