summaryrefslogtreecommitdiff
path: root/src/generic
diff options
context:
space:
mode:
authorBen Longbons <b.r.longbons@gmail.com>2014-06-26 10:27:47 -0700
committerBen Longbons <b.r.longbons@gmail.com>2014-06-26 15:04:16 -0700
commit34807ca9fccc7425573256645024722571ef4442 (patch)
tree5e716b7fa2d786b15da403072bb6f41f3dfbcc65 /src/generic
parentb353ae37eb6d374aec4127f1849a5dce81f812b5 (diff)
downloadtmwa-34807ca9fccc7425573256645024722571ef4442.tar.gz
tmwa-34807ca9fccc7425573256645024722571ef4442.tar.bz2
tmwa-34807ca9fccc7425573256645024722571ef4442.tar.xz
tmwa-34807ca9fccc7425573256645024722571ef4442.zip
specialize inventory/storage indices
Diffstat (limited to 'src/generic')
-rw-r--r--src/generic/array.hpp94
-rw-r--r--src/generic/array_test.cpp159
-rw-r--r--src/generic/enum.hpp60
-rw-r--r--src/generic/oops.cpp45
-rw-r--r--src/generic/oops.hpp43
-rw-r--r--src/generic/oops_test.cpp50
6 files changed, 386 insertions, 65 deletions
diff --git a/src/generic/array.hpp b/src/generic/array.hpp
index e6fefae..5e4dd67 100644
--- a/src/generic/array.hpp
+++ b/src/generic/array.hpp
@@ -24,18 +24,94 @@
# include <cassert>
# include <cstddef>
-template<class T, size_t n>
-struct Array
+# include "oops.hpp"
+
+template<class I, I be, I en>
+struct ExclusiveIndexing
+{
+ using index_type = I;
+ constexpr static size_t index_to_offset(index_type idx)
+ { return static_cast<size_t>(idx) - static_cast<size_t>(be); }
+ constexpr static index_type offset_to_index(size_t off)
+ { return static_cast<I>(off + static_cast<size_t>(be)); }
+ constexpr static size_t alloc_size = index_to_offset(en) - index_to_offset(be);
+};
+
+template<size_t n>
+using SimpleIndexing = ExclusiveIndexing<size_t, 0, n>;
+
+template<class I, I lo, I hi>
+struct InclusiveIndexing
+{
+ using index_type = I;
+ constexpr static size_t index_to_offset(index_type idx)
+ { return static_cast<size_t>(idx) - static_cast<size_t>(lo); }
+ constexpr static index_type offset_to_index(size_t off)
+ { return static_cast<I>(off + static_cast<size_t>(lo)); }
+ constexpr static size_t alloc_size = index_to_offset(hi) - index_to_offset(lo) + 1;
+};
+
+template<class E, E n=E::COUNT>
+struct EnumIndexing : ExclusiveIndexing<E, static_cast<E>(0), n>
{
- T data[n];
+};
+
+template<class I, size_t limit>
+struct InventoryIndexing
+{
+ using index_type = I;
+ constexpr static size_t index_to_offset(index_type idx)
+ { return idx.get0(); }
+ constexpr static index_type offset_to_index(size_t off)
+ { return I::from(off); }
+ constexpr static size_t alloc_size = limit;
+};
+
+template<class T, class I>
+struct GenericArray
+{
+ T data[I::alloc_size];
public:
- T& operator [](size_t i) { assert (i < n); return data[i]; }
- const T& operator [](size_t i) const { assert (i < n); return data[i]; }
+ T *begin()
+ { return data + 0; }
+ T *end()
+ { return data + I::alloc_size; }
+ const T *begin() const
+ { return data + 0; }
+ const T *end() const
+ { return data + I::alloc_size; }
+ size_t size() const
+ { return I::alloc_size; }
+
+ T& operator [](typename I::index_type i_)
+ {
+ size_t i = I::index_to_offset(i_);
+ ALLEGE ("index in bounds", i < size());
+ return data[i];
+ }
+ const T& operator [](typename I::index_type i_) const
+ {
+ size_t i = I::index_to_offset(i_);
+ ALLEGE ("index in bounds", i < size());
+ return data[i];
+ }
- T *begin() { return data + 0; }
- T *end() { return data + n; }
- const T *begin() const { return data + 0; }
- const T *end() const { return data + n; }
+ friend bool operator == (GenericArray& lhs, GenericArray& rhs)
+ {
+ for (size_t i = 0; i < I::alloc_size; ++i)
+ {
+ if (lhs.data[i] != rhs.data[i])
+ return false;
+ }
+ return true;
+ }
+ friend bool operator != (GenericArray& lhs, GenericArray& rhs)
+ {
+ return !(lhs == rhs);
+ }
};
+template<class T, size_t n>
+using Array = GenericArray<T, SimpleIndexing<n>>;
+
#endif // TMWA_GENERIC_ARRAY_HPP
diff --git a/src/generic/array_test.cpp b/src/generic/array_test.cpp
new file mode 100644
index 0000000..7b5ffca
--- /dev/null
+++ b/src/generic/array_test.cpp
@@ -0,0 +1,159 @@
+#include "array.hpp"
+// array_test.cpp - Testsuite for a simple bounds-checked array.
+//
+// Copyright © 2014 Ben Longbons <b.r.longbons@gmail.com>
+//
+// This file is part of The Mana World (Athena server)
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+#include <gtest/gtest.h>
+
+#include "../poison.hpp"
+
+
+TEST(Array, simple)
+{
+ GenericArray<int, SimpleIndexing<3>> a;
+ try
+ {
+ a[0];
+ a[1];
+ a[2];
+ SUCCEED();
+ }
+ catch (const AssertionError&)
+ {
+ FAIL();
+ }
+ try
+ {
+ a[3];
+ FAIL();
+ }
+ catch (const AssertionError&)
+ {
+ SUCCEED();
+ }
+}
+
+TEST(Array, inclusive1)
+{
+ GenericArray<int, InclusiveIndexing<int, 1, 3>> a;
+ try
+ {
+ a[0];
+ FAIL();
+ }
+ catch (const AssertionError&)
+ {
+ SUCCEED();
+ }
+ try
+ {
+ a[1];
+ a[2];
+ a[3];
+ SUCCEED();
+ }
+ catch (const AssertionError&)
+ {
+ FAIL();
+ }
+ try
+ {
+ a[4];
+ FAIL();
+ }
+ catch (const AssertionError&)
+ {
+ SUCCEED();
+ }
+}
+
+TEST(Array, negative)
+{
+ GenericArray<int, InclusiveIndexing<int, -1, 1>> a;
+ try
+ {
+ a[-2];
+ FAIL();
+ }
+ catch (const AssertionError&)
+ {
+ SUCCEED();
+ }
+ try
+ {
+ a[-1];
+ a[0];
+ a[1];
+ SUCCEED();
+ }
+ catch (const AssertionError&)
+ {
+ FAIL();
+ }
+ try
+ {
+ a[2];
+ FAIL();
+ }
+ catch (const AssertionError&)
+ {
+ SUCCEED();
+ }
+}
+
+TEST(Array, enum)
+{
+ enum class Blah
+ {
+ FOO,
+ BAR,
+ BAZ,
+ COUNT,
+ };
+
+ GenericArray<int, EnumIndexing<Blah>> a;
+ try
+ {
+ a[static_cast<Blah>(-1)];
+ FAIL();
+ }
+ catch (const AssertionError&)
+ {
+ SUCCEED();
+ }
+ try
+ {
+ a[Blah::FOO];
+ a[Blah::BAR];
+ a[Blah::BAZ];
+ SUCCEED();
+ }
+ catch (const AssertionError&)
+ {
+ FAIL();
+ }
+ try
+ {
+ a[Blah::COUNT];
+ FAIL();
+ }
+ catch (const AssertionError&)
+ {
+ SUCCEED();
+ }
+}
diff --git a/src/generic/enum.hpp b/src/generic/enum.hpp
index 5f075bc..1e83c24 100644
--- a/src/generic/enum.hpp
+++ b/src/generic/enum.hpp
@@ -29,62 +29,10 @@
# include "../compat/iter.hpp"
-template<class T, class E, E max>
-struct earray
-{
- constexpr static
- size_t size()
- {
- return static_cast<size_t>(max);
- }
-
- // no ctor/dtor and one public member variable for easy initialization
- T _data[size()];
+# include "array.hpp"
- T& operator[](E v)
- {
- auto i = static_cast<size_t>(v);
- assert (i < size());
- return _data[i];
- }
-
- const T& operator[](E v) const
- {
- auto i = static_cast<size_t>(v);
- assert (i < size());
- return _data[i];
- }
-
- T *begin()
- {
- return _data;
- }
-
- T *end()
- {
- return _data + size();
- }
-
- const T *begin() const
- {
- return _data;
- }
-
- const T *end() const
- {
- return _data + size();
- }
-
- friend bool operator == (const earray& l, const earray& r)
- {
- return std::equal(l.begin(), l.end(), r.begin());
- }
-
- friend bool operator != (const earray& l, const earray& r)
- {
- return !(l == r);
- }
-};
+template<class T, class E, E max>
+using earray = GenericArray<T, EnumIndexing<E, max>>;
template<class T, class E, E max>
class eptr
@@ -102,7 +50,7 @@ public:
{}
eptr(earray<T, E, max>& arr)
- : _data(arr._data)
+ : _data(arr.data)
{}
T& operator [](E v) const
diff --git a/src/generic/oops.cpp b/src/generic/oops.cpp
new file mode 100644
index 0000000..95fdcad
--- /dev/null
+++ b/src/generic/oops.cpp
@@ -0,0 +1,45 @@
+#include "oops.hpp"
+// oops.cpp - Stuff that shouldn't happen.
+//
+// Copyright © 2014 Ben Longbons <b.r.longbons@gmail.com>
+//
+// This file is part of The Mana World (Athena server)
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+#include <cstdlib>
+#include <cstring>
+#include <cstdio>
+
+//#include "../poison.hpp"
+
+
+static
+std::string do_asprintf(const char *desc, const char *expr,
+ const char *file, size_t line, const char *function)
+{
+ char *what = nullptr;
+ int len = asprintf(&what, "%s:%zu: error: in '%s', incorrectly alleged that '%s' (%s)",
+ file, line, function, desc, expr);
+ if (len == -1)
+ abort();
+ std::string out = what;
+ free(what);
+ return out;
+}
+
+AssertionError::AssertionError(const char *desc, const char *expr,
+ const char *file, size_t line, const char *function)
+: std::runtime_error(do_asprintf(desc, expr, file, line, function))
+{}
diff --git a/src/generic/oops.hpp b/src/generic/oops.hpp
new file mode 100644
index 0000000..231a4e4
--- /dev/null
+++ b/src/generic/oops.hpp
@@ -0,0 +1,43 @@
+#ifndef TMWA_GENERIC_OOPS_HPP
+#define TMWA_GENERIC_OOPS_HPP
+// oops.hpp - Stuff that shouldn't happen.
+//
+// Copyright © 2014 Ben Longbons <b.r.longbons@gmail.com>
+//
+// This file is part of The Mana World (Athena server)
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+# include "fwd.hpp"
+
+# include <cstddef>
+
+# include <stdexcept>
+
+# include "oops.hpp"
+
+
+class AssertionError : public std::runtime_error
+{
+ const char *_what;
+public:
+ AssertionError(const char *desc, const char *expr,
+ const char *file, size_t line, const char *function);
+};
+
+# define ALLEGE(desc, expr) \
+ if (expr) {} \
+ else throw AssertionError(desc, #expr, __FILE__, __LINE__, __PRETTY_FUNCTION__)
+
+#endif // TMWA_GENERIC_OOPS_HPP
diff --git a/src/generic/oops_test.cpp b/src/generic/oops_test.cpp
new file mode 100644
index 0000000..d16db04
--- /dev/null
+++ b/src/generic/oops_test.cpp
@@ -0,0 +1,50 @@
+#include "oops.hpp"
+// oops_test.cpp - Testsuite for stuff that shouldn't happen.
+//
+// Copyright © 2014 Ben Longbons <b.r.longbons@gmail.com>
+//
+// This file is part of The Mana World (Athena server)
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+#include <gtest/gtest.h>
+
+#include "../poison.hpp"
+
+TEST(oops, okay)
+{
+ try
+ {
+ ALLEGE ("the sky is gray", true);
+ SUCCEED();
+ }
+ catch (const AssertionError& e)
+ {
+ FAIL();
+ }
+}
+
+TEST(oops, uhoh)
+{
+ try
+ {
+ ALLEGE ("the sky is falling", 1 == 0);
+ FAIL();
+ }
+ catch (const AssertionError& e)
+ {
+ ASSERT_STREQ(strstr(e.what(), "src/generic/"),
+ "src/generic/oops_test.cpp:42: error: in 'virtual void oops_uhoh_Test::TestBody()', incorrectly alleged that 'the sky is falling' (1 == 0)");
+ }
+}