summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/CMakeLists.txt1
-rw-r--r--src/flag_set.hpp144
-rw-r--r--src/include/tome/pp/global_constexpr.hpp21
-rw-r--r--tests/flag_set.cc85
4 files changed, 251 insertions, 0 deletions
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index 6d942dfe..a68e056b 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -118,6 +118,7 @@ SET(SRCS_TESTS
../tests/get_level_device.cc
../tests/harness.cc
../tests/lua_get_level.cc
+ ../tests/flag_set.cc
)
ADD_LIBRARY(game
diff --git a/src/flag_set.hpp b/src/flag_set.hpp
new file mode 100644
index 00000000..cbc4e7aa
--- /dev/null
+++ b/src/flag_set.hpp
@@ -0,0 +1,144 @@
+#pragma once
+
+#include <array>
+#include <cassert>
+#include <cstdint>
+
+#include "tome/pp/global_constexpr.hpp"
+
+/**
+ * Set of binary flags.
+ */
+template<std::size_t Tiers> struct flag_set
+{
+private:
+ static constexpr std::size_t tiers = Tiers;
+ std::uint32_t m_data[tiers];
+
+public:
+
+ constexpr flag_set()
+ : m_data { 0 }
+ {
+ // It is *extremely* important that there are absolutely
+ // NO dependencies on any global objects in here; lest we
+ // fall into SIOF territory; see DECLARE_FLAG_ZERO_IMPL
+ }
+
+ // This method is a workaround for a a segmentation fault
+ // when compiling with GCC 5.3.0 (Arch Linux). We should be
+ // able to use make() directly in DECLARE_FLAG_MAKE_INIT,
+ // but GCC segfaults.
+ template<std::uint32_t tier, std::size_t index> static constexpr flag_set make_static()
+ {
+ static_assert(tier < tiers, "tier >= tiers");
+ static_assert(index < 32, "index >= 32");
+ flag_set f;
+ f.m_data[tier] = (1UL << index);
+ return f;
+ }
+
+ static constexpr flag_set make(std::uint32_t tier, std::size_t index)
+ {
+ assert(tier < tiers);
+ assert(index < 32);
+ flag_set f;
+ f.m_data[tier] = (1UL << index);
+ return f;
+ }
+
+ constexpr std::size_t size() const
+ {
+ return tiers;
+ }
+
+ constexpr bool empty() const
+ {
+ for (std::size_t i = 0; i < tiers; i++)
+ {
+ if (m_data[i])
+ {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ uint32_t &operator[](std::size_t i)
+ {
+ assert(i < tiers);
+ return m_data[i];
+ }
+
+ constexpr uint32_t const &operator [](std::size_t i) const
+ {
+ assert(i < tiers);
+ return m_data[i];
+ }
+
+ constexpr operator bool() const
+ {
+ return !empty();
+ }
+
+ flag_set &operator |= (flag_set const &other)
+ {
+ for (std::size_t i = 0; i < tiers; i++)
+ {
+ m_data[i] |= other.m_data[i];
+ }
+ return *this;
+ }
+
+ constexpr flag_set operator | (flag_set const &other) const
+ {
+ flag_set f;
+ for (std::size_t i = 0; i < tiers; i++)
+ {
+ f.m_data[i] = m_data[i] | other.m_data[i];
+ }
+ return f;
+ }
+
+ flag_set &operator &= (flag_set const &other)
+ {
+ for (std::size_t i = 0; i < tiers; i++)
+ {
+ m_data[i] &= other.m_data[i];
+ }
+ return *this;
+ }
+
+ constexpr flag_set operator & (flag_set const &other) const
+ {
+ flag_set f;
+ for (std::size_t i = 0; i < tiers; i++)
+ {
+ f.m_data[i] = m_data[i] & other.m_data[i];
+ }
+ return f;
+ }
+
+};
+
+// Implementation details, because preprocessor.
+#define DECLARE_FLAG_MAKE_INIT(type, tier, index) \
+ type::make_static<tier-1,index>()
+
+/**
+ * Macro for declaring a "flag" constant.
+ */
+#define DECLARE_FLAG(type, name, tier, index) \
+ PP_GLOBAL_CONSTEXPR_CONST(type, name, DECLARE_FLAG_MAKE_INIT(type, tier, index))
+
+/**
+ * Macro for declaring a zero'ed "flag" variable.
+ */
+#define DECLARE_FLAG_ZERO_INTF(type, name) \
+ extern type name
+
+/**
+ * Macro for declaring the implementation of a zero'ed "flag" variable.
+ */
+#define DECLARE_FLAG_ZERO_IMPL(type, name) \
+ type name { };
diff --git a/src/include/tome/pp/global_constexpr.hpp b/src/include/tome/pp/global_constexpr.hpp
new file mode 100644
index 00000000..83168c59
--- /dev/null
+++ b/src/include/tome/pp/global_constexpr.hpp
@@ -0,0 +1,21 @@
+#pragma once
+
+/**
+ * Macro for declaring a global constexpr variable without
+ * violating the ODR and without running into the SIOF.
+ *
+ * Shamelessly cribbed from http://stackoverflow.com/a/20374473
+ */
+#define PP_GLOBAL_CONSTEXPR_CONST(type, var, value) \
+namespace global_constexpr { namespace var { \
+template<class = void> \
+struct wrapper \
+{ \
+ static constexpr type var = value; \
+}; \
+template<class T> \
+constexpr type wrapper<T>::var; \
+} } \
+namespace { \
+auto const& var = global_constexpr::var::wrapper<>::var; \
+}
diff --git a/tests/flag_set.cc b/tests/flag_set.cc
new file mode 100644
index 00000000..125d8a81
--- /dev/null
+++ b/tests/flag_set.cc
@@ -0,0 +1,85 @@
+#include "flag_set.hpp"
+#include <bandit/bandit.h>
+using namespace bandit;
+
+//
+// Tests
+//
+
+go_bandit([]() {
+
+ describe("flag_set", []() {
+
+ // Convenience typedef
+ typedef flag_set<2> fs_t;
+
+ it("make function should handle tier=0, index=31 properly", [&] {
+ // Setup
+ fs_t fs = fs_t::make(0, 31);
+ // Exercise
+ auto result = fs;
+ // Verify
+ AssertThat(result[0], Equals(2147483648UL));
+ AssertThat(result[1], Equals(0UL));
+ });
+
+ it("make function should handle tier=1, index=31 properly", [&] {
+ // Setup
+ fs_t fs = fs_t::make(1, 31);
+ // Exercise
+ auto result = fs;
+ // Verify
+ AssertThat(result[0], Equals(0UL));
+ AssertThat(result[1], Equals(2147483648UL));
+ });
+
+ it("make function should respect the tier and index", [&] {
+ // Exercise
+ fs_t fs = fs_t::make(1, 7);
+ // Verify
+ AssertThat(fs[0], Equals(0UL));
+ AssertThat(fs[1], Equals(128UL));
+ });
+
+ it("bool conversion should return false for zero flags", [&] {
+ // Setup
+ fs_t fs = fs_t();
+ // Exercise
+ bool result = fs;
+ // Verify
+ AssertThat(result, Equals(false));
+ });
+
+ it("bool conversion should return true for non-zero flags", [&] {
+ // Setup
+ fs_t fs = fs_t::make(1, 3);
+ // Exercise
+ bool result = fs;
+ // Verify
+ AssertThat(result, Equals(true));
+ });
+
+ it("| operator should respect the tier and index", [&] {
+ // Setup
+ fs_t fs1 = fs_t::make(0, 31);
+ fs_t fs2 = fs_t::make(1, 3);
+ // Exercise
+ fs_t fs = fs1 | fs2;
+ // Verify
+ AssertThat(fs[0], Equals(2147483648UL));
+ AssertThat(fs[1], Equals(8UL));
+ });
+
+ it("& operator should respect the tier and index", [&] {
+ // Setup
+ fs_t fs = fs_t::make(0, 31) | fs_t::make(1, 3);
+ // Exercise
+ fs_t result = fs & fs;
+ // Verify
+ AssertThat(result[0], Equals(2147483648UL));
+ AssertThat(result[1], Equals(8UL));
+ });
+
+ });
+
+});