OR: What do you get if you convert an I/O stream to a void *?

A neat feature of the C++ I/O library is the error handling provided by iostreams and friends. Any fstream has a fail() method, which says if something's wrong. So to read an int from std::cin while checking errors, just say

int i;
std::cin >> i;
if (std::cin.fail()) {
  // something's wrong...
}
Since you usually want a readable way to check for errors, the language lets you use the stream itself as a boolean test:
int i;
std::cin >> i;
if (! std::cin) {
  // something's wrong...
}
Since the >> input operator returns its LHS as an lvalue (this also lets you write "std::cin >> i >> s"; see C++: why input is >> and output is << for the gory details), you can just say
?
int i;
if (! (std::cin >> i)) {
  // something's wrong...
}
which is very elegant.

Design and Implementation

How is all this implemented? The obvious way would be a conversion:

class my_istream {      // templates, inheritance left out
  // ...
public:
  // ...
  operator bool() const;
  // ...
};
Presumably defining the operator by saying something like
my_istream::operator bool() const { return ! fail(); }

This is enough to make all the above work. But it's also enough to defeat almost any type checking the C++ compiler might try to do. Assuming my_istream a, b;, all of these are legal with the above declaration:

  • a+b;
  • a%b;
  • a==b+1 (same as saying !a.fail() && b.fail()!);
  • a+b < 2 (same as saying a.fail() || b.fail()!)
  • 17 << a (NOT the same as a >> 17!).
bools are just integer types, and integer types have very "convenient" arithmetic defined for them. But it's all nonsense for a file stream.

