diff --git a/include/boost/operators.hpp b/include/boost/operators.hpp index 202265a..b2f68b3 100644 --- a/include/boost/operators.hpp +++ b/include/boost/operators.hpp @@ -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 +struct bool_testable : B +{ + friend bool operator!(const T& t) { return !static_cast(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) ---------------// diff --git a/operators.htm b/operators.htm index 07a8754..236ebc6 100644 --- a/operators.htm +++ b/operators.htm @@ -72,6 +72,8 @@
  • Ordering Note
  • Symmetry Note
  • + +
  • Safe-Bool Note
  • @@ -340,6 +342,8 @@ class MyInt
  • indexable<>
  • +
  • bool_testable<>
  • +
  • Any composite operator template that includes at least one of the above
  • @@ -527,6 +531,17 @@ const point<float> pi_over_4_normalized = pi_over_4 / length(pi_over_4); Return convertible to bool. + + bool_testable<T> + + bool operator!(const T&) + + static_cast<bool>(t).
    + T convertible to bool. See the Safe-Bool Note. + + addable<T>
    addable1<T> @@ -974,7 +989,132 @@ T operator+( T lhs, const T& rhs ) that don't implement the NRVO.

    +

    Safe-Bool Note

    +

    bool_testable provides the + antithesis of operator bool, such that the expression if (!p) + is valid, whilst also making operator bool safer by preventing + accidental conversions to integer types. If operator bool + were declared for a class without using + bool_testable then expressions + such as :

    + +
    +void f(int);
    +
    +void g(object o)
    +{
    +    f(o);
    +}
    +
    + +

    would compile silently, not warning the user of the (probably) + unintended behaviour of passing 0 or 1 to + f().

    bool_testable<> prevents these + accidental conversions by declaring a private conversion operator to + signed char, and not defining the body.

    + +
    Example :
    + +
    +class Stream : boost::bool_testable<Stream>
    +{
    +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<>
    +
    +    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 note
    +    {
    +        // print something...
    +    }
    +
    +    g(stream); // Will not compile, but would compile 
    +               // fine without bool_testable<>
    +}
    +
    + +
    Note for MSVC version 6 users :
    + +

    Due to a bug in MSVC6, whilst compiling the above code, the compiler + will incorrectly diagnose an 'ambiguous operator &&' at + the asterisked line. The workaround for this is to either:

    +
    +    if (s2)
    +        if (s2.can_print())
    +
    +

    or

    +
    +    if (!!s2 && s2.can_print())
    +
    +

    This problem also affects the other logical operators.

    + +
    Rationale for Implementation
    + +

    Another possible implementation for bool_testable was the + safe-bool idiom as found in shared_ptr<> + written by Peter Dimov. + This implementation required the user to provide operator!, + 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:

    + +
    +class MyInt : alternative_bool_testable<MyInt>
    +{
    +public:
    +    bool operator!() const;
    +    operator int() const;
    +
    +    // alternative_bool_testable<MyInt> 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()
    +        ...
    +}
    +
    +

    Grouped Arithmetic Operators

    The following templates provide common groups of related operations. @@ -2062,6 +2202,11 @@ public:

    Contributed the NRVO-friendly and symmetric implementation of arithmetic operators.
    + +
    Sam Partington
    + +
    Contributed the bool_testable class.
    +

    Note for Users of Older Versions

    diff --git a/operators_test.cpp b/operators_test.cpp index 581c1f1..47c7b20 100644 --- a/operators_test.cpp +++ b/operators_test.cpp @@ -50,6 +50,7 @@ namespace class Wrapped1 : boost::operators > , boost::shiftable > + , boost::bool_testable > { 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; #define PRIVATE_EXPR_TEST(e, t) BOOST_TEST( ((e), (t)) ) +#define PRIVATE_BOOLEAN_EXPR_TEST(t, res) BOOST_TEST(static_cast((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);