]> git.tdb.fi Git - libs/game.git/blobdiff - source/game/accessguard.h
Enforce no creation or destruction of objects during tick
[libs/game.git] / source / game / accessguard.h
diff --git a/source/game/accessguard.h b/source/game/accessguard.h
new file mode 100644 (file)
index 0000000..c64efe7
--- /dev/null
@@ -0,0 +1,99 @@
+#ifndef MSP_GAME_ACCESSGUARD_H_
+#define MSP_GAME_ACCESSGUARD_H_
+
+#include <cstdint>
+#include <stdexcept>
+#include <string>
+#include <vector>
+#include <msp/core/noncopyable.h>
+
+namespace Msp::Game {
+
+class invalid_access: public std::logic_error
+{
+public:
+       invalid_access(const std::string &w): logic_error(w) { }
+};
+
+
+class AccessGuard
+{
+public:
+       struct Create { static std::string describe() { return "create"; } };
+       struct Destroy { static std::string describe() { return "destroy"; } };
+
+       template<typename T = void>
+       struct BlockForScope: NonCopyable
+       {
+               BlockForScope() { get_instance().block<T>(); }
+               ~BlockForScope() { get_instance().unblock<T>(); }
+       };
+
+private:
+       const uint8_t UNBLOCKED = 0;
+       const uint8_t BLOCKED = 1;
+
+       std::uint8_t default_flag = UNBLOCKED;
+       std::vector<std::uint8_t> flags;
+
+       static thread_local AccessGuard *instance;
+
+public:
+       AccessGuard();
+       AccessGuard(AccessGuard &&);
+       AccessGuard &operator=(AccessGuard &&);
+       ~AccessGuard();
+
+       static AccessGuard &get_instance();
+
+       void block_all();
+       void unblock_all();
+
+private:
+       static unsigned get_next_index();
+
+       template<typename T>
+       static unsigned get_index();
+
+       template<typename T>
+       std::uint8_t &get();
+
+public:
+       template<typename T>
+       void block() { get<T>() = BLOCKED; }
+
+       template<typename T>
+       void unblock() { get<T>() = UNBLOCKED; }
+
+       template<typename T>
+       void check() { if(get<T>()!=UNBLOCKED) throw invalid_access(T::describe()); }
+};
+
+
+template<typename T>
+inline unsigned AccessGuard::get_index()
+{
+       static unsigned index = get_next_index();
+       return index;
+}
+
+template<typename T>
+inline std::uint8_t &AccessGuard::get()
+{
+       unsigned index = get_index<T>();
+       if(flags.size()<=index)
+               flags.resize(index+1, default_flag);
+       return flags[index];
+}
+
+template<>
+inline void AccessGuard::block<void>()
+{ block_all(); }
+
+template<>
+inline void AccessGuard::unblock<void>()
+{ unblock_all(); }
+
+} // namespace Msp::Game
+
+#endif