Skip to content

Instantly share code, notes, and snippets.

@Journeyman1337
Last active July 30, 2023 02:01
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save Journeyman1337/38d6e6e767200c562a465bd64c16aadd to your computer and use it in GitHub Desktop.
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.
// SPDX-FileCopyrightText: 2020 Marin Peko <marinpeko5@gmail.com>
//
// 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 };
};
}
// SPDX-FileCopyrightText: 2023 Daniel Aimé Valcour <fosssweeper@gmail.com>
//
// 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