fork download
  1. #include <typeinfo>
  2. #include <vector>
  3. #include <cassert>
  4.  
  5. //Quick macro to replace my real Assert() function, just for this file.
  6. #define Assert(condition, message) assert(condition && message)
  7.  
  8. //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.
  9. //Ownership is *not* taken. It is the original owner's responsibillity to ensure the lifetime of the object when
  10. //someone tries to retrieve the pointer later. The Userdata class itself never de-references the pointer.
  11. class Userdata
  12. {
  13. public:
  14. Userdata() = default;
  15.  
  16. template<typename UserType>
  17. Userdata(UserType *data)
  18. {
  19. this->Set(data);
  20. }
  21.  
  22. //Sets a user-spe
  23. template<typename UserType>
  24. void Set(UserType *data)
  25. {
  26. //Save the pointer (cast to void*), and the type's RTTI information, so we can validate on retrieval.
  27. this->type_info = &typeid(UserType);
  28. this->userdata = data;
  29. }
  30.  
  31. template<typename UserType>
  32. UserType *Get()
  33. {
  34. //Validate that we've actually set *anything*.
  35. Assert(this->type_info, "We never set any data, so we can't retrieve any.");
  36.  
  37. //Validate that we are getting the same type that we initially set.
  38. Assert((*this->type_info) == typeid(UserType), "The types don't match - we can't retrieve the userdata safely.");
  39.  
  40. return static_cast<UserType*>(this->userdata);
  41. }
  42.  
  43. void Reset()
  44. {
  45. this->type_info = nullptr;
  46. this->userdata = nullptr;
  47. }
  48.  
  49. private:
  50. //A pointer to the global compiler-specific (but standardized) type_info for this type (the type that Userdata's void* points to).
  51. //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)
  52. const std::type_info *type_info = nullptr;
  53.  
  54. //A pointer to the user-defined data.
  55. void *userdata = nullptr;
  56. };
  57.  
  58. //Manages a FILO stack of void pointers in a type-safe way.
  59. //This class does not take any ownership of the data pointed to.
  60. class UserdataStack
  61. {
  62. public:
  63. UserdataStack() = default;
  64. ~UserdataStack() = default;
  65.  
  66. template<typename UserType>
  67. void Push(UserType *userdata)
  68. {
  69. this->stack.push_back(Userdata(userdata));
  70. }
  71.  
  72. template<typename UserType>
  73. UserType *Get()
  74. {
  75. Assert(!this->stack.empty(), "The stack is empty - we can't retrieve any userdata.");
  76. return this->stack.back().Get<UserType>();
  77. }
  78.  
  79. void Pop()
  80. {
  81. if(!this->stack.empty())
  82. {
  83. this->stack.pop_back();
  84. }
  85. }
  86.  
  87. //Pops the stack, and returns the pointer previously pointed to.
  88. template<typename UserType>
  89. UserType *Take()
  90. {
  91. UserType *data = this->Get<UserType>();
  92. this->Pop();
  93.  
  94. return data;
  95. }
  96.  
  97. void Clear()
  98. {
  99. this->stack.clear();
  100. }
  101.  
  102. private:
  103. std::vector<Userdata> stack;
  104. };
  105.  
  106. //=====================================================
  107.  
  108. #include <string>
  109. #include <iostream>
  110.  
  111. int main()
  112. {
  113. int myInt = 357;
  114. float myFloat = 7.53f;
  115. std::string myString = "Test";
  116.  
  117. UserdataStack stack;
  118. stack.Push(&myInt);
  119. stack.Push(&myFloat);
  120. stack.Push(&myString);
  121.  
  122. //Asserts, because it's the wrong type.
  123. //int *test = stack.Get<int>();
  124.  
  125. std::cout << "std::string:\t" << *(stack.Take<std::string>()) << " (should be " << myString << ")" << std::endl;
  126. std::cout << "float:\t\t" << *(stack.Take<float>()) << " (should be " << myFloat << ")" << std::endl;
  127. std::cout << "int:\t\t" << *(stack.Take<int>()) << " (should be " << myInt << ")" << std::endl;
  128.  
  129. //Asserts, because the stack is now empty:
  130. //stack.Take<int>();
  131.  
  132. return 0;
  133. }
  134.  
Success #stdin #stdout 0s 3032KB
stdin
Standard input is empty
stdout
std::string:	Test (should be Test)
float:		7.53 (should be 7.53)
int:		357 (should be 357)