Máscaras de bits conformes estándar de C++ 11 utilizando la clase enum
c++11 bit-fields (5)
¿Puede implementar máscaras de bits seguras de tipo conformante estándar (como se describe en 17.5.2.1.3 del borrador n3242) utilizando la clase enum? De la forma en que lo leí, un tipo T es una máscara de bits si admite los operadores |, &, ^, ~, | =, & = y ^ = y además puede hacer si (l & r) donde l y r son de tipo T . Faltan en la lista el operador! = Y == y para permitir la clasificación, probablemente también desee sobrecargar <.
Lograr que los operadores funcionen es un código molesto pero no veo cómo hacerlo si (l & r). Al menos lo siguiente no se compila con GCC (además de ser extremadamente peligroso, ya que permite una conversión implícita errónea a int):
enum class Foo{
operator bool(){
return (unsigned)*this;
}
};
EDIT: Ahora sé con certeza que las clases de enumeración no pueden tener miembros. La pregunta real de cómo hacerlo si (l & r) permanece sin embargo.
Faltan en la lista el operador! = Y ==
Esos operadores ya son compatibles con tipos de enumeración, tipos de enteros y std::bitset
por lo que no hay necesidad de sobrecargarlos.
y para permitir la clasificación uno probablemente también quiera sobrecargar <.
¿Por qué quieres ordenar las máscaras de bits? ¿Es (a | b) mayor que (a | c)? ¿Está std::ios::in
menos que std::ios::app
? ¿Importa? Los operadores relacionales siempre se definen para tipos de enumeración y tipos enteros de todos modos.
Para responder a la pregunta principal, implementaría &
como una función no miembro sobrecargada:
Foo operator&(Foo l, Foo r)
{
typedef std::underlying_type<Foo>::type ut;
return static_cast<Foo>(static_cast<ut>(l) & static_cast<ut>(r));
}
Creo que todas las operaciones requeridas para los tipos de máscara de bits se podrían definir para enums de ámbito, pero no los requisitos como
Ci & Cj es distinto de cero y Ci & Cj iszero
y:
El valor Y se establece en el objeto X es la expresión X & Y no es cero.
Dado que las enumeraciones de ámbito no admiten la conversión de impicit a tipos enteros, no puede probar de forma confiable si es distinto de cero o no. Tendría que escribir if ((X&Y) != bitmask{})
y no creo que esa sea la intención del comité.
(Inicialmente pensé que se podrían usar para definir tipos de máscara de bits, luego recordé que había intentado implementar uno utilizando enumeraciones de ámbito y encontré el problema con la prueba de cero / distinto de cero).
Edición: Acabo de recordar que std::launch
es un tipo de enumeración de ámbito y un tipo de máscara de bits ... ¡por lo que aparentemente las enums de ámbito pueden ser tipos de máscara de bits!
Creo que puedes ... Tendrás que agregar operadores para cosas de máscara de bits. No lo hice aquí, pero podría sobrecargar fácilmente a cualquier operador relacional.
/**
*
*/
// NOTE: I changed to a more descriptive and consistent name
// This needs to be a real bitmask type.
enum class file_permissions : int
{
no_perms = 0,
owner_read = 0400,
owner_write = 0200,
owner_exe = 0100,
owner_all = 0700,
group_read = 040,
group_write = 020,
group_exe = 010,
group_all = 070,
others_read = 04,
others_write = 02,
others_exe = 01,
others_all = 07,
all_all = owner_all | group_all | others_all, // 0777
set_uid_on_exe = 04000,
set_gid_on_exe = 02000,
sticky_bit = 01000,
perms_mask = all_all | set_uid_on_exe | set_gid_on_exe | sticky_bit, // 07777
perms_not_known = 0xffff,
add_perms = 0x1000,
remove_perms = 0x2000,
symlink_perms = 0x4000
};
inline constexpr file_permissions
operator&(file_permissions x, file_permissions y)
{
return static_cast<file_permissions>
(static_cast<int>(x) & static_cast<int>(y));
}
inline constexpr file_permissions
operator|(file_permissions x, file_permissions y)
{
return static_cast<file_permissions>
(static_cast<int>(x) | static_cast<int>(y));
}
inline constexpr file_permissions
operator^(file_permissions x, file_permissions y)
{
return static_cast<file_permissions>
(static_cast<int>(x) ^ static_cast<int>(y));
}
inline constexpr file_permissions
operator~(file_permissions x)
{
return static_cast<file_permissions>(~static_cast<int>(x));
}
inline file_permissions &
operator&=(file_permissions & x, file_permissions y)
{
x = x & y;
return x;
}
inline file_permissions &
operator|=(file_permissions & x, file_permissions y)
{
x = x | y;
return x;
}
inline file_permissions &
operator^=(file_permissions & x, file_permissions y)
{
x = x ^ y;
return x;
}
Las enumeraciones con alcance (aquellas creadas con enum class
enum struct
o enum struct
) no son clases. No pueden tener funciones miembro, solo proporcionan enumeradores incluidos (no visibles a nivel de espacio de nombres).
No estoy completamente seguro de cuáles son sus criterios de aceptación, pero solo puede hacer operator &
devolver una clase de envoltorio con las conversiones apropiadas y un explicit operator bool
:
#include <type_traits>
template<typename T> using Underlying = typename std::underlying_type<T>::type;
template<typename T> constexpr Underlying<T>
underlying(T t) { return Underlying<T>(t); }
template<typename T> struct TruthValue {
T t;
constexpr TruthValue(T t): t(t) { }
constexpr operator T() const { return t; }
constexpr explicit operator bool() const { return underlying(t); }
};
enum class Color { RED = 0xff0000, GREEN = 0x00ff00, BLUE = 0x0000ff };
constexpr TruthValue<Color>
operator&(Color l, Color r) { return Color(underlying(l) & underlying(r)); }
Todos sus otros operadores pueden continuar devolviendo Color
, por supuesto:
constexpr Color
operator|(Color l, Color r) { return Color(underlying(l) | underlying(r)); }
constexpr Color operator~(Color c) { return Color(~underlying(c)); }
int main() {
constexpr Color YELLOW = Color::RED | Color::GREEN;
constexpr Color WHITE = Color::RED | Color::GREEN | Color::BLUE;
static_assert(YELLOW == (WHITE & ~Color::BLUE), "color subtraction");
return (YELLOW & Color::BLUE) ? 1 : 0;
}
Un breve ejemplo de enum-flags abajo.
#indlude "enum_flags.h"
ENUM_FLAGS(foo_t)
enum class foo_t
{
none = 0x00
,a = 0x01
,b = 0x02
};
ENUM_FLAGS(foo2_t)
enum class foo2_t
{
none = 0x00
,d = 0x01
,e = 0x02
};
int _tmain(int argc, _TCHAR* argv[])
{
if(flags(foo_t::a & foo_t::b)) {};
// if(flags(foo2_t::d & foo_t::b)) {}; // Type safety test - won''t compile if uncomment
};
ENUM_FLAGS (T) es una macro, definida en enum_flags.h (menos de 100 líneas, de uso gratuito sin restricciones).