Click here to Skip to main content
6,630,586 members and growing! (16,471 online)
Email Password   helpLost your password?
Languages » C / C++ Language » General     Intermediate

Tiny Template Library: working with flags

By kig

An article on how to implement and use bit flags generically and type-safely.
VC7.1, WindowsVS.NET2003, Dev
Posted:5 Apr 2004
Views:27,395
Bookmarked:15 times
Announcements
Loading...
 
Search    
Advanced Search
Add to IE Search
printPrint   add Share
      Discuss Discuss   Broken Article?Report  
10 votes for this article.
Popularity: 4.45 Rating: 4.45 out of 5
1 vote, 10.0%
1

2

3
5 votes, 50.0%
4
4 votes, 40.0%
5

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:

//we cannot just use 'winstyle' as the 

//parameter type because it is a combination

//of winstyle values.

void set_style( int style )

main()
{
    int x = 8989;

    //'x' is just an integer and has nothing to do

    //with window styles... but the compiler

    //doesn't know about it an it is compiled just fine.

    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  //the last used bit

    };
};

//use our new class template

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) ) //is CAPTION set

    {
    ...
    }


    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.

//flag_map keeps a map of pairs (my_flag, win32_flag)

struct flag_map
{
    //the first parameter is the key

    typedef std::multimap< winstyle_flags, int > map;

    //the above could also be

    //typedef std::vector< std::pair< winstyle_flags, int > > map;

    
    flag_map()
    {
        if( init_ ) //initialize only once

        {
            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;
    //map our flags to Win32

    int w32style = ttl::flg::flag_mapper( map.map_.begin(), map.map_.end(), style );

    ::CreateWindow( ... w32style );  //use Win32 function with 'style' :)

}

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!

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here

About the Author

kig


Member

Occupation: Web Developer
Location: United States United States

Other popular C / C++ Language articles:

Article Top
You must Sign In to use this message board.
FAQ FAQ 
 
Noise Tolerance  Layout  Per page   
 Msgs 1 to 5 of 5 (Total in Forum: 5) (Refresh)FirstPrevNext
Generalenum in class? Pinmemberwheregone23:57 5 Nov '07  
Generalttl::sig::signal PinmemberWREY14:24 20 May '05  
GeneralRe: ttl::sig::signal Pinmemberkig13:22 22 May '05  
Generalrobust return values PinmemberMårten R8:39 4 Aug '04  
GeneralRe: robust return values Pinmemberkig11:04 4 Aug '04  

General General    News News    Question Question    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

PermaLink | Privacy | Terms of Use
Last Updated: 5 Apr 2004
Editor: Smitha Vijayan
Copyright 2004 by kig
Everything else Copyright © CodeProject, 1999-2009
Web17 | Advertise on the Code Project