Introduction
Compilers MSVC v6.0/v7.0 are not supported. This project requires a compliant compiler. MSVC v7.1 or GCC v3.2.3 will work just fine.
I have another handy (I hope) addition to the TTL library. Suppose, we need to define a set of flags that can be passed around. Typically, we would use macros to define the flags and an integer data type to hold them.
An example might look like the following code:
#define WND_STYLE_CAPTION 0x01
#define WND_STYLE_BORDER 0x02
void set_style( int style );
First of all, using macros for defining constants is not always a very good idea. The reason is that macro names belong to the global namespace. For instance, you cannot do something like:
namespace x
{
#define WND_STYLE_CAPTION 0x01
#define WND_STYLE_BORDER 0x02
};
namespace y
{
#define WND_STYLE_CAPTION 0x01
#define WND_STYLE_BORDER 0x02
};
main()
{
int fx = x::WND_STYLE_CAPTION;
int fy = y::WND_STYLE_CAPTION;
}
To resolve the problem, we can use enum
.
namespace x
{
enum winstyle
{
WND_STYLE_CAPTION = 1,
WND_STYLE_BORDER = 1<<1
};
};
namespace y
{
enum winstyle
{
WND_STYLE_CAPTION = 1,
WND_STYLE_BORDER = 1<<1
};
};
main()
{
int fx = x::WND_STYLE_CAPTION;
int fy = y::WND_STYLE_CAPTION;
}
An even better solution is:
namespace x
{
struct winstyle
{
enum type
{
CAPTION = 1,
BORDER = 1<<1
};
};
};
namespace y
{
struct winstyle
{
enum type
{
CAPTION = 1,
BORDER = 1<<1
};
};
};
main()
{
int fx = x::winstyle::CAPTION;
int fy = y::winstyle::BORDER|y::winstyle::CAPTION;
}
Now, everything works fine... well, almost. The problem is that we don't have type safety. The following code illustrates the problem:
void set_style( int style )
main()
{
int x = 8989;
set_style( x );
}
There is another problem that is the type of the flags holder. To hold a combination of flags, people usually use int
or unsigned int
. Consider a vector of flag holders:
std::vector< int > styles;
In such a case, on a 32 bit machine, we'll be allocating 3 extra bytes per vector element. So we need to watch as to what the optimal flags holder type should be. Clearly we have two objectives:
- Make the flags type safe.
- Make the compiler automatically select the optimal flags holder type.
Implementation
The implementation is relatively straightforward. Here are some ideas. The complete implementation can be found in TTL.
namespace impl
{
template<
int Bits,
int type = Bits <= sizeof(char)*8?
1:(Bits <= sizeof(short)*8?2:(Bits <= sizeof(int)*8?3:4))
> struct bestfit;
template< int Bits >
struct bestfit<Bits, 1>
{
typedef unsigned char type;
};
template< int Bits >
struct bestfit<Bits, 2>
{
typedef unsigned short type;
};
template< int Bits >
struct bestfit<Bits, 3>
{
typedef unsigned int type;
};
template< int Bits >
struct bestfit<Bits, 4>
{
typedef unsigned int type;
};
};
template< typename T, int Bits = sizeof(int)*8,
typename Holder = typename impl::bestfit<Bits>::type >
struct flags
{
typedef flags this_t;
typedef T value_type;
Holder f_;
flags() : f_(0) {}
flags( T f1 ) : f_(f1) {}
flags( T f1, T f2 ) : f_(f1|f2) {}
flags( T f1, T f2, T f3 ) : f_(f1|f2|f3) {}
...
this_t& operator |=( const this_t& f ) { f_ |= f.f_; return *this; }
this_t& operator &=( const this_t& f ) { f_ &= f.f_; return *this; }
this_t operator~() { f_ = ~f_; return *this; }
bool operator ==( const this_t& l ) const { return f_ == l.f_; }
bool operator !=( const this_t& l ) const { return f_ != l.f_; }
bool operator !() { return f_ == 0; }
Holder get_holder() const { return f_; }
bool test( const this_t& l ) const { return (f_ & l.f_)?true:false; }
bool test() const { return f_!=0; }
};
template< typename T, int Bits, typename Holder >
flags<T, Bits, Holder> operator |(const flags<T,
Bits, Holder>& l, const T& r)
{
flags<T, Bits, Holder> tmp( r );
return l|tmp;
}
template< typename T, int Bits, typename Holder >
flags<T, Bits, Holder> operator |(const flags<T,
Bits, Holder>& l, const flags<T,
Bits, Holder>& r)
{
flags<T, Bits, Holder> tmp( l );
tmp|=r;
return tmp;
}
template< typename T, int Bits, typename Holder >
flags<T, Bits, Holder> operator &(const flags<T,
Bits, Holder>& l, const T& r)
{
flags<T, Bits, Holder> tmp( r );
return l&tmp;
}
template< typename T, int Bits, typename Holder >
flags<T, Bits, Holder> operator &(const flags<T,
Bits, Holder>& l, const flags<T,
Bits, Holder>& r)
{
flags<T, Bits, Holder> tmp( l );
tmp&=r;
return tmp;
}
As you can see, we use the bestfit
class to select the optimal size holder. This class is partially specialized on sizes of char
, short
and int
. If the number of flags is more than 8*sizeof(int)
, we'll get the compilation error.
Usage
Back to our example:
struct winstyle
{
enum type
{
CAPTION = 1,
BORDER = 1<<1,
size = 2
};
};
typedef flags<winstyle, winstyle::size> winstyle_flags;
How do old flags compare to the new ones?
main()
{
int fy = winstyle::BORDER|winstyle::CAPTION;
}
is equivalent to:
main()
{
winstyle_flags fy(winstyle::BORDER, winstyle::CAPTION);
}
In the current implementation, you can initialize flags
with up to 10 flags. If you need 11, just add a new constructor with 11 parameters and so on.
The second template parameter in flags
, is looking for T::size
that defines the last used bit in T
. By default, the size is 8*sizeof(int)
bits.
Now, our set_style()
can be rewritten in a type safe fashion.
void set_style( winstyle_flags style );
The usage of flags
is not really different from the traditional case. You can apply boolean and bitwise operators. Some examples are:
main()
{
winstyle_flags fx( winstyle::CAPTION );
winstyle_flags fy( winstyle::BORDER, winstyle::CAPTION);
if( fx == fy )
{
...
}
fx |= y::winstyle::BORDER;
if( fx.test(winstyle::CAPTION) )
{
...
}
winstyle_flags fz = fx & fy;
}
TTL has a function for mapping one set of flags to another set. For instance, if we need to map our winstyle
flags to Win32 WS_CAPTION
and WS_BORDER
, we can define the flag_map
class that will hold our associations.
struct flag_map
{
typedef std::multimap< winstyle_flags, int > map;
flag_map()
{
if( init_ )
{
init_ = false;
map_.insert( winstyle::CAPTION, WS_CAPTION );
map_.insert( winstyle::BORDER, WS_BORDER );
}
}
static bool init_;
static map map_;
}
void set_style( winstyle_flags style );
main()
{
set_style( winstyle_flags(winstyle::CAPTION,winstyle::BORDER) )
}
void set_style( winstyle_flags style )
{
flag_map map;
int w32style = ttl::flg::flag_mapper( map.map_.begin(), map.map_.end(), style );
::CreateWindow( ... w32style );
}
flag_mapper
requires that the data type de-referenced by the input iterator had the interface of std::pair
.
If you do need to convert flags< >
to an 'int
', use get_holder()
.
winstyle_flags fx( winstyle::CAPTION );
int f = fx.get_holder();
Using TTL
TTL is a completely header based library. There is no need to link to any .lib files. To use TTL:
- Copy TTL files to folder.
- Add the folder to the list of include folders in your compiler.
- Include TTL headers such as:
#include "ttl/flg/flags.hpp"
As for now, flags.hpp can be used as a standalone header separately from the rest of TTL.
Enjoy!