Files
scylladb/enum_set.hh
Pekka Enberg 38a54df863 Fix pre-ScyllaDB copyright statements
People keep tripping over the old copyrights and copy-pasting them to
new files. Search and replace "Cloudius Systems" with "ScyllaDB".

Message-Id: <1460013664-25966-1-git-send-email-penberg@scylladb.com>
2016-04-08 08:12:47 +03:00

231 lines
5.9 KiB
C++

/*
* Copyright (C) 2015 ScyllaDB
*/
/*
* This file is part of Scylla.
*
* Scylla is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Scylla 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 Scylla. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <cstddef>
#include <type_traits>
#include <limits>
/**
*
* Allows to take full advantage of compile-time information when operating
* on a set of enum values.
*
* Examples:
*
* enum class x { A, B, C };
* using my_enum = super_enum<x, x::A, x::B, x::C>;
* using my_enumset = enum_set<my_enum>;
*
* static_assert(my_enumset::frozen<x::A, x::B>::contains<x::A>(), "it should...");
*
* assert(my_enumset::frozen<x::A, x::B>::contains(my_enumset::prepare<x::A>()));
*
* assert(my_enumset::frozen<x::A, x::B>::contains(x::A));
*
*/
template<typename EnumType, EnumType... Items>
struct super_enum {
using enum_type = EnumType;
template<enum_type... values>
struct max {
static constexpr enum_type max_of(enum_type a, enum_type b) {
return a > b ? a : b;
}
template<enum_type first, enum_type second, enum_type... rest>
static constexpr enum_type get() {
return max_of(first, get<second, rest...>());
}
template<enum_type first>
static constexpr enum_type get() { return first; }
static constexpr enum_type value = get<values...>();
};
template<enum_type... values>
struct min {
static constexpr enum_type min_of(enum_type a, enum_type b) {
return a < b ? a : b;
}
template<enum_type first, enum_type second, enum_type... rest>
static constexpr enum_type get() {
return min_of(first, get<second, rest...>());
}
template<enum_type first>
static constexpr enum_type get() { return first; }
static constexpr enum_type value = get<values...>();
};
using sequence_type = typename std::underlying_type<enum_type>::type;
template<enum_type Elem>
static constexpr sequence_type sequence_for() {
return static_cast<sequence_type>(Elem);
}
static sequence_type sequence_for(enum_type elem) {
return static_cast<sequence_type>(elem);
}
static constexpr sequence_type max_sequence = sequence_for<max<Items...>::value>();
static constexpr sequence_type min_sequence = sequence_for<min<Items...>::value>();
static_assert(min_sequence >= 0, "negative enum values unsupported");
};
template<typename Enum>
class enum_set {
public:
using mask_type = size_t; // TODO: use the smallest sufficient type
using enum_type = typename Enum::enum_type;
private:
mask_type _mask;
constexpr enum_set(mask_type mask) : _mask(mask) {}
template<enum_type Elem>
static constexpr unsigned shift_for() {
return Enum::template sequence_for<Elem>();
}
public:
constexpr enum_set() : _mask(0) {}
static constexpr enum_set from_mask(mask_type mask) {
return enum_set(mask);
}
static inline mask_type mask_for(enum_type e) {
return mask_type(1) << Enum::sequence_for(e);
}
template<enum_type Elem>
static constexpr mask_type mask_for() {
return mask_type(1) << shift_for<Elem>();
}
struct prepared {
mask_type mask;
bool operator==(const prepared& o) const {
return mask == o.mask;
}
};
static prepared prepare(enum_type e) {
return {mask_for(e)};
}
template<enum_type e>
static constexpr prepared prepare() {
return {mask_for<e>()};
}
static_assert(std::numeric_limits<mask_type>::max() >= ((size_t)1 << Enum::max_sequence), "mask type too small");
template<enum_type e>
bool contains() const {
return bool(_mask & mask_for<e>());
}
bool contains(enum_type e) const {
return bool(_mask & mask_for(e));
}
template<enum_type e>
void remove() {
_mask &= ~mask_for<e>();
}
void remove(enum_type e) {
_mask &= ~mask_for(e);
}
template<enum_type e>
void set() {
_mask |= mask_for<e>();
}
template<enum_type e>
void set_if(bool condition) {
_mask |= mask_type(condition) << shift_for<e>();
}
void set(enum_type e) {
_mask |= mask_for(e);
}
explicit operator bool() const {
return bool(_mask);
}
mask_type mask() const {
return _mask;
}
template<enum_type... items>
struct frozen {
template<enum_type first>
static constexpr mask_type make_mask() {
return mask_for<first>();
}
static constexpr mask_type make_mask() {
return 0;
}
template<enum_type first, enum_type second, enum_type... rest>
static constexpr mask_type make_mask() {
return mask_for<first>() | make_mask<second, rest...>();
}
static constexpr mask_type mask = make_mask<items...>();
template<enum_type Elem>
static constexpr bool contains() {
return mask & mask_for<Elem>();
}
static bool contains(enum_type e) {
return mask & mask_for(e);
}
static bool contains(prepared e) {
return mask & e.mask;
}
static enum_set<Enum> unfreeze() {
return enum_set<Enum>(mask);
}
};
template<enum_type... items>
static enum_set<Enum> of() {
return frozen<items...>::unfreeze();
}
};