JVene
JVene

Reputation: 1759

Safe Bool Idiom - did I find a simpler way, or did I miss something?

I needed to find a safe bool idiom solution. You know the drill, I'm using VS 2012, XCode, GCC, LLVM, various platforms, some support the C++11 explicit conversion operator and some don't. I needed something portable.

I found several, but devised that seems so simple that I have to ask, am I missing something here?

Background (brief):

Several examples one the web provide either highly intrusive methods (essentially putting everything required inside a given class), or a generic approach using CRTP, but most are based on a pointer to member function (which is of wildly unpredictable size and performance penalty).

Then I read about boost::spirit::classic safe bool, tried it, examined it, etc. What I liked about spirit's implementation was that it depended upon a pointer to member data rather than a pointer to member function (with a compiler workaround for 'obscure' versions).

So I thought, it really doesn't matter what the type the pointer to member is based upon, the implementations are working because, among other things, these are pointers to members of anything other than objects we're likely to use.

So, I tried this kind of thing (and I'm wondering if I'm way off or ok with this)

First:

class SafeBool 
{ private:  SafeBool() : Value( 0 ) { }
  public:   int Value;
};

SafeBool takes the job of boost::spirit's safe_bool template without imposing the burden of driving user classes from it.

// pointer to member typedef, made widely available

typedef int SafeBool :: *  safe_bool;

//  more about why this define in a moment
#define EXPLICIT_OPERATOR_BOOL operator safe_bool


// here is an example user class, some smart pointer or something 
template< typename T > class SomeSPtr 
{
 public:  ... other class stuff, obviously

 EXPLICIT_OPERATOR_BOOL() const
      { if ( get() ) return &SafeBool::Value; 

        return 0; 
      }
};

First, EXPLICIT_OPERATOR_BOOL is a define which can be switched on C++11 compliant compilers to use the "real" explicit operator bool, whereas on VS 2012 and old GCC (or what have you), it can be as it is here, conversion to operator safe_bool.

One can even use a related define for the return...say

SAFE_BOOL_RETURN( get() )

Which could modulate the return logic to essentially make the code switchable between modern and older compilers (that is, for C++11 the define would resolve to return get() != 0)

Now, this had the same basic results on VS 2012 as spirit's code. It allowed

SomePtr< AType > p, q; // or some such

if ( p ) { } // this works as expected

While at the same time denying code like

if ( p < q ) // compile time error
if ( p > q ) // ""

BUT it allowed this:

if ( p == q ) // compiles fine

This is the same result I get with a number of implementations, but several do something like:

template< typename T > bool operator ==( const safe_bool & , const T & )
 {
  SafeBool CantCompareThatType;
  return false;
 }

template< typename T > bool operator ==( const T &, const safe_bool & )
 {
  SafeBool CantCompareThatType;
  return false;
 }

Or similar trickery, which is template code that doesn't instantiate until the operator is used, and since the SafeBool constructor is private, generates an error if (p == q) is attempted.

Obviously all other operators (!=....), etc to be excluded would be handled similarly as required (template functions which respond to whatever type has the EXPLICIT_OPERATOR_BOOL created function in it).

Ok, here's the question. Am I deluding myself here? Other implementations impose the burden of deriving from a template class AND placing a bool like function in the user's class - or one puts the whole thing into the user class.

It appears to me that this approach uses one class, SafeBool (which is never intended for instantiation) to provide the pointer to member, and a type (the class itself) upon which a safe bool idiom can be implemented while imposing ONLY a single function in the user's class, which itself mimics the C++11 explicit conversion operator, possibly enabling more upwardly portable code where this idiom is needed.

Please, somebody stop me if I'm wrong about this!

Upvotes: 3

Views: 324

Answers (1)

Cort Ammon
Cort Ammon

Reputation: 10863

Your technique works, and is discussed as one of the http://www.artima.com/cppsource/safebool.html . It discusses it as an option that works. It is called "poisoning operators" and Douglas Gregor is credited with inventing it.

I think the real safe_bool idiom got more headway by being even more safe. In particular, if your safe_bool is of type int T::*, and the only pointer to an integer member of T is private, then only that class can generate that particular variant of safe_bool, so its even easier to prove that no one can generate unsafe patterns. Since anyone can take the address of &SafeBool::Value, it is "less safe."

Pathological? Perhaps.

Upvotes: 2

Related Questions