 |
C++Talk.NET C++ language newsgroups
|
| View previous topic :: View next topic |
| Author |
Message |
Thiago R. Adams Guest
|
Posted: Thu Jul 14, 2005 1:16 am Post subject: How to implement a type safe bit set? |
|
|
I would like to create a named bitset.
In C/C++ it's generally made with enums or #define.
for example:
enum FontStyle {
FontStyleBold = 1 << 1,
FontStyleItalic = 1 << 2,
FontStyleUnderline = 1 << 3
};
unsigned long fontStyle = FontStyleBold + FontStyleItalic;
if (fontStyle & FontStyleBold)
{
. . .
}
But this solution is not strong typed.
std::bitset is not so good for this case because it doens't use names,
and std::set is not otimized for this situation.
The boost libray has another alternative?
How I can made this with correct type checking?
The Pascal language has the "Set" concept that doens't exists in C++,
but is usefull for this case.
I´m trying to make the use like this:
FontStyle fontStyle; // empty set
fontStyle = Bold; // only Bold bit is set
fontStyle = Bold + Italic; // Bold and Italic are set
fontStyle += Bold; // Bold is appended in the set
fontStyle -= Bold; // Bold is removed from the set
fontStyle.has(Bold); // true is Bold bit is set
// true is Bold and Italic bit are set
fontStyle.has(Bold + Italic);
fontStyle = 1; // error
FontStyle fontStyle1 = Bold;
FontStyle fontStyle2 = Italic;
fontStyle1 = fontStyle1 + fontStyle2; // union
fontStyle1 = fontStyle1 - fontStyle2; // difference
My first approach:
class FontStyle {
friend FontStyle operator + (const FontStyle& fs1, const FontStyle&
fs2);
unsigned long value;
protected:
explicit FontStyle(unsigned long i) : value(i) {}
public:
FontStyle() : value(0) {}
bool Has(const FontStyle& fs) const {
return (value & fs.value) != 0;
}
};
FontStyle operator + (const FontStyle& fs1, const FontStyle& fs2) {
return FontStyle(fs1.value + fs2.value);
}
static const struct SBold : public FontStyle { SBold() : FontStyle(1 <<
1){} } Bold;
static const struct SItalic : public FontStyle { SItalic() :
FontStyle(1 << 2 ){} } Italic;
static const struct SUnderline : public FontStyle { SUnderline() :
FontStyle(1 << 3){} } Underline;
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
|
|
| Back to top |
|
 |
Ulrich Eckhardt Guest
|
Posted: Fri Jul 15, 2005 10:58 am Post subject: Re: How to implement a type safe bit set? |
|
|
Thiago R. Adams wrote:
| Quote: | I would like to create a named bitset.
In C/C++ it's generally made with enums or #define.
for example:
enum FontStyle {
FontStyleBold = 1 << 1,
FontStyleItalic = 1 << 2,
FontStyleUnderline = 1 << 3
};
unsigned long fontStyle = FontStyleBold + FontStyleItalic;
if (fontStyle & FontStyleBold)
{
. . .
}
But this solution is not strong typed.
|
True, but almost. The thing you're missing is to overload operator+ for your
enumeration:
FontStyle operator+( FontStyle s1, FontStyle s2)
{
return static_cast
static_cast<unsigned>(s1)+static_cast<unsigned>(s2));
}
Uli
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
|
|
| Back to top |
|
 |
Dave Harris Guest
|
Posted: Fri Jul 15, 2005 11:00 am Post subject: Re: How to implement a type safe bit set? |
|
|
[email]thiago.adams (AT) gmail (DOT) com[/email] (Thiago R. Adams) wrote (abridged):
| Quote: | I would like to create a named bitset.
[...]
But this solution is not strong typed.
|
Provide operator overloads for the enum.
enum FontStyle {
FontStyleBold = 1 << 1,
FontStyleItalic = 1 << 2,
FontStyleUnderline = 1 << 3
};
FontStyle &operator|( FontStyle lhs, FontStyle rhs ) {
return lhs = FontStyle( lhs | rhs );
}
-- Dave Harris, Nottingham, UK.
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
|
|
| Back to top |
|
 |
tony_in_da_uk@yahoo.co.uk Guest
|
Posted: Fri Jul 15, 2005 11:10 am Post subject: Re: How to implement a type safe bit set? |
|
|
Hi Thiago,
This is the sort of post I thought people would swoop on - I think it's
natural for C++ programmers to have tried to improve enum-style
facilities - but as it's been a couple days and nobody else's answered
I'll chip in. First though, note that bitwise operations traditionally
use the |, &, ^ and ~ operators, rather than +, - etc.. A rough and
poorly-tested implementation follows. Note the general ugliness: all
the explicit treatment of Value and Flags conversions disambiguates
code involving the non-explicit constructor and bool() operator, both
of which I feel are desirable for natural usage.
Another ugliness is that the streaming operator doesn't output symbolic
names. Particularly if you're prepared to make a few simplifying
assumptions such as values starting at one and doubling, it's possible
to reimplement this as a preprocessor macro taking a list of
identifiers and use the # operator to save the string representation,
then parse this at run time. Boring to code though, run-time parsing's
a bit tacky (as are #defines), and I don't have my old code around
(which was for incrementing enums rather than powers-of-two. Can't
remember, but I think I must have relied on GNU g++ __VAR_ARGS__ for
that one, so it'd be non-portable.
Cheers,
Tony
template <typename VALUE>
class Flags
{
public:
Flags(VALUE value) : value_(value) { }
Flags(const Flags& rhs) : value_(rhs.value_) { }
Flags() : value_(0) { }
Flags& operator=(Flags rhs)
{ value_ = rhs.value_; return *this; }
Flags& operator|=(Flags rhs)
{ value_ |= rhs.value_; return *this; }
Flags& operator&=(Flags rhs)
{ value_ &= rhs.value_; return *this; }
Flags& operator^=(Flags rhs)
{ value_ ^= rhs.value_; return *this; }
Flags operator~() const
{ return static_cast<VALUE>(~value_); }
Flags operator|(Flags rhs) const
{ return static_cast<VALUE>(value_ | rhs.value_); }
Flags operator&(Flags rhs) const
{ return static_cast<VALUE>(value_ & rhs.value_); }
Flags operator^(Flags rhs) const
{ return static_cast<VALUE>(value_ ^ rhs.value_); }
Flags operator|(VALUE rhs) const
{ return static_cast<VALUE>(value_ | rhs); }
Flags operator&(VALUE rhs) const
{ return static_cast<VALUE>(value_ & rhs); }
Flags operator^(VALUE rhs) const
{ return static_cast<VALUE>(value_ ^ rhs); }
friend Flags operator~(VALUE value)
{ return ~Flags(value); }
friend Flags operator|(VALUE lhs, Flags rhs)
{ return rhs | lhs; }
friend Flags operator&(VALUE lhs, Flags rhs)
{ return rhs & lhs; }
friend Flags operator^(VALUE lhs, Flags rhs)
{ return rhs ^ lhs; }
friend Flags operator|(VALUE lhs, VALUE rhs)
{ return Flags(lhs) | rhs; }
friend Flags operator&(VALUE lhs, VALUE rhs)
{ return Flags(lhs) & rhs; }
friend Flags operator^(VALUE lhs, VALUE rhs)
{ return Flags(lhs) ^ rhs; }
operator bool() const { return value_; }
friend std::ostream& operator<<(std::ostream& os, const Flags& c)
{
return os << c.value_;
}
private:
int value_;
};
enum Font_Style { BOLD = 1, ITALIC = 2, UNDERLINE = 4 };
enum Parts { ENGINE = 1, GEARBOX = 2, BRAKES = 4 };
int main()
{
Flags
b = a;
a |= ITALIC;
std::cout << "a " << a << ", b " << b << std::endl;
Flags
q = ~p & (ENGINE | GEARBOX) & ~BRAKES;
// p = a; // can't - type mismatch
// Flags<Parts> r(ITALIC); // wrong VALUE type
std::cout << "p " << p << ", q " << q << std::endl;
if (p & ENGINE) // triggers operator bool(), unlike following...
std::cout << "p & ENGINE == " << (p & ENGINE) << std::endl;
}
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
|
|
| Back to top |
|
 |
Thiago R. Adams Guest
|
Posted: Fri Jul 15, 2005 2:50 pm Post subject: Re: How to implement a type safe bit set? |
|
|
Hi,
Thanks all for sugestions,
I make another version. The operators can be changed from "+" to "|".
The correct mathematic symbol is "U" and inverted "U"
(http://mathworld.wolfram.com/Set.html) but "U" is not c++ operator .
The c++ 0.x has sugestions for enum class and nullptr. I think that
"Sets" are usefull like enums, and the "empty set" is usefull too.
Is much better if compiler check it in compiler time. Mybe "enum
union"?
enum union FontStyle { bold, italic, underline };
FontStyle f = bold + italic;
---
template<class T>
class Set
{
unsigned long value;
inline static unsigned long to_uint(const T &e) { return (1 << e); }
inline static unsigned long to_uint(const Set
v.value;}
inline static unsigned long to_uint(const Set<void>& v) { return 0; }
public:
typedef T Type;
inline explicit Set(T e1, T e2) : value(to_uint(e1) + to_uint(e2)) {
}
inline explicit Set(T e1, T e2, T e3) : value(to_uint(e1) +
to_uint(e2) + to_uint(e3)) { }
inline explicit Set(T e) : value(to_uint(e)) {}
inline Set() : value(0) {}
template<class V> bool Has(const V& v) const { return (value &
to_uint(v)) == to_uint(v); }
void Clear() { value = 0; }
bool IsEmpty() const { return value == 0; }
template<class V> Set<T>& operator = (const V& v) { value =
to_uint(v); return *this; }
template<class V> bool operator == (const V& v) const { return value
== to_uint(v); }
template<class V> bool operator != (const V& v) const { return value
!= to_uint(v); }
template<class V> Set<T>& operator += (const V& v) { value |=
to_uint(v); return *this; }
template<class V> Set<T>& operator -= (const V& v) { value &= ~
to_uint(v); return *this; }
template<class V> Set<T> operator +(const V& v) { Set<T> s; s.value =
(value | to_uint(v)); return s; }
template<class V> Set<T> operator -(const V& v) { Set<T> s; s.value =
(value & ~ to_uint(v)); return s; }
};
//empty set
template<> class Set<void>
{
public:
template<class V> bool Has(const V& v) const { return to_uint(v) == 0;
}
template<class V> bool operator == (const V& v) const { return 0 ==
to_uint(v); }
template<class V> bool operator != (const V& v) const { return 0 !=
to_uint(v); }
};
static const Set<void> empty;
------------------
sample:
enum FontStyle { Bold, Italic, Underline };
inline Set<FontStyle> operator + (FontStyle e1, FontStyle e2) {
return Set<FontStyle>(e1,e2);
}
Set<FontStyle> fontStyle = Bold + Italic;
fontStyle.Has(Bold); // true
//etc...
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
|
|
| Back to top |
|
 |
REH Guest
|
Posted: Sun Jul 17, 2005 8:27 pm Post subject: Re: How to implement a type safe bit set? |
|
|
"Thiago R. Adams" <thiago.adams (AT) gmail (DOT) com> wrote
| Quote: | I would like to create a named bitset.
In C/C++ it's generally made with enums or #define.
for example:
enum FontStyle {
FontStyleBold = 1 << 1,
FontStyleItalic = 1 << 2,
FontStyleUnderline = 1 << 3
};
unsigned long fontStyle = FontStyleBold + FontStyleItalic;
if (fontStyle & FontStyleBold)
{
. . .
}
But this solution is not strong typed.
std::bitset is not so good for this case because it doens't use names,
and std::set is not otimized for this situation.
The boost libray has another alternative?
How I can made this with correct type checking?
The Pascal language has the "Set" concept that doens't exists in C++,
but is usefull for this case.
I have an implementation of the Pascal's set type in my C++ library here: |
http://www.richherrick.com/software/index.html
It basically ties an enum (normally numbered--no need to do the bit shifts
as above) to a class template.
For example:
enum color_type
{NON_COLOR = -1, RED, ORANGE, YELLOW, GREEN, BLUE, INDIGO, VIOLET,
NUM_COLORS};
typedef herrick::pascal_set
with "color_set", you basically have a type-safe bit map (one bit for each
color).
Constructors:
pascal_set();
-- This creates a null (empty) set.
pascal_set(const NullSetType&);
-- This creates a null (empty) set using the "NULL_SET" constant object.
pascal_set(const UniversalSetType&);
-- This creates a set containing all elements within the domain using the
-- "UNIVERSAL_SET" constant object.
explicit pascal_set(element_type e01, ...);
-- This creates a set containing the give elements. for efficiency and type
-- safety this is actually several constructors, each taking a different
number
-- of elements. More may be added as needed. The current limit is 8
elements.
explicit pascal_set(const element_type* e);
-- This creates a set from an array of elements, the last of which must be
the
-- "non-element" value.
pascal_set(const element_type* e, int count);
-- This creates a set from an array of elements of the given size.
Operators (assume: A and B are sets, and E is an element):
A + B union (also defined: +=)
A * B intersection (also defined: *=)
A - B difference (also defined: -=)
A / B symmetric difference (also defined: /=)
A <= B true if A is a subset of B
A < B true if A is a proper subset of B
A >= B true if B is a subset of A
A > B true if B is a proper subset of A
A == B true is A is equal to B
A != B true is A is not equal to B
bool(A) true if A is not an empty set
!A true if A is an empty set
A + E creates a set equal to A, including E (also defined: +=)
A - E creates a set equal to A, excluding E (also defined: -=)
A ^ E creates a set equal to A, toggling E (also defined: ^=)
E == A true if E is a member of A
E != A true if E is not a member of A
A << E adds E to A (i.e., A << E1 << E2 << ... << En)
A >> E remove E from A (i.e., A >> E1 >> E2 >> ... >> En)
Member Functions:
clear();
-- This clears the set object of all elements. This is equivalent to:
-- s = NULL_SET;
set_all();
-- This sets added all the elements in the domain to the set object. This
is
-- equivalent to: s = UNIVERSAL_SET.
bool empty() const;
-- This returns true if the set object does not contains any elements (is a
null
-- set).
bool contains(element_type e) const;
-- This returns true if the set object contains the given element;
Plus, there is a class member template to create sets at compile-time:
const color_set additive_primaries =
color_set::setof<RED, GREEN, BLUE>::value;
const color_set subtractive_primaries =
color_set::setof<RED, YELLOW, BLUE>::value;
REH
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
|
|
| Back to top |
|
 |
thiago.adams@gmail.com Guest
|
Posted: Thu Aug 04, 2005 10:35 am Post subject: Re: How to implement a type safe bit set? |
|
|
I think that a good solution is implement operator + (or |) and &.
For example:
enum FontStyle {
FontStyleBold = 1 << 1,
FontStyleItalic = 1 << 2,
FontStyleUnderline = 1 << 3
};
inline FontStyle operator + (FontStyle e1, FontStyle e2) {
return static_cast
}
inline bool operator & (FontStyle e1, FontStyle e2) {
return ((unsigned long)e1 & ( unsigned long)e2) == e2;
}
FontStyle fontStyle = (FontStyle) 0; // empty
fontStyle = FontStyleBold + FontStyleItalic; // ok
fontStyle = 2; // error
fontStyle = FontStyleBold + FontStyleItalic + 4; // error
fontStyle & FontStyleBold; // returns bool
Maybe a empty class is usefull: (something like nullptr implementation)
static struct {
template<class T>
operator T(){ return static_cast<T>(0); }
} empty;
FontStyle fontStyle = empty;
Operator - can be defined too:
inline FontStyle operator - (FontStyle e1, FontStyle e2) {
return static_cast<FontStyle>(e1 & ~ e2);
}
The operators implementation can be make by #define or namespace;
In the case of namespace used the enum must be declared in the same
namespace of operators for argument lookup.
Thiago
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
|
|
| Back to top |
|
 |
|
|
You cannot post new topics in this forum You cannot reply to topics in this forum You cannot edit your posts in this forum You cannot delete your posts in this forum You cannot vote in polls in this forum
|
|