Last active
July 30, 2023 02:01
-
-
Save Journeyman1337/38d6e6e767200c562a465bd64c16aadd to your computer and use it in GitHub Desktop.
Funky bitflags stuff. Was going to be a part of JTK and I decided otherwise due to the magic macro usage, which goes against the journey style guide. I think its kindof cool though.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// SPDX-FileCopyrightText: 2020 Marin Peko <[email protected]> | |
// | |
// SPDX-License-Identifier: MIT | |
/* | |
The following is based on the bitflags library by Marin Peko. | |
https://github.com/m-peko/bitflags | |
It was adapted for usage with JTK. | |
*/ | |
#pragma once | |
#include <jtk/fundamental_types.hpp> | |
#include <jtk/math.hpp> | |
#include <jtk/concepts.hpp> | |
#include <cstdint> | |
#include <utility> | |
#include <type_traits> | |
namespace jtk::inl | |
{ | |
template<typename IMPL> | |
struct flag_impl_underlying_type_trait | |
{ | |
using type_t = | |
jtk::min_sized_unsigned_t< | |
IMPL::__end - IMPL::__begin + 1 | |
>; | |
}; | |
template<typename IMPL> | |
using flag_impl_underlying_t = jtk::inl::flag_impl_underlying_type_trait<IMPL>::type_t; | |
} // namespace jtk::inl | |
#define JTK_BEGIN_BITFLAGS(NAME) \ | |
template <jtk::unsigned_concept UNDERLYING_TTARG> \ | |
class NAME##FlagsImpl \ | |
{ \ | |
private: \ | |
UNDERLYING_TTARG bits = 0; \ | |
public: \ | |
using flag_t = NAME##FlagsImpl<UNDERLYING_TTARG>; \ | |
constexpr NAME##FlagsImpl() noexcept = default; \ | |
constexpr NAME##FlagsImpl(UNDERLYING_TTARG bits) noexcept : bits(bits) {} \ | |
constexpr NAME##FlagsImpl(const flag_t& flags) noexcept : bits(flags.bits) {} \ | |
template<jtk::same_concept<flag_t> ...N_TTARG> \ | |
constexpr NAME##FlagsImpl(const flag_t& flags_a, const N_TTARG&... flags_n) noexcept \ | |
{ \ | |
this->Add(flags_a, flags_n...); \ | |
} \ | |
static constexpr flag_t None = flag_t(0); \ | |
static constexpr int __begin = __LINE__; | |
#define JTK_END_BITFLAGS(NAME) \ | |
constexpr bool Contains(const flag_t& flags) const noexcept \ | |
{ \ | |
return \ | |
(*this & flags) == flags; \ | |
} \ | |
constexpr void Add(const flag_t& flags) noexcept \ | |
{ \ | |
*this |= flags; \ | |
} \ | |
template<jtk::same_concept<flag_t> ...N_TTARG> \ | |
constexpr void Add(const flag_t& flags_a, const N_TTARG&... flags_n) noexcept \ | |
{ \ | |
this->Add(flags_a); \ | |
this->Add(flags_n...); \ | |
} \ | |
constexpr void Remove(const flag_t& flags) noexcept \ | |
{ \ | |
*this &= ~flags; \ | |
} \ | |
template<jtk::same_concept<flag_t> ...N_TTARG> \ | |
constexpr void Remove(const flag_t& flags_a, const N_TTARG&... flags_n) noexcept \ | |
{ \ | |
this->Remove(flags_a); \ | |
this->Remove(flags_n...); \ | |
} \ | |
constexpr void Set(const flag_t& flags, bool value) noexcept \ | |
{ \ | |
if (value) \ | |
{ \ | |
this->Add(flags); \ | |
} \ | |
else \ | |
{ \ | |
this->Remove(flags); \ | |
} \ | |
} \ | |
constexpr void Toggle(const flag_t& flags) noexcept \ | |
{ \ | |
this->Set(flags, !this->Contains(flags)); \ | |
} \ | |
constexpr bool GetIsEmpty() \ | |
{ \ | |
return \ | |
this->bits == flag_t::None; \ | |
} \ | |
constexpr void Clear() \ | |
{ \ | |
*this = flag_t::None; \ | |
} \ | |
friend constexpr bool operator==(const flag_t& lhs, const flag_t& rhs) \ | |
{ \ | |
return \ | |
lhs.bits == rhs.bits; \ | |
}; \ | |
friend constexpr bool operator!=(const flag_t& lhs, const flag_t& rhs) \ | |
{ \ | |
return \ | |
lhs.bits != rhs.bits; \ | |
}; \ | |
friend constexpr flag_t operator~(const flag_t& rhs) \ | |
{ \ | |
return \ | |
flag_t(~rhs.bits); \ | |
}; \ | |
friend constexpr flag_t operator|(const flag_t& lhs, const flag_t& rhs) \ | |
{ \ | |
return \ | |
flag_t(lhs.bits | rhs.bits); \ | |
}; \ | |
friend constexpr flag_t operator&(const flag_t& lhs, const flag_t& rhs) \ | |
{ \ | |
return \ | |
flag_t(lhs.bits & rhs.bits); \ | |
}; \ | |
friend constexpr bool operator^(const flag_t& lhs, const flag_t& rhs) \ | |
{ \ | |
return \ | |
flag_t(lhs.bits ^ rhs.bits); \ | |
}; \ | |
constexpr flag_t& operator|=(const flag_t& rhs) \ | |
{ \ | |
this->bits |= rhs.bits; \ | |
return *this; \ | |
}; \ | |
constexpr flag_t& operator&=(const flag_t& rhs) \ | |
{ \ | |
this->bits &= rhs.bits; \ | |
return *this; \ | |
}; \ | |
constexpr flag_t& operator^=(const flag_t& rhs) \ | |
{ \ | |
this->bits ^= rhs.bits; \ | |
return *this; \ | |
}; \ | |
}; \ | |
using NAME##Flags = \ | |
NAME##FlagsImpl< \ | |
jtk::inl::flag_impl_underlying_t< \ | |
NAME##FlagsImpl< \ | |
jtk::u_dont_care \ | |
> \ | |
> \ | |
>; | |
#define JTK_ALL_BITFLAG() \ | |
static constexpr flag_t All = flag_t(jtk::fill_bits<UNDERLYING_TTARG>(__LINE__ - __begin - 2)); \ | |
static constexpr int __end = __LINE__; | |
#define JTK_BITFLAG(NAME) static constexpr flag_t NAME = flag_t(jtk::bit<UNDERLYING_TTARG>(__LINE__ - __begin - 1)); | |
#define JTK_COMBINE_BITFLAG(NAME, ...) static constexpr flag_t NAME = flag_t(__VA_ARGS__); | |
namespace jtk | |
{ | |
template<typename TTARG> | |
concept bitflags_concept = | |
requires(TTARG t) | |
{ | |
{ TTARG::__begin }; | |
{ TTARG::__end }; | |
{ TTARG::All }; | |
{ TTARG::None }; | |
}; | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// SPDX-FileCopyrightText: 2023 Daniel Aimé Valcour <[email protected]> | |
// | |
// SPDX-License-Identifier: MIT | |
#include <catch2/catch_all.hpp> | |
#include <jtk/bitflags.hpp> | |
JTK_BEGIN_BITFLAGS(Fruit) | |
JTK_BITFLAG(Apple) | |
JTK_BITFLAG(Orange) | |
JTK_BITFLAG(Grape) | |
JTK_BITFLAG(Tomato) | |
JTK_ALL_BITFLAG() | |
JTK_COMBINE_BITFLAG(NotVegetable, Apple, Orange, Grape) | |
JTK_END_BITFLAGS(Fruit) | |
TEST_CASE("bitflags") | |
{ | |
FruitFlags fruit = FruitFlags::Apple | FruitFlags::Orange; | |
CHECK(fruit.Contains(FruitFlags::Orange | FruitFlags::Apple)); | |
CHECK_FALSE(fruit.Contains(FruitFlags::Grape)); | |
CHECK(FruitFlags::All.Contains(FruitFlags::Apple | FruitFlags::Orange | FruitFlags::Grape)); | |
CHECK_FALSE(FruitFlags::None.Contains(FruitFlags::Apple)); | |
CHECK_FALSE(FruitFlags::NotVegetable.Contains(FruitFlags::Tomato)); | |
CHECK(FruitFlags::All == (FruitFlags::Apple | FruitFlags::Orange | FruitFlags::Grape | FruitFlags::Tomato)); | |
CHECK(FruitFlags::All == FruitFlags(FruitFlags::Apple | FruitFlags::Orange | FruitFlags::Grape | FruitFlags::Tomato)); | |
CHECK(FruitFlags::All == FruitFlags(FruitFlags::Apple, FruitFlags::Orange, FruitFlags::Grape, FruitFlags::Tomato)); | |
fruit.Set(FruitFlags::Tomato, true); | |
CHECK(fruit.Contains(FruitFlags::Tomato)); | |
fruit.Toggle(FruitFlags::Tomato); | |
CHECK_FALSE(fruit.Contains(FruitFlags::Tomato)); | |
fruit.Clear(); | |
CHECK(fruit == FruitFlags::None); | |
fruit.Add(FruitFlags::All); | |
CHECK(fruit == FruitFlags::All); | |
fruit.Remove(FruitFlags::Grape, FruitFlags::Apple); | |
CHECK(fruit == (FruitFlags::Tomato | FruitFlags::Orange)); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment