Add easy to use macro to generate enum helper functions for parsing, stringifying and iteration.
continuous-integration/drone/push Build is failing
Details
continuous-integration/drone/push Build is failing
Details
This commit is contained in:
parent
a8944e2026
commit
2732a904c4
|
@ -0,0 +1,52 @@
|
|||
#include <algorithm>
|
||||
#include <string>
|
||||
#include "Exceptions/NotReachableException.hpp"
|
||||
#include "MacroUtils.hpp"
|
||||
|
||||
#define ENUM_CASE(x, name) \
|
||||
case name::x: \
|
||||
return #x;
|
||||
#
|
||||
#define ENUM_PARSE_CASE(x, name) \
|
||||
case ConstHash(#x): \
|
||||
return name::x;
|
||||
#
|
||||
#define ENUM_PARSE_CASE_INSENSITIVE(x, name) \
|
||||
case ConstHashCI(#x): \
|
||||
return name::x;
|
||||
#
|
||||
#define ARRAY_NAME(x, name) name::x,
|
||||
|
||||
#define ENUM(name, type, values...) \
|
||||
enum class name : type { values }; \
|
||||
class name##Helper { \
|
||||
inline static uint32_t constexpr ConstHash(char const* input) { \
|
||||
return *input ? static_cast<uint32_t>(*input) + 33 * ConstHash(input + 1) : 5381; \
|
||||
} \
|
||||
inline static constexpr char charToLower(const char c) { \
|
||||
return (c >= 'A' && c <= 'Z') ? c + ('a' - 'A') : c; \
|
||||
} \
|
||||
inline static uint32_t constexpr ConstHashCI(char const* input) { \
|
||||
return charToLower(*input) ? static_cast<uint32_t>(charToLower(*input)) + 33 * ConstHashCI(input + 1) \
|
||||
: 5381; \
|
||||
} \
|
||||
\
|
||||
public: \
|
||||
static std::string ToString(name value) { \
|
||||
switch (value) { FOR_EACH(ENUM_CASE, name, values) } \
|
||||
throw NotReachableException(); \
|
||||
} \
|
||||
static name Parse(const std::string& input, bool caseInsensitive = false) { \
|
||||
if (caseInsensitive) \
|
||||
return ParseCaseInsensitive(input); \
|
||||
switch (ConstHash(input.c_str())) { FOR_EACH(ENUM_PARSE_CASE, name, values) } \
|
||||
throw CreatureException("String '" + input + "' could not be parsed as " #name); \
|
||||
} \
|
||||
static std::vector<name> GetValues() { return {FOR_EACH(ARRAY_NAME, name, values)}; } \
|
||||
\
|
||||
private: \
|
||||
static name ParseCaseInsensitive(const std::string& input) { \
|
||||
switch (ConstHashCI(input.c_str())) { FOR_EACH(ENUM_PARSE_CASE_INSENSITIVE, name, values) } \
|
||||
throw CreatureException("String '" + input + "' could not be parsed as " #name); \
|
||||
} \
|
||||
};
|
|
@ -0,0 +1,43 @@
|
|||
#define FE_0(FUNC, arg)
|
||||
#define FE_1(FUNC, arg, X) FUNC(X, arg)
|
||||
#define FE_2(FUNC, arg, X, ...) FUNC(X, arg) FE_1(FUNC, arg, __VA_ARGS__)
|
||||
#define FE_3(FUNC, arg, X, ...) FUNC(X, arg) FE_2(FUNC, arg, __VA_ARGS__)
|
||||
#define FE_4(FUNC, arg, X, ...) FUNC(X, arg) FE_3(FUNC, arg, __VA_ARGS__)
|
||||
#define FE_5(FUNC, arg, X, ...) FUNC(X, arg) FE_4(FUNC, arg, __VA_ARGS__)
|
||||
#define FE_6(FUNC, arg, X, ...) FUNC(X, arg) FE_5(FUNC, arg, __VA_ARGS__)
|
||||
#define FE_7(FUNC, arg, X, ...) FUNC(X, arg) FE_6(FUNC, arg, __VA_ARGS__)
|
||||
#define FE_8(FUNC, arg, X, ...) FUNC(X, arg) FE_7(FUNC, arg, __VA_ARGS__)
|
||||
#define FE_9(FUNC, arg, X, ...) FUNC(X, arg) FE_8(FUNC, arg, __VA_ARGS__)
|
||||
#define FE_10(FUNC, arg, X, ...) FUNC(X, arg) FE_9(FUNC, arg, __VA_ARGS__)
|
||||
#define FE_11(FUNC, arg, X, ...) FUNC(X, arg) FE_10(FUNC, arg, __VA_ARGS__)
|
||||
#define FE_12(FUNC, arg, X, ...) FUNC(X, arg) FE_11(FUNC, arg, __VA_ARGS__)
|
||||
#define FE_13(FUNC, arg, X, ...) FUNC(X, arg) FE_12(FUNC, arg, __VA_ARGS__)
|
||||
#define FE_14(FUNC, arg, X, ...) FUNC(X, arg) FE_13(FUNC, arg, __VA_ARGS__)
|
||||
#define FE_15(FUNC, arg, X, ...) FUNC(X, arg) FE_14(FUNC, arg, __VA_ARGS__)
|
||||
#define FE_16(FUNC, arg, X, ...) FUNC(X, arg) FE_15(FUNC, arg, __VA_ARGS__)
|
||||
#define FE_17(FUNC, arg, X, ...) FUNC(X, arg) FE_16(FUNC, arg, __VA_ARGS__)
|
||||
#define FE_18(FUNC, arg, X, ...) FUNC(X, arg) FE_17(FUNC, arg, __VA_ARGS__)
|
||||
#define FE_19(FUNC, arg, X, ...) FUNC(X, arg) FE_18(FUNC, arg, __VA_ARGS__)
|
||||
#define FE_20(FUNC, arg, X, ...) FUNC(X, arg) FE_19(FUNC, arg, __VA_ARGS__)
|
||||
#define FE_21(FUNC, arg, X, ...) FUNC(X, arg) FE_20(FUNC, arg, __VA_ARGS__)
|
||||
#define FE_22(FUNC, arg, X, ...) FUNC(X, arg) FE_21(FUNC, arg, __VA_ARGS__)
|
||||
#define FE_23(FUNC, arg, X, ...) FUNC(X, arg) FE_22(FUNC, arg, __VA_ARGS__)
|
||||
#define FE_24(FUNC, arg, X, ...) FUNC(X, arg) FE_23(FUNC, arg, __VA_ARGS__)
|
||||
#define FE_25(FUNC, arg, X, ...) FUNC(X, arg) FE_24(FUNC, arg, __VA_ARGS__)
|
||||
#define FE_26(FUNC, arg, X, ...) FUNC(X, arg) FE_25(FUNC, arg, __VA_ARGS__)
|
||||
#define FE_27(FUNC, arg, X, ...) FUNC(X, arg) FE_26(FUNC, arg, __VA_ARGS__)
|
||||
#define FE_28(FUNC, arg, X, ...) FUNC(X, arg) FE_27(FUNC, arg, __VA_ARGS__)
|
||||
#define FE_29(FUNC, arg, X, ...) FUNC(X, arg) FE_28(FUNC, arg, __VA_ARGS__)
|
||||
#define FE_30(FUNC, arg, X, ...) FUNC(X, arg) FE_29(FUNC, arg, __VA_ARGS__)
|
||||
#define FE_31(FUNC, arg, X, ...) FUNC(X, arg) FE_30(FUNC, arg, __VA_ARGS__)
|
||||
#define FE_32(FUNC, arg, X, ...) FUNC(X, arg) FE_31(FUNC, arg, __VA_ARGS__)
|
||||
#define FE_33(FUNC, arg, X, ...) THE_FOREACH_MACRO_CURRENTLY_ONLY_SUPPORTS_UP_TO_32_VALUES FE_32(FUNC, arg, __VA_ARGS__)
|
||||
|
||||
#define GET_MACRO(_0, _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, NAME, ...) \
|
||||
NAME
|
||||
#define FOR_EACH(action, arg, ...) \
|
||||
GET_MACRO(_0, __VA_ARGS__, FE_33, FE_32, FE_31, FE_30, FE_29, FE_28, FE_27, FE_26, FE_25, FE_24, FE_23, FE_22, \
|
||||
FE_21, FE_20, FE_19, FE_18, FE_17, FE_16, FE_15, FE_14, FE_13, FE_12, FE_11, FE_10, FE_9, FE_8, FE_7, \
|
||||
FE_6, FE_5, FE_4, FE_3, FE_2, FE_1, FE_0) \
|
||||
(action, arg, __VA_ARGS__)
|
|
@ -2,16 +2,10 @@
|
|||
#define CREATURELIB_STATISTIC_HPP
|
||||
|
||||
#include <cstdint>
|
||||
#include "Enum.hpp"
|
||||
|
||||
namespace CreatureLib::Core {
|
||||
enum Statistic : uint8_t {
|
||||
Health,
|
||||
PhysicalAttack,
|
||||
PhysicalDefense,
|
||||
MagicalAttack,
|
||||
MagicalDefense,
|
||||
Speed
|
||||
};
|
||||
ENUM(Statistic, uint8_t, Health, PhysicalAttack, PhysicalDefense, MagicalAttack, MagicalDefense, Speed)
|
||||
}
|
||||
|
||||
#endif // CREATURELIB_STATISTIC_HPP
|
||||
|
|
|
@ -31,47 +31,47 @@ namespace CreatureLib::Core {
|
|||
|
||||
[[nodiscard]] inline T GetStat(Statistic stat) const {
|
||||
switch (stat) {
|
||||
case Health: return _health;
|
||||
case PhysicalAttack: return _physicalAttack;
|
||||
case PhysicalDefense: return _physicalDefense;
|
||||
case MagicalAttack: return _magicalAttack;
|
||||
case MagicalDefense: return _magicalDefense;
|
||||
case Speed: return _speed;
|
||||
case CreatureLib::Core::Statistic::Health: return _health;
|
||||
case CreatureLib::Core::Statistic::PhysicalAttack: return _physicalAttack;
|
||||
case CreatureLib::Core::Statistic::PhysicalDefense: return _physicalDefense;
|
||||
case CreatureLib::Core::Statistic::MagicalAttack: return _magicalAttack;
|
||||
case CreatureLib::Core::Statistic::MagicalDefense: return _magicalDefense;
|
||||
case CreatureLib::Core::Statistic::Speed: return _speed;
|
||||
default: throw NotReachableException();
|
||||
}
|
||||
}
|
||||
|
||||
inline void SetStat(Statistic stat, T value) {
|
||||
switch (stat) {
|
||||
case Health: _health = value; break;
|
||||
case PhysicalAttack: _physicalAttack = value; break;
|
||||
case PhysicalDefense: _physicalDefense = value; break;
|
||||
case MagicalAttack: _magicalAttack = value; break;
|
||||
case MagicalDefense: _magicalDefense = value; break;
|
||||
case Speed: _speed = value; break;
|
||||
case CreatureLib::Core::Statistic::Health: _health = value; break;
|
||||
case CreatureLib::Core::Statistic::PhysicalAttack: _physicalAttack = value; break;
|
||||
case CreatureLib::Core::Statistic::PhysicalDefense: _physicalDefense = value; break;
|
||||
case CreatureLib::Core::Statistic::MagicalAttack: _magicalAttack = value; break;
|
||||
case CreatureLib::Core::Statistic::MagicalDefense: _magicalDefense = value; break;
|
||||
case CreatureLib::Core::Statistic::Speed: _speed = value; break;
|
||||
default: throw NotReachableException();
|
||||
}
|
||||
}
|
||||
|
||||
inline void IncreaseStatBy(Statistic stat, T amount) {
|
||||
switch (stat) {
|
||||
case Health: _health += amount; break;
|
||||
case PhysicalAttack: _physicalAttack += amount; break;
|
||||
case PhysicalDefense: _physicalDefense += amount; break;
|
||||
case MagicalAttack: _magicalAttack += amount; break;
|
||||
case MagicalDefense: _magicalDefense += amount; break;
|
||||
case Speed: _speed += amount; break;
|
||||
case CreatureLib::Core::Statistic::Health: _health += amount; break;
|
||||
case CreatureLib::Core::Statistic::PhysicalAttack: _physicalAttack += amount; break;
|
||||
case CreatureLib::Core::Statistic::PhysicalDefense: _physicalDefense += amount; break;
|
||||
case CreatureLib::Core::Statistic::MagicalAttack: _magicalAttack += amount; break;
|
||||
case CreatureLib::Core::Statistic::MagicalDefense: _magicalDefense += amount; break;
|
||||
case CreatureLib::Core::Statistic::Speed: _speed += amount; break;
|
||||
default: throw NotReachableException();
|
||||
}
|
||||
}
|
||||
inline void DecreaseStatBy(Statistic stat, T amount) {
|
||||
switch (stat) {
|
||||
case Health: _health -= amount; break;
|
||||
case PhysicalAttack: _physicalAttack -= amount; break;
|
||||
case PhysicalDefense: _physicalDefense -= amount; break;
|
||||
case MagicalAttack: _magicalAttack -= amount; break;
|
||||
case MagicalDefense: _magicalDefense -= amount; break;
|
||||
case Speed: _speed -= amount; break;
|
||||
case CreatureLib::Core::Statistic::Health: _health -= amount; break;
|
||||
case CreatureLib::Core::Statistic::PhysicalAttack: _physicalAttack -= amount; break;
|
||||
case CreatureLib::Core::Statistic::PhysicalDefense: _physicalDefense -= amount; break;
|
||||
case CreatureLib::Core::Statistic::MagicalAttack: _magicalAttack -= amount; break;
|
||||
case CreatureLib::Core::Statistic::MagicalDefense: _magicalDefense -= amount; break;
|
||||
case CreatureLib::Core::Statistic::Speed: _speed -= amount; break;
|
||||
default: throw NotReachableException();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,9 +2,10 @@
|
|||
#define CREATURELIB_ATTACKCATEGORY_HPP
|
||||
|
||||
#include <cstdint>
|
||||
#include "../../Core/Enum.hpp"
|
||||
|
||||
namespace CreatureLib::Library {
|
||||
enum class AttackCategory : uint8_t { Physical, Magical, Status };
|
||||
ENUM(AttackCategory, uint8_t, Physical, Magical, Status)
|
||||
}
|
||||
|
||||
#endif // CREATURELIB_ATTACKCATEGORY_HPP
|
||||
|
|
|
@ -2,25 +2,16 @@
|
|||
#define CREATURELIB_ATTACKTARGET_HPP
|
||||
|
||||
#include <cstdint>
|
||||
#include "../../Core/Enum.hpp"
|
||||
|
||||
namespace CreatureLib::Library {
|
||||
enum class AttackTarget : uint8_t {
|
||||
Adjacent,
|
||||
AdjacentAlly,
|
||||
AdjacentAllySelf,
|
||||
AdjacentOpponent,
|
||||
ENUM(AttackTarget, uint8_t, Adjacent, AdjacentAlly, AdjacentAllySelf, AdjacentOpponent,
|
||||
|
||||
All,
|
||||
AllAdjacent,
|
||||
AllAdjacentOpponent,
|
||||
AllAlly,
|
||||
AllOpponent,
|
||||
All, AllAdjacent, AllAdjacentOpponent, AllAlly, AllOpponent,
|
||||
|
||||
Any,
|
||||
|
||||
RandomOpponent,
|
||||
Self,
|
||||
};
|
||||
RandomOpponent, Self)
|
||||
}
|
||||
|
||||
#endif // CREATURELIB_ATTACKTARGET_HPP
|
||||
|
|
|
@ -2,13 +2,14 @@
|
|||
#define CREATURELIB_GENDER_HPP
|
||||
|
||||
#include <cstdint>
|
||||
#include "../Core/Enum.hpp"
|
||||
|
||||
namespace CreatureLib::Library {
|
||||
/*!
|
||||
\brief Might be somewhat controversial nowadays, but as many creature battling games only have two genders, we'll
|
||||
hardcode those.
|
||||
*/
|
||||
enum class Gender : uint8_t { Male, Female, Genderless };
|
||||
ENUM(Gender, uint8_t, Male, Female, Genderless)
|
||||
}
|
||||
|
||||
#endif // CREATURELIB_GENDER_HPP
|
||||
|
|
|
@ -0,0 +1,40 @@
|
|||
#ifdef TESTS_BUILD
|
||||
#include <cstring>
|
||||
#include "../extern/catch.hpp"
|
||||
#include "../src/Core/Enum.hpp"
|
||||
|
||||
ENUM(TestEnum, uint8_t, Val1, Val2, Val3)
|
||||
|
||||
TEST_CASE("Parse Enum case sensitive", "[Utilities]") {
|
||||
CHECK(TestEnumHelper::Parse("Val1") == TestEnum::Val1);
|
||||
CHECK(TestEnumHelper::Parse("Val2") == TestEnum::Val2);
|
||||
CHECK(TestEnumHelper::Parse("Val3") == TestEnum::Val3);
|
||||
CHECK_THROWS(TestEnumHelper::Parse("Val4"));
|
||||
CHECK_THROWS(TestEnumHelper::Parse("val1"));
|
||||
}
|
||||
|
||||
TEST_CASE("Parse Enum case insensitive", "[Utilities]") {
|
||||
CHECK(TestEnumHelper::Parse("Val1", true) == TestEnum::Val1);
|
||||
CHECK(TestEnumHelper::Parse("Val2", true) == TestEnum::Val2);
|
||||
CHECK(TestEnumHelper::Parse("Val3", true) == TestEnum::Val3);
|
||||
CHECK(TestEnumHelper::Parse("val1", true) == TestEnum::Val1);
|
||||
CHECK(TestEnumHelper::Parse("vAL2", true) == TestEnum::Val2);
|
||||
CHECK(TestEnumHelper::Parse("VaL3", true) == TestEnum::Val3);
|
||||
CHECK_THROWS(TestEnumHelper::Parse("Val4", true));
|
||||
}
|
||||
|
||||
TEST_CASE("Enum To String", "[Utilities]") {
|
||||
CHECK(TestEnumHelper::ToString(TestEnum::Val1) == "Val1");
|
||||
CHECK(TestEnumHelper::ToString(TestEnum::Val2) == "Val2");
|
||||
CHECK(TestEnumHelper::ToString(TestEnum::Val3) == "Val3");
|
||||
}
|
||||
|
||||
TEST_CASE("Enum Get Values", "[Utilities]") {
|
||||
auto vec = TestEnumHelper::GetValues();
|
||||
REQUIRE(vec.size() == 3);
|
||||
CHECK(vec[0] == TestEnum::Val1);
|
||||
CHECK(vec[1] == TestEnum::Val2);
|
||||
CHECK(vec[2] == TestEnum::Val3);
|
||||
}
|
||||
|
||||
#endif
|
Loading…
Reference in New Issue