By contrast, about the only arithmetic that is legal for a C stdio.h FILE * is adding or subtracting an int (and even that can easily be prevented, by keeping FILE an undeclared struct in the header file. This too is unpleasant, but surely C++ should be more type safe than C!?

A "better"(?) way

C++ avoids the above unpleasantries, by not defining a bool conversion. Instead, to test a file stream for errors, the equivalent of the following is used:

class my_istream {      // templates, inheritance left out
  // ...
public:
  // ...
  operator void*() const;
  // ...
};
// ... and later...
my_istream::operator void*() const { return fail() ? 0 : this; }
C++ doesn't have any implicit conversions from void * to other pointer types, so code like int *i = a; remains illegal. You cannot perform arithmetic on 2 void pointers, so a+b and friends are illegal. You cannot do pointer arithmetic, either, so a+2 is also right out (this is why the conversion is to void *, not to my_istream *).

About the only things you can do with the implicitly-converted void pointer are test for truth and falsehood, by saying "if (a) ..." and "if (! a) ...". This is exactly what was desired above.

But who would have guessed that the correct way to convert to bool is to convert instead to void *?

First, a disclaimer, this is something that I have learnt from other sources -- I can't remember where, except for the fact that I'd taken the source from the examples that it gave and used them in my own code. Please msg me about any formatting inconsistencies -- things like < and & need to be escaped to be valid HTML, and I may not have got all of them.


Ah Ariels -- if only it was that easy. Conversion to bool, as you have noted, is bad because we can do arithmetic on it. Which is not cool. But conversion to void * allows two things that we don't want:

Testable_By_Conversion t;
delete t; // damn! that sucks!
std::cout << t;

Yep, that's right, we can delete it, and we've got a surprise when the compiler implicitly converts it. The second one can be extended to any time when a void * can be used, which, if it happens deep in a template library, can be really hard to pick apart.

The fact that STL uses it is just because the correct technique hadn't been discovered at the time.

There are, in fact, two ways to do this, one is cleaner but loses on syntax, the other is perfect in terms of syntax, but the mechanism is enough to make even the greatest C++ gurus cross their legs and wince.

The first is to overload operator!, which means that the usual test for failure is then:

if(!testable) {
    // do something when it fails...

and the test for "good":

if(!!testable)...

That's right kids! Double Bang! It's something that most people would not find comfortable doing, even though it is perhaps the best way to realise this feature. However, a library/component which uses an unknown idiom is itself useless.

So we get to try something else instead. The following code is what we need:

class Testable {
private:
    typedef void (Testable::*bool_type)() const;
    void __deep_dark_member_function() const {}
public:
    // ... class stuff...

    // et le piece de la resistance
    operator bool_type () const {
        if(/* internal test */) {
            return &Testable::__deep_dark_member_function;
        } else {
            return 0;
        }
    }

    // more class stuff ...
};

Is your head hurting yet?

For those who like nouns, this is a case where we return a member function pointer to a private member function. It happens that member function pointers can do even less than void *, and the fact that it's a private type means that there's no chance of namespace collision (technically, a very much reduced chance, where it wouldn't happen unless you were trying) -- there can't be any function that takes the returned value as a parameter. It also can't be delete'd, and it doesn't participate in arthimetic.

Actually, the code above is incomplete, since it still allows us to write silly things such as:

Testable t1, t2;
if(t1 == t2) { ...

This is dangerous if we had tried to disable comparisons between instances of Testable and then found that the compiler worked out a way to allow us to do it anyway... The solution is, as with the rest of this, sublime:

template <typename T>
bool operator==(const Testable & a, const T & b)
{
    lhs.__deep_dark_member_function();
    return false;
}

Note:we'd also have to overload operator!=, but I'm lazy

By having this as a non-member, non-friend function means that we can't actually access the deep and dark bits of Testable, and by having a template version, we know that it doesn't get generated by the compiler unless we actually try to use that comparison operator. Cool, huh? Further more, if we called the __deep_dark_member_function something useful, like this_class_does_not_support_comparisons, then the compiler would put that in the error message, and we'd effectively get diagnostic back out (this is also how the Boost concept check library does custom error messages)!

To wrap up then: this is the "correct way" to handle the case given. This is also painful, if you had to do it every time. Here, we see C++ flex its not-inconsiderable muscle. Yes, it's a pain to work out how it works, but the next bit ought to leave you amazed that this works at all. Basically, we're going to wrap this up into a neat little base class, so that classes that need to implement this, can just derive from the base class.

class safe_bool_base {
protected:
    typedef void (safe_bool_base::*bool_type)() const;
    void this_type_does_not_support_comparisons() const {}

    safe_bool_base() {}
    safe_bool_base(const safe_bool_base&) {}
    safe_bool_base& operator=(const safe_bool_base&) {return *this;}
    ~safe_bool_base() {}
};

template <typename T=void> class safe_bool : public safe_bool_base {
public:
    operator bool_type() const {
      return (static_cast<const T*>(this))->boolean_test()
        ? &safe_bool_base::this_type_does_not_support_comparisons : 0;
    }
protected:
    ~safe_bool() {}
};

template<> class safe_bool<void> : public safe_bool_base {
public:
    operator bool_type () const {
      return boolean_test()==true ? 
        &safe_bool_base::this_type_does_not_support_comparisons : 0;
    }
protected:
    virtual bool boolean_test() const=0;
    virtual ~safe_bool() {}
};

template <typename T, typename U> 
    void operator==(const safe_bool& lhs,const safe_bool<U>& rhs) {
      lhs.this_type_does_not_support_comparisons();	
      return false;
}

template <typename T,typename U> 
void operator!=(const safe_bool<T>& lhs,const safe_bool<U>& rhs) {
    lhs.this_type_does_not_support_comparisons();
    return false;	
}

This actually creates two versions that may be used. One is when you derive your class T from safe_bool<> (or, equivalently, safe_bool<void>), which means that you implement a virtual function called boolean_test, which contains the test. But a virtual function implies a vtable, which slows things down, etc, etc. So the other version is to derive T from safe_bool<T> (yes, you read that right, derived from a base that takes the current class as a parameter... see Curiously Recurring Template Pattern), this way, you define a non-virtual boolean_test, which is bound at compile time, since the compiler can now static_cast the function pointer.

Examples:

class T1: public safe_bool<> {
public:
    virtual bool boolean_test() const {
        // test goes here
    }
}

class T2: public safe_bool<T2> {
public:
    bool boolean_test() const {
        // test goes here
    }
}

So surprisingly, after all that, the final result is actually very clean and elegant. This is perhaps the best demonstration of the pain and the beauty that is in C++ -- yes, we had a difficult and archane journey, but the result is worth it -- a simple, easy to understand idiom that follows the Once And Only Once rule.

There are still a couple of variations possible. One is to use a pointer to member variable instead of function, because certain compilers (so I'm told) have difficulty in optimising the member function case; actually, some compilers have difficulting in applying EBO anyway, and most can't do it for the multiple inheritance case. The other variation is to derive the safe_bool bases as private instead, and bring the operator bool_type into the public area of the derived class with using clauses; this is safer in terms of keeping a clean namespace (especially if you then derive from the safe_bool child), but it means some typing, and nobody likes that.

Log in or register to write something here or to contact authors.