Added bool_testable by Sam Partington

[SVN r18413]
This commit is contained in:
Daniel Frey 2003-05-15 22:40:33 +00:00
parent 1be04eeec5
commit 88f4e47550
3 changed files with 179 additions and 1 deletions

View File

@ -9,6 +9,7 @@
// See http://www.boost.org/libs/utility/operators.htm for documentation.
// Revision History
// 04 May 05 Added operator class bool_testable. (Sam Partington)
// 21 Oct 02 Modified implementation of operators to allow compilers with a
// correct named return value optimization (NRVO) to produce optimal
// code. (Daniel Frey)
@ -308,6 +309,18 @@ struct indexable : B
}
};
// bool_testable -----------------------------------------------------------//
// (contributed by Sam Partington, David Abrahams and Daniel Frey) ---------//
template <class T, class B = ::boost::detail::empty_base>
struct bool_testable : B
{
friend bool operator!(const T& t) { return !static_cast<bool>(t); }
private:
typedef signed char private_number_type;
operator private_number_type() const;
};
// More operator classes (contributed by Daryle Walker) --------------------//
// (NRVO-friendly implementation contributed by Daniel Frey) ---------------//

View File

@ -72,6 +72,8 @@
<li><a href="#ordering">Ordering Note</a></li>
<li><a href="#symmetry">Symmetry Note</a></li>
<li><a href="#safe_bool_note">Safe-Bool Note</a></li>
</ul>
</li>
@ -340,6 +342,8 @@ class MyInt
<li><code><a href="#indexable">indexable&lt;&gt;</a></code></li>
<li><code><a href="#bool_testable">bool_testable&lt;&gt;</a></code></li>
<li>Any composite operator template that includes at least one of the
above</li>
</ul>
@ -527,6 +531,17 @@ const point&lt;float&gt; pi_over_4_normalized = pi_over_4 / length(pi_over_4);
Return convertible to <code>bool</code>.</td>
</tr>
<tr>
<td><code><a name="bool_testable">bool_testable&lt;T&gt;</a></code></
td>
<td><code>bool operator!(const T&)</code></td>
<td><code>static_cast&lt;bool&gt;(t)</code>.<br>
<code>T</code> convertible to <code>bool</code>. See the <a href=
"#safe_bool_note">Safe-Bool Note</a>.</td>
</tr>
<tr>
<td><code><a name="addable1">addable&lt;T&gt;</a></code><br>
<code>addable1&lt;T&gt;</code></td>
@ -974,7 +989,132 @@ T operator+( T lhs, const T&amp; rhs )
that don't implement the NRVO. <br>
<br>
<h4><a name="safe_bool_note">Safe-Bool</a> Note</h4>
<p><code><a href="#bool_testable">bool_testable</a></code> provides the
antithesis of operator bool, such that the expression <code>if (!p)</code>
is valid, whilst also making <code>operator bool</code> safer by preventing
accidental conversions to integer types. If <code>operator bool</code>
were declared for a class without using
<code><a href="#bool_testable">bool_testable</code></a> then expressions
such as :</p>
<pre>
void f(int);
void g(object o)
{
f(o);
}
</pre>
<p>would compile silently, not warning the user of the (probably)
unintended behaviour of passing <code>0</code> or <code>1</code> to
<code>f()</code>.</p> <code>bool_testable&lt;&gt;</code> prevents these
accidental conversions by declaring a private conversion operator to
<code>signed char</code>, and not defining the body.</p>
<h5>Example :</h5>
<pre>
class Stream : boost::bool_testable&lt;Stream&gt;
{
public:
explicit Stream(const char * source);
operator bool() const;
// non-member bool operator!(const T&) const auto-generated and
// operator bool made safe by bool_testable&lt;&gt;
bool can_print();
};
void g(int);
void f()
{
if (Stream s1("source.txt"))
{
// use s1 ...
}
// or..
Stream s2("source.txt");
if (!s2)
{
// handle problem
}
// or ..
if (s2 && s2.can_print()) //* see <a href="#bool_testable_msvc">note</a>
{
// print something...
}
g(stream); // Will not compile, but would compile
// fine without bool_testable&lt;&gt;
}
</pre>
<h5><a name="bool_testable_msvc">Note for MSVC version 6 users</a> :</h5>
<p>Due to a bug in MSVC6, whilst compiling the above code, the compiler
will incorrectly diagnose an '<code>ambiguous operator &&</code>' at
the asterisked line. The workaround for this is to either:</p>
<pre>
if (s2)
if (s2.can_print())
</pre>
<p>or</p>
<pre>
if (!!s2 && s2.can_print())
</pre>
<p>This problem also affects the other logical operators.</p>
<h5>Rationale for Implementation</h5>
<p>Another possible implementation for <code>bool_testable</code> was the
safe-bool idiom as found in <a href=
"../smart_ptr/shared_ptr.htm#conversions"><code>shared_ptr&lt;&gt;</code>
</a> written by <a href="../../people/peter_dimov.htm">Peter Dimov</a>.
This implementation required the user to provide <code>operator!</code>,
and provided a conversion operator to a pointer to member function. This
method had the advantage of more descriptive diagnostic messages on most
platforms, when the bool conversion was used incorrectly. Unfortunately
that implementation had issues when used with a class with other user
defined integer conversion operators. This is because the integer
conversion operator provided a better match than the conversion to pointer
to member function. For example:</p>
<pre>
class MyInt : alternative_bool_testable&lt;MyInt&gt;
{
public:
bool operator!() const;
operator int() const;
// alternative_bool_testable&lt;MyInt&gt; provides :
//
// typedef void (MyInt::*member_fn)();
// operator member_fn() { return &MyInt::f; }
};
void g(int);
void f(const MyInt& i)
{
if (!i) // fine: calls operator!
...
g(i); // fine: calls operator int
if (i) // error: calls operator int NOT operator member_fn()
...
}
</pre>
<h3><a name="grpd_oprs">Grouped Arithmetic Operators</a></h3>
<p>The following templates provide common groups of related operations.
@ -2062,6 +2202,11 @@ public:
<dd>Contributed the NRVO-friendly and symmetric implementation of
arithmetic operators.</dd>
<dt>Sam Partington</dt>
<dd>Contributed the bool_testable class.</dd>
</dl>
<h2>Note for Users of <a name="old_lib_note">Older Versions</a></h2>

