Motivation
We can't ignore compiler warnings, but they are sometimes annoying:
void motivation(HWND hWnd)
{
bool b = ::IsWindow(hWnd);
}
The above code produces the warning:
warning C4800: 'BOOL' : forcing value to bool 'true'
or 'false' (performance warning)
static_cast
makes the same warning as the one given above. Then you write:
void wrong(HWND hWnd)
{
bool b = (::IsWindow(hWnd) == TRUE);
}
The behavior is undefined, because the Win32 document says:
If the window handle identifies an existing window,
the return value is nonzero.
If the window handle does not identify an existing window,
the return value is zero.
You may think that BOOL
is defined as TRUE
or FALSE
, but it seems wrong. Then you write:
void right(HWND hWnd)
{
bool b = (::IsWindow(hWnd) != 0);
}
Your job is completed (you can write FALSE
as 0). Anyway, you must be thinking it is still annoying. So that is where boolean_cast
comes in.
Requirements
boolean_cast
boolean_cast
that is defined in the namespace poost
safely converts values between bool
, BOOL
and VARIANT_BOOL
types:
void good(HWND hWnd)
{
bool b = boolean_cast<bool>(::IsWindow(hWnd));
}
VARIANT_TRUE
is defined as ((short)-1
), in fact, the implicit cast is dangerous:
void good_save()
{
VARIANT_BOOL vb = TRUE;
ATLASSERT( vb != VARIANT_TRUE );
vb = boolean_cast<VARIANT_BOOL>(TRUE);
ATLASSERT( vb == VARIANT_TRUE );
}
boolean_cast
increases the safety and readability. But well, why should you write target types? The compilers must know the types.
auto_boolean
auto_boolean
uses the type of the assigned object as the target type:
void best(HWND hWnd)
{
bool b = auto_boolean(::IsWindow(hWnd));
}
That's what you want. No warning, no annoying. Note that auto_boolean
doesn't work under broken compilers like VC6 or eVC4.
How it works
The implementation of boolean_cast
is as follows:
template< class TargetT, class SourceT >
TargetT boolean_cast(SourceT b)
{
typedef boolean_converter<TargetT, SourceT> converter_t;
return converter_t::convert(b);
}
boolean_cast
delegates its task to boolean_converter
class template. The real task is performed by the template. Let's look at the implementation:
template< class TargetT, class SourceT >
struct boolean_converter;
template< >
struct boolean_converter<bool, BOOL>
{
static bool convert(BOOL b)
{
return b ? true : false;
}
}
The task is sent to "full class template specialization" depending on two types, TargetT
and SourceT
.
Next, let's look into auto_boolean
. It is somewhat technical:
template< class SourceT >
struct auto_boolean_type
{
explicit auto_boolean_type(SourceT b) : m_b(b)
{ }
template< class TargetT >
operator TargetT() const
{ return boolean_cast<TargetT>(m_b); }
private:
SourceT m_b;
};
template< class SourceT >
auto_boolean_type<SourceT> auto_boolean(SourceT b)
{
return auto_boolean_type<SourceT>(b);
}
It is interesting that auto_boolean
returns not a type but auto_boolean_type
. The compilers must convert auto_boolean_type
to the target type. They find and call the "conversion operator" and at last boolean_cast
is automatically invoked instead of you invoking it.
Points of interest
reinterpret_cast
and static_cast
get a lot of use in most programs. It can be time consuming to determine what each cast is doing after you haven't looked at your code for several months, and a domain-specific cast such as boolean_cast
can help you reduce this time.
Tested under
References
History
- 5th December, 2005: Initial version uploaded.
- 6th December, 2005: Document updated.