blob: 89a8dde90ee0976abad10f0125721fed097f760a (
plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
|
#pragma once
#ifdef __cplusplus
#include <any>
#include <cassert>
#include <limits>
#include <stdint.h>
#include <unordered_map>
/*
* This class can attach additional data to pointers. It can only attach a single instance of each type of data.
* Use the ObjectExtension::Register class to register a type to be used as an object extension.
* An example usage is:
*
* struct MyData {
* s32 data = -1;
* };
* static ObjectExtension::Register<MyData> MyDataRegister;
*
* Then you can get with
* ObjectExtension::GetInstance().Get<MyData>(ptr);
* and set with
* ObjectExtension::GetInstance().Set<MyData>(ptr, MyData{});
* (or with the returned pointer from Get()).
*/
class ObjectExtension {
public:
using Id = uint32_t;
static constexpr Id InvalidId = std::numeric_limits<Id>::max();
// Registers type T to be used as an object extension
template <typename T> class Register {
public:
Register() {
Id = ObjectExtension::GetInstance().RegisterId();
}
static ObjectExtension::Id Id;
};
// Gets the singleton ObjectExtension instance
static ObjectExtension& GetInstance();
// Gets the data of type T associated with an object, or nullptr if no such data has been attached
template <typename T> T* Get(const void* object) {
assert(ObjectExtension::Register<T>::Id != InvalidId);
if (object == nullptr) {
return nullptr;
}
auto it = Data.find(std::make_pair(object, ObjectExtension::Register<T>::Id));
if (it == Data.end()) {
return nullptr;
}
return std::any_cast<T>(&(it->second));
}
// Sets the data of type T for an object. Data will be copied.
template <typename T> void Set(const void* object, const T&& data) {
assert(ObjectExtension::Register<T>::Id != InvalidId);
if (object != nullptr) {
Data[std::make_pair(object, ObjectExtension::Register<T>::Id)] = data;
}
}
// Returns true if an object has data of type T associated with it
template <typename T> bool Has(const void* object) {
assert(ObjectExtension::Register<T>::Id != InvalidId);
if (object == nullptr) {
return false;
}
return Data.contains(std::make_pair(object, ObjectExtension::Register<T>::Id));
}
// Removes data of type T from an object
template <typename T> void Remove(const void* object) {
assert(ObjectExtension::Register<T>::Id != InvalidId);
Data.erase(std::make_pair(object, ObjectExtension::Register<T>::Id));
}
// Removes all data from an object
void Free(const void* object);
private:
ObjectExtension() = default;
// Returns the next free object extension Id
Id RegisterId();
ObjectExtension::Id NextId = 0;
struct KeyHash {
std::size_t operator()(const std::pair<const void*, ObjectExtension::Id>& key) const {
return std::hash<const void*>{}(key.first) ^ (std::hash<ObjectExtension::Id>{}(key.second) << 1);
}
};
// Collection of all object extension data.
std::unordered_map<std::pair<const void*, ObjectExtension::Id>, std::any, KeyHash> Data;
};
// Static template globals
template <typename T> ObjectExtension::Id ObjectExtension::Register<T>::Id = ObjectExtension::InvalidId;
extern "C" {
#endif // __cplusplus
void ObjectExtension_Free(const void* object);
#ifdef __cplusplus
}
#endif
|