View File

@ -50,6 +50,7 @@ namespace
class Wrapped1
: boost::operators<Wrapped1<T> >
, boost::shiftable<Wrapped1<T> >
, boost::bool_testable<Wrapped1<T> >
{
public:
explicit Wrapped1( T v = T() ) : _value(v) {}
@ -80,6 +81,7 @@ namespace
{ _value >>= x._value; return *this; }
Wrapped1& operator++() { ++_value; return *this; }
Wrapped1& operator--() { --_value; return *this; }
operator bool() const { return _value != 0; }
private:
T _value;
@ -555,6 +557,12 @@ template Wrapped6<unsigned int, unsigned char>;
#define PRIVATE_EXPR_TEST(e, t) BOOST_TEST( ((e), (t)) )
#define PRIVATE_BOOLEAN_EXPR_TEST(t, res) BOOST_TEST(static_cast<bool>((t)) == (res))
#if defined(BOOST_MSVC)
#define PRIVATE_MSVC_BOOL_TEST_WORKAROUND(x) (!!x)
#else
#define PRIVATE_MSVC_BOOL_TEST_WORKAROUND(x) (x)
#endif
int
test_main( int , char * [] )
@ -631,7 +639,19 @@ test_main( int , char * [] )
PRIVATE_EXPR_TEST( (i = i1 << i2), (i.value() == 4) );
PRIVATE_EXPR_TEST( (i = i2 >> i1), (i.value() == 1) );
PRIVATE_BOOLEAN_EXPR_TEST( i, false);
PRIVATE_BOOLEAN_EXPR_TEST( i1, true);
PRIVATE_BOOLEAN_EXPR_TEST( i2, true);
PRIVATE_BOOLEAN_EXPR_TEST(!i, true);
PRIVATE_BOOLEAN_EXPR_TEST(!i1, false);
PRIVATE_BOOLEAN_EXPR_TEST(!i2, false);
PRIVATE_BOOLEAN_EXPR_TEST(PRIVATE_MSVC_BOOL_TEST_WORKAROUND(i) && PRIVATE_MSVC_BOOL_TEST_WORKAROUND(i2), false);
PRIVATE_BOOLEAN_EXPR_TEST(PRIVATE_MSVC_BOOL_TEST_WORKAROUND(i) || PRIVATE_MSVC_BOOL_TEST_WORKAROUND(i2), true);
PRIVATE_BOOLEAN_EXPR_TEST(PRIVATE_MSVC_BOOL_TEST_WORKAROUND(i1) && PRIVATE_MSVC_BOOL_TEST_WORKAROUND(i2), false);
PRIVATE_BOOLEAN_EXPR_TEST(!i && PRIVATE_MSVC_BOOL_TEST_WORKAROUND(i2), false);
PRIVATE_BOOLEAN_EXPR_TEST(PRIVATE_MSVC_BOOL_TEST_WORKAROUND(i1) && !i, false);
cout << "Performed tests on MyInt objects.\n";
MyLong j1(1);